diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4996338 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +forcing +runs +*.asv +*.docx +*.mat + diff --git a/CryoGrid3.m b/CryoGrid3.m deleted file mode 100755 index 47231d8..0000000 --- a/CryoGrid3.m +++ /dev/null @@ -1,251 +0,0 @@ -% ------------------------------------------------------------------------- -% CryoGRID3 -% main script for running the model -% -% Developed by: S. Westermann and M. Langer 2015 -% -% ------------------------------------------------------------------------- - -paraFromFile = exist('configFile'); % check if config file passed -add_modules; % adds required modules -createLogFile=1; % set to true/1 if the command window output shall be saved - -%---------------define input parameters------------------------------------ -% here you provide the ground stratigraphy -% z w/i m o type porosity -PARA.soil.layer_properties=[ 0.0 0.40 0.10 0.15 1 0.75;... - 0.15 0.65 0.30 0.05 2 0.65;... - 0.9 0.40 0.55 0.05 1 0.40;... - 9.0 0.30 0.70 0.00 1 0.30 ]; -% soil stratigraphy -% column 1: start depth of layer (first layer must start with 0) - each layer extends until the beginning of the next layer, the last layer -% extends until the end of the model domain -% column 2: volumetric water+ice content -% column 3: volumetric mineral content -% column 4: volumetric organic content -% column 5: code for soil type: 1: sand, 2: silt -% column 6: natural porosity - should be the same as 1-mineral-organic if no ground subsidence/thermokarst occurs - -%------ model parameters -------------------------------------------------- -% parameters related to surface energy balance and boundary conditions -PARA.soil.albedo=0.2; % albedo snow-free surface -PARA.soil.albedoPond=0.07; % albedo of water, used when the uppermost grod cell is 100% water due to modeled thermokarst development -PARA.soil.epsilon=0.97; % emissvity snow-free surface -PARA.soil.z0=1e-3; % roughness length [m] snow-free surface -PARA.soil.rs=50; % surface resistance against evapotransiration [m^-1] snow-free surface -PARA.soil.Qgeo=0.05; % geothermal heat flux [W/m2] -PARA.soil.kh_bedrock=3.0; % thermal conductivity of the mineral soil fraction [W/mK] - -% parameters related to hydrology scheme -PARA.soil.fieldCapacity=0.3; %water holding capacity of the soil - this must be adapted to fit the upperlost layers!! -PARA.soil.evaporationDepth=0.1; %depth to which evaporation occurs - place on grid cell boundaries -PARA.soil.rootDepth=0.2; %depth affected by transpiration - place on grid cell boundaries -PARA.soil.wiltingPoint=0.2; %point at which transpiration shuts off -PARA.soil.residualWC=0.05; %water always remaining in the soil, not accessible to evaporation -PARA.soil.ratioET=0.5; % 1: only transpiration; 0: only evaporation, values in between must be made dependent on LAI, etc. -PARA.soil.externalWaterFlux=0;%-2e-3; %external water flux / drainage in [m/day] -PARA.soil.convectiveDomain=[]; % soil domain where air convection due to buoyancy is possible -> start and end [m] - if empty no convection is possible -PARA.soil.mobileWaterDomain=[0 10.0]; % soil domain where water from excess ice melt is mobile -> start and end [m] - if empty water is not mobile -PARA.soil.waterTable=0.0; % depth at which a water table will form [m] - above excess water is removed, below it pools up - -% parameters related to snow -PARA.snow.max_albedo=0.85; % albedo of fresh snow -PARA.snow.min_albedo=0.5; % albedo of old snow -PARA.snow.epsilon=0.99; % surface emissivity snow -PARA.snow.z0=5e-4; % roughness length surface [m] -PARA.snow.rs=0.0; % surface resistance -> should be 0 for snow -PARA.snow.rho_snow=200.0; % density in [kg/m3] -PARA.snow.tau_1=86400.0; % time constants of snow albedo change (according to ECMWF reanalysis) [sec] -PARA.snow.tau_a=0.008; % [per day] -PARA.snow.tau_f=0.24; % [per day] -PARA.snow.maxSnow= [] ; % maximum snow depth that can be reached [m] - excess snow is removed in the model - if empty, no snow threshold -PARA.snow.extinction=25.0; % light extinction coefficient of snow - -% parameters related to the site location -PARA.location.altitude=20.0; %used to generate pressure forcing based on barometric altitude formula, if pressure forcing is not given - -% technical parameters -PARA.technical.z=2.0; % height of input air temperature above ground in [m] - assumed constant even when snow depth increases -PARA.technical.SWEperCell=0.005; % SWE per grid cell in [m] - determines size of snow grid cells -PARA.technical.maxSWE=0.4; % in [m] SWE -PARA.technical.arraySizeT=5002; % number of values in the look-up tables for conductivity and capacity -PARA.technical.starttime=datenum('2000.06.01 00:00:00','yyyy.mm.dd HH:MM:SS'); % starttime of the simulation - if empty start from first value of time series -PARA.technical.endtime=datenum('2000.06.15 00:00:00','yyyy.mm.dd HH:MM:SS'); % endtime of the simulation - if empty end at last value of time series -PARA.technical.minTimestep=0.1 ./ 3600 ./ 24; % smallest possible time step in [days] - here 0.1 seconds -PARA.technical.maxTimestep=300 ./ 3600 ./ 24; % largest possible time step in [days] - here 300 seconds -PARA.technical.targetDeltaE=1e5; % maximum energy change of a grid cell between time steps in [J/m3] %1e5 corresponds to heating of pure water by 0.025 K -PARA.technical.outputTimestep= 3 ./ 24.0 ; % output time step in [days] - here three hours -PARA.technical.saveDate='01.08.'; % date of year when output file is written - no effect if "saveInterval" is empty -PARA.technical.saveInterval=[]; % interval [years] in which output files are written - if empty the entire time series is written - minimum is 1 year -PARA.technical.waterCellSize=0.02; % default size of a newly added water cell when water ponds below water table [m] -PARA.technical.subsurfaceGrid = [[0:0.02:2], [2.1:0.1:10], [10.2:0.2:20], [21:1:30], [35:5:50], [60:10:100], [200:100:1000]]'; % the subsurface K-grid in [m] - -%initial temperature profile -> first column depth [m] -> second column temperature [degree C] -%default: -PARA.Tinitial = [-5 10;... - 0 0;... - 5 -5;... - 20 -10;... - 100 -10;... - 2000 10]; - -% load natural constants (given in SI units) to PARA.constants -PARA = loadConstants(PARA); - -%FORCING data mat-file -PARA.forcing.filename='samoylov_ERA_obs_fitted_1979_2014_spinup.mat'; %must be in subfolder "forcing" and follow the conventions for CryoGrid 3 forcing files -PARA.forcing.rain_fraction=0; -PARA.forcing.snow_fraction=0; - -% switches for modules -PARA.modules.infiltration = 0; % true if infiltration into unfrozen ground occurs - -% ------update parameter values if config file provided ------------------- -% ------changes output directory to name specified in configfile which is the config filename by default -if paraFromFile - run(configFile); -end - -run_number = sprintf('testrun'); - -% ------make output directory (name depends on parameters) ---------------- -mkdir(run_number) - -% ------redirect command line output to logfile --------------------------- -if createLogFile - diary(['./' run_number '/' run_number '_log.mat']); -end - - -%-------------------------------------------------------------------------- -%-----------do not modify from here onwards-------------------------------- -%-------------------------------------------------------------------------- -[FORCING, success]=load_forcing_from_file(PARA); % load FORCING mat-file - -if ~success - return -end -clear success - -PARA = initializeParameters(PARA, FORCING); %set start time, etc. - -%----------------create and initialize the grids -------------------------- -GRID=makeGrids(PARA); %create all grids -GRID=createStratigraphy(PARA,GRID); %interpolate input stratigraphy to the soil grid - -%----- initializie soil thermal properties -------------------------------- -GRID = initializeSoilThermalProperties(GRID, PARA); - -%------ initializie snow properties---------------------------------------- -GRID = initializeSnow(GRID); - -%---- initialize the surface energy balance struct ------------------------ -SEB = initializeSEB(); - -%---- initialize temperature profile -------------------------------------- -T = inititializeTemperatureProfile_simple(GRID, PARA, FORCING); - -%---- modification for infiltration -wc=GRID.soil.cT_water; -GRID.soil.E_lb = find(PARA.soil.evaporationDepth==GRID.soil.soilGrid(:,1))-1; -GRID.soil.T_lb= find(PARA.soil.rootDepth==GRID.soil.soilGrid(:,1))-1; - -%---- preallocate temporary arrays for capacity and conductivity----------- -[c_cTgrid, k_cTgrid, k_Kgrid, lwc_cTgrid] = initializeConductivityCapacity(T,wc, GRID, PARA); - -%---- energy and water balance initialization ----------------------------- -BALANCE = initializeBALANCE(T, wc, c_cTgrid, lwc_cTgrid, GRID, PARA); - -%__________________________________________________________________________ -%-------- provide arrays for data storage --------------------------------- -[t, TEMPORARY] = generateTemporary(T, PARA); -OUT = generateOUT(); - -disp('initialization successful'); -save([run_number '/' run_number '_settings.mat'], 'FORCING', 'PARA', 'GRID') - -%% ________________________________________________________________________ -% Time Integration Routine I -% I -%_________________________________________________________________________I - -while t 0.5 * min( GRID.general.K_delta.^2 .* c_cTgrid ./ k_cTgrid ./ (GRID.soil.cT_domain + GRID.snow.cT_domain) ) ./ (24.*3600) - warning( 'numerical stability not guaranteed' ); - end - - %------ update T array ------------------------------------------------ - T = T + SEB.dE_dt./c_cTgrid./GRID.general.K_delta.*timestep.*24.*3600; - T(GRID.air.cT_domain)=FORCING.i.Tair; - - %------- snow cover module -------------------------------------------- - [T, GRID, PARA, SEB, BALANCE] = CryoGridSnow(T, GRID, FORCING, SEB, PARA, c_cTgrid, timestep, BALANCE); - [GRID, T, BALANCE] = updateGRID_snow(T, GRID, PARA, BALANCE); - - %------- infiltration module------------------------------------------- - if PARA.modules.infiltration - [wc, GRID, BALANCE] = CryoGridInfiltration(T, wc, dwc_dt, timestep, GRID, PARA, FORCING, BALANCE); - end - - %------- update Lstar for next time step ------------------------------ - SEB = L_star(FORCING, PARA, SEB); - - %------- water balance calculations ----------------------------------- - % rainfall - BALANCE.water.dp_rain = BALANCE.water.dp_rain + FORCING.i.rainfall.*timestep; %sum up rainfall in [mm] per output interval - % snowfall - BALANCE.water.dp_snow = BALANCE.water.dp_snow + FORCING.i.snowfall.*timestep; %sum up snowfall in [mm SWE] per output interval - - %---------- sum up + OUTPUT ------------------------------------------- - sum_up_output_store; - - %------- next time step ----------------------------------------------- - t=t+timestep; - - %final energy state - if t>=PARA.technical.endtime - % get lwc for current (after timestep) thermal state - [c_cTgrid, ~, ~, lwc_cTgrid] = getThermalPropertiesInfiltration(T, wc, c_cTgrid, k_cTgrid, k_Kgrid, lwc_cTgrid, GRID, PARA); - BALANCE = updateBALANCE(T, wc, c_cTgrid, lwc_cTgrid, BALANCE, GRID, PARA); - % final output at t=endtime - sum_up_output_store; - end - - -end -%profile off -save([run_number '/' run_number '_output.mat'], 'OUT') -disp('Done.'); \ No newline at end of file diff --git a/CryoGrid3_lake.m b/CryoGrid3_lake.m new file mode 100644 index 0000000..2710d74 --- /dev/null +++ b/CryoGrid3_lake.m @@ -0,0 +1,604 @@ +% CryoGRID3 +% main script for running the model +% +% Developed by: S. Westermann and M. Langer 2015 + +% +% Extended by J. Nitzborn (infiltration of soils, lateral exchange of heat, water, snow) + +% Extended by T. Schneider von Deimling (coupling with FLAKE (based on version M. Langer) +% ---------------------------------------------------------------------------------------- + +%clear all; close all; profile off + +% runs modes +debug_mode=0 % if set to 1, timestep = timestepMin for debugging (avoid of NaN for timestep calculation) +par_mode = 1; % parallel mode off/on + +if(par_mode==1) + delete(gcp('nocreate')) % must be off in batch mode +end + +add_modules; %adds required modules + +number_of_realizations=2; % specify number of workers + +if number_of_realizations>1 && isempty( gcp('nocreate') ) + parpool(number_of_realizations); % must not be invoked here in batch mode +end + +spmd %zzz use function calls to calls below to enable debugging in par mode! + index=labindex; %number identifying the process; change this to e.g. 1 for single realization (non-parallel) run +%nnn index=1 + +%---------------define input parameters------------------------------------ + % here you provide the ground stratigraphy + % z w/i m o type porosity + + % default stratigraphy used in publication: + PARA.soil.layer_properties=[ 0.0 0.60 0.10 0.15 1 0.75;... + 0.15 0.65 0.3 0.05 2 0.65;... + 0.9 0.65 0.3 0.05 1 0.65;... + 9.0 0.30 0.70 0.00 1 0.30 ]; + % soil stratigraphy + % column 1: start depth of layer (first layer must start with 0) - each layer extends until the beginning of the next layer, the last layer extends until the end of the model domain + % column 2: volumetric water+ice content; column 3: volumetric mineral content; column 4: volumetric organic content; + % column 5: code for soil type: 1: sand, 2: silt + % column 6: natural porosity - should be the same as 1-mineral-organic if no ground subsidence/thermokarst occurs + + %------ model parameters -------------------------------------------------- + % parameters related to soil + PARA.soil.albedo=0.2; % albedo snow-free surface + PARA.soil.epsilon=0.97; % emissvity snow-free surface + PARA.soil.z0=1e-3; % roughness length [m] snow-free surface + PARA.soil.rs=50; % surface resistance against evapotransiration [m^-1] snow-free surface + PARA.soil.Qgeo=0.05; % geothermal heat flux [W/m2] + PARA.soil.kh_bedrock=3.0; % thermal conductivity of the mineral soil fraction [W/mK] + + % parameters related to hydrology scheme + PARA.soil.fieldCapacity=0.5; %water holding capacity of the soil - this must be adapted to fit the upperlost layers!! + PARA.soil.evaporationDepth=0.1; %depth to which evaporation occurs - place on grid cell boundaries + PARA.soil.rootDepth=0.2; %depth affected by transpiration - place on grid cell boundaries + PARA.soil.wiltingPoint=0.2; %point at which transpiration shuts off + PARA.soil.residualWC=0.05; %water always remaining in the soil, not accessible to evaporation + PARA.soil.ratioET=0.5; % 1: only transpiration; 0: only evaporation, values in between must be made dependent on LAI, etc. + %tsvd PARA.soil.externalWaterFlux=2e-3; %external water flux / drainage in [m/day] + PARA.soil.externalWaterFlux=0.; %external water flux / drainage in [m/day] + PARA.soil.convectiveDomain=[]; % soil domain where air convection due to buoyancy is possible -> start and end [m] - if empty no convection is possible + PARA.soil.mobileWaterDomain=[0 10.0]; % soil domain where water from excess ice melt is mobile -> start and end [m] - if empty water is not mobile + PARA.soil.relative_maxWater=0.; % depth at which a water table will form [m] - above excess water is removed, below it pools up jjj zzz + PARA.soil.hydraulic_conductivity = 1e-5; + PARA = loadSoilTypes( PARA ); + % parameters related to snow + PARA.snow.max_albedo=0.85; % albedo of fresh snow + PARA.snow.min_albedo=0.5; % albedo of old snow + PARA.snow.epsilon=0.99; % surface emissivity snow + PARA.snow.z0=5e-4; % roughness length surface [m] + PARA.snow.rs=0.0; % surface resistance -> should be 0 for snow + PARA.snow.rho_snow=200.0; % density in [kg/m3] + PARA.snow.tau_1=86400.0; % time constants of snow albedo change (according to ECMWF reanalysis) [sec] + PARA.snow.tau_a=0.008; % [per day] + PARA.snow.tau_f=0.24; % [per day] + PARA.snow.relative_maxSnow= [0.1]; %ttt maximum snow depth that can be reached [m] - excess snow is removed in the model - if empty, no snow threshold + PARA.snow.extinction=25.0; % light extinction coefficient of snow + %tsvd add lake parameters + % parameters related to lake + PARA.water.albedo=0.05; % albedo water (parameterization after Wayne and Burt (1954) in surfaceCondition.m) + PARA.water.epsilon=0.99; % surface emissivity water + PARA.water.rs=0.; % surface resistance -> should be 0 for water + %tsvd + PARA.water.z0=5e-4; % roughness length surface [m] % JAN: value for summer / vegetation + %tsvd PARA.water.z0=1e-4; % roughness length surface [m] - gets overridden by value calculated by function flake_roughnessLength.m version Flake + PARA.water.extinction=1.2; % light extinction coefficient of water + PARA.water.depth=0.; + PARA.water.fetch=20; + + PARA.ice.albedo =0.20; % albedo ice / Lei et al. (2011) shows a range of 0.1 to 0.35 + PARA.ice.epsilon=0.98; % surface emissivity snow + PARA.ice.rs=0.0; % surface resistance -> should be 0 for ice + PARA.ice.z0=5e-4; % roughness length surface [m] % JAN: value for snow + PARA.ice.extinction=4.5; % [m^-1] light extinction coefficient of ice / Lei et al. (2011) shows a range of 1 to 5 m^-1 + + PARA.technical.z=2.0; % height of input air temperature above ground in [m] - assumed constant even when snow depth increases + PARA.technical.SWEperCell=0.005; % SWE per grid cell in [m] - determines size of snow grid cells + PARA.technical.maxSWE=0.4; % in [m] SWE + PARA.technical.arraySizeT=5002; % number of values in the look-up tables for conductivity and capacity + PARA.technical.starttime=datenum(2010, 6, 1); % starttime of the simulation - if empty start from first value of time series + PARA.technical.endtime=datenum(2099, 12, 30); % endtime of the simulation - if empty end at last value of time series + %PARA.technical.endtime = SETUP.endtime; % endtime of the simulation - if empty end at last value of time series + PARA.technical.minTimestep=0.1 ./ 3600 ./ 24; % smallest possible time step in [days] - here 0.1 seconds + PARA.technical.maxTimestep=300 ./ 3600 ./ 24; % largest possible time step in [days] - here 300 seconds + %tsvd PARA.technical.targetDeltaE=1e5; % maximum energy change of a grid cell between time steps in [J/m3] %1e5 corresponds to heating of pure water by 0.025 K + PARA.technical.targetDeltaE=1e6; % maximum energy change of a grid cell between time steps in [J/m3] %1e5 corresponds to heating of pure water by 0.025 K lll DeltaE increased by 1 order of M + PARA.technical.outputTimestep= 3 ./ 24.0 ; % output time step in [days] - here three hours +% PARA.technical.syncTimeStep = 12 ./ 24.0 ; % output time step in [days] - here three hours + PARA.technical.syncTimeStep = 3 ./ 24.0 ; % output time step in [days] - here three hours + PARA.technical.saveDate='01.01.'; % date of year when output file is written - no effect if "saveInterval" is empty + PARA.technical.saveInterval=[1]; % interval [years] in which output files are written - if empty the entire time series is written - minimum is 1 year + PARA.technical.waterCellSize=0.02; % default size of a newly added water cell when water ponds below water table [m] + + %default grid used for publications and testing of water balance: + %tsvd PARA.technical.subsurfaceGrid = [[0:0.02:4], [4.1:0.1:10], [10.2:0.2:20], [21:1:30], [35:5:50], [60:10:100], [200:100:1000]]'; % the subsurface K-grid in [m] + PARA.technical.subsurfaceGrid = [[0:0.04:4], [4.1:0.1:10], [10.2:0.2:20], [21:1:30], [35:5:50], [60:10:100], [200:100:1000]]'; % the subsurface K-grid in [m] + + PARA.location.area=1.0; + PARA.location.initial_altitude=0.0; + % JAN: the following quantities are dynamic and should hence be moved to another struct, e.g. "STATE" + PARA.location.altitude = PARA.location.initial_altitude; % used to generate pressure forcing based on barometric altitude formula, if pressure forcing is not given; excluding snow domain + PARA.location.soil_altitude=PARA.location.initial_altitude; + PARA.location.surface_altitude=PARA.location.initial_altitude; % this is dynamic and refers to the surface including snow + PARA.location.active_layer_depth_altitude = nan; % defined at runtime + PARA.location.water_table_altitude = nan; % defined at runtime + % thresholds + PARA.location.absolute_maxWater_altitude = PARA.location.altitude + PARA.soil.relative_maxWater; + if isempty( PARA.snow.relative_maxSnow ) + PARA.location.absolute_maxSnow_altitude = []; + else + PARA.location.absolute_maxSnow_altitude = [ PARA.location.altitude + PARA.snow.relative_maxSnow ]; + end +%tsvd + PARA.location.latitude = 72.376; % [deg] < - + PARA.location.longitude = 126.491; % [deg] < - + %PARA.location.GridCellSize = 1e6; % [m^2] < - + %PARA.location.TileFraction = 1e-2; + % parameters related to the site location + PARA.location.water_table_altitude = nan; % defined at runtime + + %initial temperature profile -> first column depth [m] -> second column temperature [degree C] + PARA.Tinitial = [ -2 5 ;... + 0 0 ;... + 2 -5 ;... + 10 -10 ;... + 25 -9 ;... + 100 -9 ;... + 2000 10 ]; + +% PARA.Tinitial = [ -2 5 ;... +% 0 0 ;... +% 2 -2 ;... +% 5 -7 ;... +% 10 -9 ;... +% 25 -9 ;... +% 100 -8 ;... +% 1100 10.2 ]; % the geothermal gradient for Qgeo=0.05W/m² and K=2.746W/Km is about 18.2 K/km + +% load natural constants (given in SI units) to PARA.constants + PARA = loadConstants( PARA ); + + %FORCING data mat-file + %PARA.forcing.filename='samoylov_ERA_obs_fitted_1979_2014_spinup.mat'; %must be in subfolder "forcing" and follow the conventions for CryoGrid 3 forcing files + PARA.forcing.filename='Samoylov_rcp85_1901_2300_CryoGrid_windModified.mat'; + %PARA.forcing.filename='CG3_CCLM_forcing_90_101'; + PARA.forcing.rain_fraction=1; + PARA.forcing.snow_fraction=1; + + % switches for modules + PARA.modules.infiltration=1; % true if infiltration into unfrozen ground occurs + PARA.modules.xice=0; % true if thaw subsicdence is enabled + PARA.modules.lateral=1; % true if adjacent realizations are run (this does not require actual lateral fluxes) +%tsvd extended for lateral switched off + if ~PARA.modules.lateral + PARA.modules.exchange_heat = 0; + PARA.modules.exchange_water = 0; + PARA.modules.exchange_snow = 0; + PARA.snow.maxSnow= [] ; % maximum snow depth that can be reached [m] - excess snow is removed in the model - if empty, no snow threshold + % in par mode this is replaced by PARA.ensemble.immobile_snow_height + elseif PARA.modules.lateral + % switches for lateral processes + PARA.modules.exchange_heat = 1; + PARA.modules.exchange_water = 0; %ttt + PARA.modules.exchange_snow = 0; %ttt + + %---------overwrites variables for each realization-------------------- + % this function must define everything that is realization-specific or dependent of all realizations + PARA = get_parallel_variables( PARA ); + %PARA = get_parallel_variables_batch( PARA,SETUP ); + end + + disp('Running experiment with xxxx -> indicate switches here') + % ------make output directory (name depends on parameters) ---------------- + saveDir = './runs_RCP85'; + run_number= sprintf( 'WD%d_LD%d_LR%d_LF%d',[PARA.water.depth,5,10,25] ) % water depth in m, lake coverage in % + %run_number= sprintf( 'WD%d_LD%d_LR%d_LF%d',[PARA.water.depth, PARA.water.depth, SETUP.LR, 100*SETUP.LF] ) % water depth in m, lake coverage in % + %run_number= sprintf( 'WD%1.0f_xH%d',[PARA.water.depth,PARA.modules.exchange_heat] ) + + mkdir([ saveDir '/' run_number]); + + %tsvd ------redirect command line output to logfile (does not work here in batch/parallel mode (use diary(job{jobnumber}) instead) + % if createLogFile + % diary([ saveDir '/' run_number '/' run_number '_log.mat']); + % end + + %-------------------------------------------------------------------------- + %-----------do not modify from here onwards-------------------------------- + %-------------------------------------------------------------------------- + [FORCING, success]=load_forcing_from_file(PARA); % load FORCING mat-file + + if ~success + warning('A problem with the Forcing occured.'); + end + + PARA = initializeParameters(PARA, FORCING); %set start time, etc. + + %----------------create and initialize the grids -------------------------- + GRID=makeGrids(PARA); %create all grids + GRID=createStratigraphy(PARA,GRID); %interpolate input stratigraphy to the soil grid (GRID.soil.*) + + %----- initializie excess ground ice -------------------------------------- + [GRID,PARA] = initializeExcessIce2(GRID,PARA); + + %----- initializie soil thermal properties -------------------------------- + GRID = initializeSoilThermalProperties(GRID, PARA); + + %------ initializie snow properties---------------------------------------- + GRID = initializeSnow(GRID); + + %---- initialize the surface energy balance struct ------------------------ + SEB = initializeSEB(); +%tsvd replace by new call lake scheme +% %---- initialize the water body module ------------------------------------ +% GRID = initializeLAKE(GRID); + +%---- initialize the lake scheme structs ---------------------------------- + [FLAKE GRID] = initializeLAKE(GRID, PARA); + %---- initialize temperature profile -------------------------------------- + T = inititializeTemperatureProfile_simple(GRID, PARA, FORCING); + + %---- modification for infiltration + wc=GRID.soil.cT_water; + GRID.soil.E_lb = find(PARA.soil.evaporationDepth==GRID.soil.soilGrid(:,1))-1; + GRID.soil.T_lb= find(PARA.soil.rootDepth==GRID.soil.soilGrid(:,1))-1; + + %---- preallocate temporary arrays for capacity and conductivity----------- + [c_cTgrid, k_cTgrid, k_Kgrid, lwc_cTgrid] = initializeConductivityCapacity(T,wc, GRID, PARA); % this is basically the same as "getThermalProperties" during integration, but without interpolation to K grid + + %---- energy and water balance initialization ----------------------------- + BALANCE = initializeBALANCE(T, wc, c_cTgrid, lwc_cTgrid, GRID, PARA); + + %---- temporary arrays for storage of lateral fluxes --> these could go into a LATERAL struct or TEMPORARY? +%nnn comment out for single mode... + water_fluxes = zeros( numlabs, 1 ); % total water flux in [m/s] from each worker to worker index + snow_fluxes = zeros( numlabs, 1 ); % total snow flux in [m SWE] per output interval from each worker to worker index + heat_fluxes = zeros( numlabs, 1); % total heat flux in [J] per output interval of all workers to worker index + dE_dt_lateral = zeros( length(GRID.general.cT_grid), 1); % cell-wise heat flux in [W/m^3]? of all workers to worker index + dE_dt_lateral_j_cor = zeros( length(GRID.general.cT_grid), 1); % cell-wise heat flux in [W/m^3]? of all workers to worker index, corrected for interp bias + xxx = zeros( length(GRID.general.cT_grid), 1); % cell-wise heat flux in [W/m^3]? of all workers to worker index, corrected for interp bias + + %__________________________________________________________________________ + %-------- provide arrays for data storage --------------------------------- + [t, TEMPORARY] = generateTemporary(T, PARA); + OUT = generateOUT(); + + disp('initialization successful'); + %iSaveSettings( [ saveDir '/' run_number '/' run_number '_realization' num2str(index) '_settings.mat'] , FORCING, PARA, GRID) %tsvd non-interpolated forcing data saved! + iSaveSettings( [ saveDir '/' run_number '/' run_number '_settings.mat'] , FORCING, PARA, GRID) %tsvd non-interpolated forcing data saved! + + %% ________________________________________________________________________ + % Time Integration Routine I + % I + %_________________________________________________________________________I + + while t 0.5 * min( GRID.general.K_delta.^2 .* c_cTgrid ./ k_cTgrid ./ (GRID.soil.cT_domain + GRID.snow.cT_domain) ) ./ (24.*3600) + warning( 'numerical stability not guaranteed' ); + end + + %------ update T array ------------------------------------------------ + % account for vertical heat fluxes from ground heat flux and heat conduction + T = T + SEB.dE_dt./c_cTgrid./GRID.general.K_delta.*timestep.*24.*3600; + % account for lateral heat fluxes + T = T + dE_dt_lateral./c_cTgrid.*timestep.*24.*3600; % no division by K_delta necessary as dE_dt_lateral in [ J / m^3 / s ] + % set grid cells in air to air temperature + T(GRID.air.cT_domain)=FORCING.i.Tair; + + if max((T<-100))==1; disp('dwd'); end + + %------- water body module -------------------------------------------- +%tsvd T = mixingWaterBody(T, GRID); % zzz check whether this is ok to switch off + %------- snow cover module -------------------------------------------- + [T, GRID, PARA, SEB, BALANCE] = CryoGridSnow(T, GRID, FORCING, SEB, PARA, c_cTgrid, timestep, BALANCE); + [GRID, T, BALANCE] = updateGRID_snow(T, GRID, PARA, BALANCE); + + %------- infiltration module------------------------------------------- + if PARA.modules.infiltration + lateral_water_flux = nansum( water_fluxes ); % lateral fluxes to/from other workers in [m/s] + [wc, GRID, BALANCE] = CryoGridInfiltration(T, wc, dwc_dt, timestep, GRID, PARA, FORCING, BALANCE, lateral_water_flux); + end +%tsvd + %------- water body module -------------------------------------------- + if timestep>0 && ( ~isempty(GRID.lake.water.cT_domain_ub) || ~isempty(GRID.lake.ice.cT_domain_ub) ) + %ice cover + [T GRID FLAKE] = cryoGridIceCover(GRID, SEB, FLAKE, T, c_cTgrid, timestep); + %FLAKE + if isempty(GRID.lake.ice.cT_domain_ub) && sum(GRID.lake.water.cT_domain)>=3 + %FLAKE is used if no ice cells exist + [T GRID FLAKE] = cryoGridFLAKE(FLAKE, GRID, SEB, PARA, T, T_old, timestep, k_Kgrid, SEB.dE_dt, SEB.dE_dt_SEB); + else + %updated FLAKE variables also when FLAKE is not used + FLAKE.t_bot_n_flk = T(GRID.lake.water.cT_domain_lb) + 273.15; + %set mixed layerdepth to zero + FLAKE.h_ml_n_flk = 0; + end + GRID = updateGRID_flake(GRID); + end + %--------- buoyancy calculation in block fields------------------------ + %T = convectionInBlocks(T, c_cTgrid, GRID) %not available + % @ ALEX: here the calculations of the excess ice module take place + % (uncomment this line to "activate" the module). Note that the + % stratigraphy must be modified such that excess ground ice is present + % in order to make the module "work" + + %------- excess ice module -------------------------------------------- + if PARA.modules.xice && ~PARA.modules.infiltration + warning( 'energy and water balances are not correct for this combination of modules'); % zzz ... + [GRID, PARA] = excessGroundIce(T, GRID, PARA); +%tsvd version Flake [GRID, PARA, wc] = excessGroundIce(T, GRID, PARA); + % assure wc has correct length + wc = wc( end-sum(GRID.soil.cT_domain)+1 : end ); +%tsvd wc( end-sum(GRID.soil.cT_domain)+1 : end ); % make vector dims consistent + elseif PARA.modules.xice && PARA.modules.infiltration + [GRID, PARA, wc, meltwaterGroundIce] = excessGroundIceInfiltration(T, wc, GRID, PARA); +%tsvd [GRID, PARA, wc] = excessGroundIceInfiltration(T, wc, GRID, PARA); + GRID = updateGRID_excessiceInfiltration2(meltwaterGroundIce, GRID); + end + + %------- update Lstar for next time step ------------------------------ + SEB = L_star(FORCING, PARA, SEB); + + %------- update auxiliary state variables + PARA.location.altitude = getAltitude( PARA, GRID ); + PARA.location.surface_altitude = getSurfaceAltitude( PARA, GRID ); + PARA.location.active_layer_depth_altitude = getActiveLayerDepthAltitude( PARA, GRID, T ); + PARA.location.water_table_altitude = getWaterTableAltitude(T, wc, GRID, PARA); + + %------- update threshold variables if no lateral exchange processes occur, otherwise updated at sync time + if ~PARA.modules.lateral + PARA.location.absolute_maxWater_altitude = PARA.location.altitude + PARA.soil.relative_maxWater; + if isempty( PARA.snow.maxSnow ) + %tsvd if isempty( PARA.ensemble.immobile_snow_height ) + PARA.location.absolute_maxSnow_altitude = []; + else + PARA.location.absolute_maxSnow_altitude = [ PARA.ensemble.altitude + PARA.snow.relative_maxSnow ]; + end + end + %------- water balance calculations ----------------------------------- + % rainfall + BALANCE.water.dp_rain = BALANCE.water.dp_rain + FORCING.i.rainfall.*timestep; %sum up rainfall in [mm] per output interval + % snowfall + BALANCE.water.dp_snow = BALANCE.water.dp_snow + FORCING.i.snowfall.*timestep; %sum up snowfall in [mm] SWE per output interval + + %------- lateral exchange module -------------------------------------- + % all functions called in this block should go into /modules/cryoGridLateral + % calling PARA.ensemble is only allowed here + if PARA.modules.lateral + if t==TEMPORARY.syncTime %communication between workers + disp('CryoGridLateral: sync - start'); + labBarrier(); %common start + PARA = updateAuxiliaryVariablesAndCommonThresholds(T, wc, GRID, PARA) ; + + % heat exchange module + if PARA.modules.exchange_heat + labBarrier(); + heat_fluxes = zeros( number_of_realizations, 1); + % check preconditions + precondition_heatExchange = true; %no specific conditions so far + if precondition_heatExchange + %WRAPPER + disp('sync - exchanging heat'); + % calculate lateral heat fluxes + dE_dt_lateral = zeros( length(GRID.general.cT_grid), 1); %in [J/m^3/s] + % zzz not needed!? dE_dt_lateral_j_cor = zeros( length(GRID.general.cT_grid), 1); %in [J/m^3/s] + % xxx = zeros( length(GRID.general.cT_grid), 1); %in [J/m^3/s] + PACKAGE_heatExchange.T = T; + PACKAGE_heatExchange.cT_grid = GRID.general.cT_grid; + PACKAGE_heatExchange.k_cTgrid = k_cTgrid; + for j=1:number_of_realizations + if j~=index + labSend( PACKAGE_heatExchange, j, 1); + end + end + for j=1:number_of_realizations + if j~=index + PACKAGE_heatExchange_j = labReceive(j, 1); + [dE_dt_lateral_j, BALANCE] = calculateLateralHeatFluxes(T, k_cTgrid, PACKAGE_heatExchange_j,GRID, PARA, BALANCE, j); % contribution from worker j to worker index + % now down after correction heat_fluxes(j) = nansum( dE_dt_lateral_j .* GRID.general.K_delta ); % in [J / m^2 s ], only for tracking the overall heat fluxes + % dE_dt_lateral = dE_dt_lateral + dE_dt_lateral_j; % sum up contributions from all realizations + end + end +%tsvd correct for biases in dE_dt_lateral (due to interpolation?), by using mean value for pairwise heat exchange + labBarrier(); + for j=1:number_of_realizations + if j~=index + %yyy2=dE_dt_lateral_j + labSend( nansum(dE_dt_lateral_j), j, 2); + end + end + for j=1:number_of_realizations + if j~=index + xxx = labReceive(j, 2); %ttt rm xxx yyy .... if not needed... + %dE_dt_lateral_j_cor = -xxx/nansum(dE_dt_lateral_j) * dE_dt_lateral_j; % scale...xxx (fluxes have opposite sign) + dE_dt_lateral_j_cor = (-xxx+nansum(dE_dt_lateral_j))/(2*nansum(dE_dt_lateral_j)) * dE_dt_lateral_j; % scale dE_dt lateral to mean value between workers (fluxes have opposite sign) + heat_fluxes(j) = nansum( dE_dt_lateral_j_cor .* GRID.general.K_delta ); % in [J / m^2 s ], only for tracking the overall heat fluxes +% % dE_dt_lateral = dE_dt_lateral + dE_dt_lateral_j; % sum up contributions from all realizations + % dE_dt_lateral_cor = dE_dt_lateral_cor + dE_dt_lateral_j_cor; % sum up contributions from all realizations - here on two workers... + dE_dt_lateral = dE_dt_lateral_j_cor; % if more than two workers, need to sum up here.... xxx zzz + end + end + end + end + + % water exchange module + if PARA.modules.exchange_water + labBarrier(); + water_fluxes = zeros( number_of_realizations, 1 ); % in [m/s] + % check preconditions + precondition_waterExchange = checkPreconditionWaterExchange( T, GRID ); + if precondition_waterExchange + % WRAPPER + disp('sync - exchanging water'); + % calculate lateral water fluxes + PACKAGE_waterExchange.water_table_altitude = PARA.ensemble.water_table_altitude(index); + PACKAGE_waterExchange.active_layer_depth_altitude = PARA.ensemble.active_layer_depth_altitude(index); + PACKAGE_waterExchange.infiltration_condition = T(GRID.soil.cT_domain_ub)>0 && isempty(GRID.snow.cT_domain_ub); %jjj zzz + for j=1:number_of_realizations + if j~=index + labSend( PACKAGE_waterExchange, j, 3); + end + end + for j=1:number_of_realizations + if j~=index + PACKAGE_waterExchange_j = labReceive(j, 3); + % JAN: for now: assume DarcyFlux and distribute over sync time step (no check for available water, missing water tracked in BALANCE.dm_lacking) + water_fluxes(j) = calculateLateralWaterFluxes( T, PACKAGE_waterExchange_j, GRID, PARA, j); + end + end + % for debugging: print water flux per column + waterflux = nansum( water_fluxes.*PARA.technical.syncTimeStep.*24.*3600 ); % in m + fprintf('Water flux to worker %d = %f mm \n', [index, waterflux*1000] ); + end + + end + + % snow exchange module + if PARA.modules.exchange_snow + labBarrier(); + % check preconditions + precondition_snowExchange = checkPreconditionSnowExchange( GRID, PARA ); + if precondition_snowExchange + disp('sync - exchanging snow'); + % calculate terrain index with updated surface_altitudes + PARA.ensemble.terrain_index_snow = calculateTerrainIndexSnow(PARA.ensemble.surface_altitude, PARA.ensemble.weight); + + % calculate mobile snow + % WRAPPER + mobile_snow = zeros( 1, number_of_realizations ); + my_mobile_snow = 0; + meltingConditions_index = sum(GRID.snow.Snow_w)>0 || sum(T(GRID.snow.cT_domain)>0)>0; %snow is assumed to be immobile under melting conditions CHECK WITH SNOW MODULE + if ~isempty(GRID.snow.cT_domain_ub) && ~meltingConditions_index % current realization has snow cover and no melting conditions + i=0; + while (abs( GRID.general.K_grid(GRID.snow.cT_domain_ub+i)-GRID.general.K_grid(GRID.snow.cT_domain_lb+1) )... % snow only mobile above realization-specific threshold + > PARA.ensemble.immobile_snow_height(index)+GRID.general.K_delta(GRID.snow.cT_domain_ub)) ... % if upper cell is drifted away, immobile snow height remains + && (PARA.ensemble.initial_altitude(index)-GRID.general.K_grid(GRID.snow.cT_domain_ub+i) ... % snow only mobile above lowermost surface altitude + snowCellSize (to prevent oscillations) + - (min( PARA.ensemble.surface_altitude )+GRID.snow.snowCellSize) > 1e-6 ) + my_mobile_snow = my_mobile_snow + (GRID.snow.Snow_i(GRID.snow.cT_domain_ub+i)); %only "ice" mobile + i=i+1; + end + end + mobile_snow(index) = my_mobile_snow; + % exchange mobile snow amounts + for j=1:number_of_realizations + if j~=index + % send mobile snow amount in [m SWE] + labSend( mobile_snow(index), j, 4 ); + end + end + for j=1:number_of_realizations + if j~=index + % receive mobile snow amount [m SWE] + mobile_snow(j) = labReceive(j, 4); + end + end + % calculate lateral snow fluxes + my_snow_change = calculateLateralSnowFluxes2( mobile_snow, PARA ); + % apply lateral snow fluxes directly + if my_snow_change ~= 0 + [T, GRID] = applyLateralSnowFluxes( T, PARA, GRID, FORCING, my_snow_change ); + [GRID, T, BALANCE] = updateGRID_snow(T, GRID, PARA, BALANCE); + BALANCE.water.dr_lateralSnow = BALANCE.water.dr_lateralSnow + my_snow_change ; + end + + snow_fluxes = zeros( numlabs , 1 ); + snow_fluxes(index) = my_snow_change ./ (PARA.technical.syncTimeStep.*3600.*24); %this is only to have comparable output to other fluxes + fprintf('Snow flux to worker %d = %f mm SWE \n', [index, my_snow_change*1000] ); + + end + end + + labBarrier(); + PARA = updateAuxiliaryVariablesAndCommonThresholds(T, wc, GRID, PARA) ; + + TEMPORARY.syncTime=round((TEMPORARY.syncTime + PARA.technical.syncTimeStep)./PARA.technical.syncTimeStep).*PARA.technical.syncTimeStep; + disp('sync - done'); + end + end + + %------- next time step ----------------------------------------------- + t=t+timestep; + %---------- sum up + OUTPUT ------------------------------------------- + %nnn + [TEMPORARY, OUT, BALANCE] = sum_up_output_store(t, T, wc, lwc_cTgrid(GRID.soil.cT_domain), timestep, TEMPORARY, BALANCE, PARA, GRID, SEB, OUT, saveDir, run_number, water_fluxes, snow_fluxes, heat_fluxes); + end + + % save final state and output at t=endtime +%iSaveOUT( [ saveDir '/' run_number '/' run_number '_realization' num2str(index) '_output' datestr(t,'yyyy') '.mat'], OUT) +%iSaveState( [ saveDir '/' run_number '/' run_number '_realization' num2str(index) '_finalState' datestr(t,'yyyy') '.mat'], T, wc, t, SEB, PARA, GRID) + +%iSaveOUT( [ saveDir '/' run_number '/' run_number '_output' datestr(t,'yyyy') '.mat'], OUT) +%iSaveState( [ saveDir '/' run_number '/' run_number '_finalState' datestr(t,'yyyy') '.mat'], T, wc, t, SEB, PARA, GRID) + +%iPlotAltitudes( [ saveDir '/' run_number '/' run_number '_realization' num2str(index) '_altitudes_vs_time_' datestr(t,'yyyy') '.png'], OUT, PARA ); +end + +if number_of_realizations>1 + delete(gcp('nocreate')) +end +%tsvd +%dsave([ saveDir '/' run_number '/' run_number '_Workspace']) % save all distributed variables + +disp('Done.'); + diff --git a/CryoGrid3_lake_batch.m b/CryoGrid3_lake_batch.m new file mode 100644 index 0000000..eddf074 --- /dev/null +++ b/CryoGrid3_lake_batch.m @@ -0,0 +1,606 @@ +function CryoGrid3_lake_batch(SETUP) +% CryoGRID3 +% main script for running the model +% +% Developed by: S. Westermann and M. Langer 2015 + +% +% Extended by J. Nitzborn (infiltration of soils, lateral exchange of heat, water, snow) + +% Extended by T. Schneider von Deimling (coupling with FLAKE (based on version M. Langer) +% ---------------------------------------------------------------------------------------- + +%clear all; close all; profile off + +% runs modes +debug_mode=0 % if set to 1, timestep = timestepMin for debugging (avoid of NaN for timestep calculation) +par_mode = 1; % parallel mode off/on + +if(par_mode==1) +% delete(gcp('nocreate')) % must be off in batch mode +end + +add_modules; %adds required modules + +number_of_realizations=2; % specify number of workers + +if number_of_realizations>1 && isempty( gcp('nocreate') ) + parpool(number_of_realizations); % must not be invoked here in batch mode +end + +spmd %zzz use function calls to calls below to enable debugging in par mode! + index=labindex; %number identifying the process; change this to e.g. 1 for single realization (non-parallel) run +%nnn index=1 + +%---------------define input parameters------------------------------------ + % here you provide the ground stratigraphy + % z w/i m o type porosity + + % default stratigraphy used in publication: + PARA.soil.layer_properties=[ 0.0 0.60 0.10 0.15 1 0.75;... + 0.15 0.65 0.3 0.05 2 0.65;... + 0.9 0.65 0.3 0.05 1 0.65;... + 9.0 0.30 0.70 0.00 1 0.30 ]; + % soil stratigraphy + % column 1: start depth of layer (first layer must start with 0) - each layer extends until the beginning of the next layer, the last layer extends until the end of the model domain + % column 2: volumetric water+ice content; column 3: volumetric mineral content; column 4: volumetric organic content; + % column 5: code for soil type: 1: sand, 2: silt + % column 6: natural porosity - should be the same as 1-mineral-organic if no ground subsidence/thermokarst occurs + + %------ model parameters -------------------------------------------------- + % parameters related to soil + PARA.soil.albedo=0.2; % albedo snow-free surface + PARA.soil.epsilon=0.97; % emissvity snow-free surface + PARA.soil.z0=1e-3; % roughness length [m] snow-free surface + PARA.soil.rs=50; % surface resistance against evapotransiration [m^-1] snow-free surface + PARA.soil.Qgeo=0.05; % geothermal heat flux [W/m2] + PARA.soil.kh_bedrock=3.0; % thermal conductivity of the mineral soil fraction [W/mK] + + % parameters related to hydrology scheme + PARA.soil.fieldCapacity=0.5; %water holding capacity of the soil - this must be adapted to fit the upperlost layers!! + PARA.soil.evaporationDepth=0.1; %depth to which evaporation occurs - place on grid cell boundaries + PARA.soil.rootDepth=0.2; %depth affected by transpiration - place on grid cell boundaries + PARA.soil.wiltingPoint=0.2; %point at which transpiration shuts off + PARA.soil.residualWC=0.05; %water always remaining in the soil, not accessible to evaporation + PARA.soil.ratioET=0.5; % 1: only transpiration; 0: only evaporation, values in between must be made dependent on LAI, etc. + %tsvd PARA.soil.externalWaterFlux=2e-3; %external water flux / drainage in [m/day] + PARA.soil.externalWaterFlux=0.; %external water flux / drainage in [m/day] + PARA.soil.convectiveDomain=[]; % soil domain where air convection due to buoyancy is possible -> start and end [m] - if empty no convection is possible + PARA.soil.mobileWaterDomain=[0 10.0]; % soil domain where water from excess ice melt is mobile -> start and end [m] - if empty water is not mobile + PARA.soil.relative_maxWater=0.; % depth at which a water table will form [m] - above excess water is removed, below it pools up jjj zzz + PARA.soil.hydraulic_conductivity = 1e-5; + PARA = loadSoilTypes( PARA ); + + % parameters related to snow + PARA.snow.max_albedo=0.85; % albedo of fresh snow + PARA.snow.min_albedo=0.5; % albedo of old snow + PARA.snow.epsilon=0.99; % surface emissivity snow + PARA.snow.z0=5e-4; % roughness length surface [m] + PARA.snow.rs=0.0; % surface resistance -> should be 0 for snow + PARA.snow.rho_snow=200.0; % density in [kg/m3] + PARA.snow.tau_1=86400.0; % time constants of snow albedo change (according to ECMWF reanalysis) [sec] + PARA.snow.tau_a=0.008; % [per day] + PARA.snow.tau_f=0.24; % [per day] + PARA.snow.relative_maxSnow= [0.1]; %ttt maximum snow depth that can be reached [m] - excess snow is removed in the model - if empty, no snow threshold + PARA.snow.extinction=25.0; % light extinction coefficient of snow + %tsvd add lake parameters + % parameters related to lake + PARA.water.albedo=0.05; % albedo water (parameterization after Wayne and Burt (1954) in surfaceCondition.m) + PARA.water.epsilon=0.99; % surface emissivity water + PARA.water.rs=0.; % surface resistance -> should be 0 for water + %tsvd + PARA.water.z0=5e-4; % roughness length surface [m] % JAN: value for summer / vegetation + %tsvd PARA.water.z0=1e-4; % roughness length surface [m] - gets overridden by value calculated by function flake_roughnessLength.m version Flake + PARA.water.extinction=1.2; % light extinction coefficient of water + PARA.water.depth=0.; + PARA.water.fetch=20; + + PARA.ice.albedo =0.20; % albedo ice / Lei et al. (2011) shows a range of 0.1 to 0.35 + PARA.ice.epsilon=0.98; % surface emissivity snow + PARA.ice.rs=0.0; % surface resistance -> should be 0 for ice + PARA.ice.z0=5e-4; % roughness length surface [m] % JAN: value for snow + PARA.ice.extinction=4.5; % [m^-1] light extinction coefficient of ice / Lei et al. (2011) shows a range of 1 to 5 m^-1 + + PARA.technical.z=2.0; % height of input air temperature above ground in [m] - assumed constant even when snow depth increases + PARA.technical.SWEperCell=0.005; % SWE per grid cell in [m] - determines size of snow grid cells + PARA.technical.maxSWE=0.4; % in [m] SWE + PARA.technical.arraySizeT=5002; % number of values in the look-up tables for conductivity and capacity + PARA.technical.starttime=datenum(2000, 6, 1); % starttime of the simulation - if empty start from first value of time series + %PARA.technical.endtime=datenum(1970, 6, 10); % endtime of the simulation - if empty end at last value of time series + PARA.technical.endtime = SETUP.endtime; % endtime of the simulation - if empty end at last value of time series + PARA.technical.minTimestep=0.1 ./ 3600 ./ 24; % smallest possible time step in [days] - here 0.1 seconds + PARA.technical.maxTimestep=300 ./ 3600 ./ 24; % largest possible time step in [days] - here 300 seconds + %tsvd PARA.technical.targetDeltaE=1e5; % maximum energy change of a grid cell between time steps in [J/m3] %1e5 corresponds to heating of pure water by 0.025 K + PARA.technical.targetDeltaE=1e6; % maximum energy change of a grid cell between time steps in [J/m3] %1e5 corresponds to heating of pure water by 0.025 K lll DeltaE increased by 1 order of M + PARA.technical.outputTimestep= 3 ./ 24.0 ; % output time step in [days] - here three hours +% PARA.technical.syncTimeStep = 12 ./ 24.0 ; % output time step in [days] - here three hours + PARA.technical.syncTimeStep = 3 ./ 24.0 ; % output time step in [days] - here three hours + PARA.technical.saveDate='01.01.'; % date of year when output file is written - no effect if "saveInterval" is empty + PARA.technical.saveInterval=[1]; % interval [years] in which output files are written - if empty the entire time series is written - minimum is 1 year + PARA.technical.waterCellSize=0.02; % default size of a newly added water cell when water ponds below water table [m] + + %default grid used for publications and testing of water balance: + %tsvd PARA.technical.subsurfaceGrid = [[0:0.02:4], [4.1:0.1:10], [10.2:0.2:20], [21:1:30], [35:5:50], [60:10:100], [200:100:1000]]'; % the subsurface K-grid in [m] + PARA.technical.subsurfaceGrid = [[0:0.04:4], [4.1:0.1:10], [10.2:0.2:20], [21:1:30], [35:5:50], [60:10:100], [200:100:1000]]'; % the subsurface K-grid in [m] + + PARA.location.area=1.0; + PARA.location.initial_altitude=0.0; + % JAN: the following quantities are dynamic and should hence be moved to another struct, e.g. "STATE" + PARA.location.altitude = PARA.location.initial_altitude; % used to generate pressure forcing based on barometric altitude formula, if pressure forcing is not given; excluding snow domain + PARA.location.soil_altitude=PARA.location.initial_altitude; + PARA.location.surface_altitude=PARA.location.initial_altitude; % this is dynamic and refers to the surface including snow + PARA.location.active_layer_depth_altitude = nan; % defined at runtime + PARA.location.water_table_altitude = nan; % defined at runtime + % thresholds + PARA.location.absolute_maxWater_altitude = PARA.location.altitude + PARA.soil.relative_maxWater; + if isempty( PARA.snow.relative_maxSnow ) + PARA.location.absolute_maxSnow_altitude = []; + else + PARA.location.absolute_maxSnow_altitude = [ PARA.location.altitude + PARA.snow.relative_maxSnow ]; + end +%tsvd + PARA.location.latitude = 72.376; % [deg] < - + PARA.location.longitude = 126.491; % [deg] < - + %PARA.location.GridCellSize = 1e6; % [m^2] < - + %PARA.location.TileFraction = 1e-2; + % parameters related to the site location + PARA.location.water_table_altitude = nan; % defined at runtime + + %initial temperature profile -> first column depth [m] -> second column temperature [degree C] + PARA.Tinitial = [ -2 5 ;... + 0 0 ;... + 2 -5 ;... + 10 -10 ;... + 25 -9 ;... + 100 -9 ;... + 2000 10 ]; + +% PARA.Tinitial = [ -2 5 ;... +% 0 0 ;... +% 2 -2 ;... +% 5 -7 ;... +% 10 -9 ;... +% 25 -9 ;... +% 100 -8 ;... +% 1100 10.2 ]; % the geothermal gradient for Qgeo=0.05W/m² and K=2.746W/Km is about 18.2 K/km + +% load natural constants (given in SI units) to PARA.constants + PARA = loadConstants( PARA ); + + %FORCING data mat-file + %PARA.forcing.filename='samoylov_ERA_obs_fitted_1979_2014_spinup.mat'; %must be in subfolder "forcing" and follow the conventions for CryoGrid 3 forcing files + PARA.forcing.filename='Samoylov_rcp85_1901_2300_CryoGrid_windModified.mat'; + %PARA.forcing.filename='CG3_CCLM_forcing_90_101'; + PARA.forcing.rain_fraction=1; + PARA.forcing.snow_fraction=1; + + % switches for modules + PARA.modules.infiltration=1; % true if infiltration into unfrozen ground occurs + PARA.modules.xice=0; % true if thaw subsicdence is enabled + PARA.modules.lateral=1; % true if adjacent realizations are run (this does not require actual lateral fluxes) +%tsvd extended for lateral switched off + if ~PARA.modules.lateral + PARA.modules.exchange_heat = 0; + PARA.modules.exchange_water = 0; + PARA.modules.exchange_snow = 0; + PARA.snow.maxSnow= [] ; % maximum snow depth that can be reached [m] - excess snow is removed in the model - if empty, no snow threshold + % in par mode this is replaced by PARA.ensemble.immobile_snow_height + elseif PARA.modules.lateral + % switches for lateral processes + PARA.modules.exchange_heat = 1; + PARA.modules.exchange_water = 0; %ttt + PARA.modules.exchange_snow = 0; %ttt + + %---------overwrites variables for each realization-------------------- + % this function must define everything that is realization-specific or dependent of all realizations + %tsvd PARA = get_parallel_variables( PARA ); + PARA = get_parallel_variables_batch( PARA,SETUP ); + end + + disp('Running experiment with xxxx -> indicate switches here') + % ------make output directory (name depends on parameters) ---------------- + saveDir = './runs'; + run_number= sprintf( 'WD%d_LD%d_LR%d_LF%d',[PARA.water.depth, SETUP.LD, SETUP.LR, 100*SETUP.LF] ) % water depth in m, lake coverage in % + + mkdir([ saveDir '/' run_number]); + + %tsvd ------redirect command line output to logfile --------------------------- + % if createLogFile +% diary(['./' run_number '/' run_number '_log.mat']); +% diary([ saveDir '/' run_number '/' run_number '_log.mat']); + % end + + %-------------------------------------------------------------------------- + %-----------do not modify from here onwards-------------------------------- + %-------------------------------------------------------------------------- + [FORCING, success]=load_forcing_from_file(PARA); % load FORCING mat-file + + if ~success + warning('A problem with the Forcing occured.'); + end + + PARA = initializeParameters(PARA, FORCING); %set start time, etc. + + %----------------create and initialize the grids -------------------------- + GRID=makeGrids(PARA); %create all grids + GRID=createStratigraphy(PARA,GRID); %interpolate input stratigraphy to the soil grid (GRID.soil.*) + + %----- initializie excess ground ice -------------------------------------- + [GRID,PARA] = initializeExcessIce2(GRID,PARA); + + %----- initializie soil thermal properties -------------------------------- + GRID = initializeSoilThermalProperties(GRID, PARA); + + %------ initializie snow properties---------------------------------------- + GRID = initializeSnow(GRID); + + %---- initialize the surface energy balance struct ------------------------ + SEB = initializeSEB(); +%tsvd replace by new call lake scheme +% %---- initialize the water body module ------------------------------------ +% GRID = initializeLAKE(GRID); + +%---- initialize the lake scheme structs ---------------------------------- + [FLAKE GRID] = initializeLAKE(GRID, PARA); + %---- initialize temperature profile -------------------------------------- + T = inititializeTemperatureProfile_simple(GRID, PARA, FORCING); + + %---- modification for infiltration + wc=GRID.soil.cT_water; + GRID.soil.E_lb = find(PARA.soil.evaporationDepth==GRID.soil.soilGrid(:,1))-1; + GRID.soil.T_lb= find(PARA.soil.rootDepth==GRID.soil.soilGrid(:,1))-1; + + %---- preallocate temporary arrays for capacity and conductivity----------- + [c_cTgrid, k_cTgrid, k_Kgrid, lwc_cTgrid] = initializeConductivityCapacity(T,wc, GRID, PARA); % this is basically the same as "getThermalProperties" during integration, but without interpolation to K grid + + %---- energy and water balance initialization ----------------------------- + BALANCE = initializeBALANCE(T, wc, c_cTgrid, lwc_cTgrid, GRID, PARA); + + %---- temporary arrays for storage of lateral fluxes --> these could go into a LATERAL struct or TEMPORARY? +%nnn comment out for single mode... + water_fluxes = zeros( numlabs, 1 ); % total water flux in [m/s] from each worker to worker index + snow_fluxes = zeros( numlabs, 1 ); % total snow flux in [m SWE] per output interval from each worker to worker index + heat_fluxes = zeros( numlabs, 1); % total heat flux in [J] per output interval of all workers to worker index + dE_dt_lateral = zeros( length(GRID.general.cT_grid), 1); % cell-wise heat flux in [W/m^3]? of all workers to worker index + dE_dt_lateral_j_cor = zeros( length(GRID.general.cT_grid), 1); % cell-wise heat flux in [W/m^3]? of all workers to worker index, corrected for interp bias + xxx = zeros( length(GRID.general.cT_grid), 1); % cell-wise heat flux in [W/m^3]? of all workers to worker index, corrected for interp bias + + %__________________________________________________________________________ + %-------- provide arrays for data storage --------------------------------- + [t, TEMPORARY] = generateTemporary(T, PARA); + OUT = generateOUT(); + + disp('initialization successful'); + %iSaveSettings( [ saveDir '/' run_number '/' run_number '_realization' num2str(index) '_settings.mat'] , FORCING, PARA, GRID) %tsvd non-interpolated forcing data saved! + iSaveSettings( [ saveDir '/' run_number '/' run_number '_settings.mat'] , FORCING, PARA, GRID) %tsvd non-interpolated forcing data saved! + + %% ________________________________________________________________________ + % Time Integration Routine I + % I + %_________________________________________________________________________I + + while t 0.5 * min( GRID.general.K_delta.^2 .* c_cTgrid ./ k_cTgrid ./ (GRID.soil.cT_domain + GRID.snow.cT_domain) ) ./ (24.*3600) + warning( 'numerical stability not guaranteed' ); + end + + %------ update T array ------------------------------------------------ + % account for vertical heat fluxes from ground heat flux and heat conduction + T = T + SEB.dE_dt./c_cTgrid./GRID.general.K_delta.*timestep.*24.*3600; + % account for lateral heat fluxes + T = T + dE_dt_lateral./c_cTgrid.*timestep.*24.*3600; % no division by K_delta necessary as dE_dt_lateral in [ J / m^3 / s ] + % set grid cells in air to air temperature + T(GRID.air.cT_domain)=FORCING.i.Tair; + + if max((T<-100))==1; disp('dwd'); end + + %------- water body module -------------------------------------------- +%tsvd T = mixingWaterBody(T, GRID); % zzz check whether this is ok to switch off + %------- snow cover module -------------------------------------------- + [T, GRID, PARA, SEB, BALANCE] = CryoGridSnow(T, GRID, FORCING, SEB, PARA, c_cTgrid, timestep, BALANCE); + [GRID, T, BALANCE] = updateGRID_snow(T, GRID, PARA, BALANCE); + + %------- infiltration module------------------------------------------- + if PARA.modules.infiltration + lateral_water_flux = nansum( water_fluxes ); % lateral fluxes to/from other workers in [m/s] + [wc, GRID, BALANCE] = CryoGridInfiltration(T, wc, dwc_dt, timestep, GRID, PARA, FORCING, BALANCE, lateral_water_flux); + end +%tsvd + %------- water body module -------------------------------------------- + if timestep>0 && ( ~isempty(GRID.lake.water.cT_domain_ub) || ~isempty(GRID.lake.ice.cT_domain_ub) ) + %ice cover + [T GRID FLAKE] = cryoGridIceCover(GRID, SEB, FLAKE, T, c_cTgrid, timestep); + %FLAKE + if isempty(GRID.lake.ice.cT_domain_ub) && sum(GRID.lake.water.cT_domain)>=3 + %FLAKE is used if no ice cells exist + [T GRID FLAKE] = cryoGridFLAKE(FLAKE, GRID, SEB, PARA, T, T_old, timestep, k_Kgrid, SEB.dE_dt, SEB.dE_dt_SEB); + else + %updated FLAKE variables also when FLAKE is not used + FLAKE.t_bot_n_flk = T(GRID.lake.water.cT_domain_lb) + 273.15; + %set mixed layerdepth to zero + FLAKE.h_ml_n_flk = 0; + end + GRID = updateGRID_flake(GRID); + end + %--------- buoyancy calculation in block fields------------------------ + %T = convectionInBlocks(T, c_cTgrid, GRID) %not available + % @ ALEX: here the calculations of the excess ice module take place + % (uncomment this line to "activate" the module). Note that the + % stratigraphy must be modified such that excess ground ice is present + % in order to make the module "work" + + %------- excess ice module -------------------------------------------- + if PARA.modules.xice && ~PARA.modules.infiltration + warning( 'energy and water balances are not correct for this combination of modules'); % zzz ... + [GRID, PARA] = excessGroundIce(T, GRID, PARA); +%tsvd version Flake [GRID, PARA, wc] = excessGroundIce(T, GRID, PARA); + % assure wc has correct length + wc = wc( end-sum(GRID.soil.cT_domain)+1 : end ); +%tsvd wc( end-sum(GRID.soil.cT_domain)+1 : end ); % make vector dims consistent + elseif PARA.modules.xice && PARA.modules.infiltration + [GRID, PARA, wc, meltwaterGroundIce] = excessGroundIceInfiltration(T, wc, GRID, PARA); +%tsvd [GRID, PARA, wc] = excessGroundIceInfiltration(T, wc, GRID, PARA); + GRID = updateGRID_excessiceInfiltration2(meltwaterGroundIce, GRID); + end + + %------- update Lstar for next time step ------------------------------ + SEB = L_star(FORCING, PARA, SEB); + + %------- update auxiliary state variables + PARA.location.altitude = getAltitude( PARA, GRID ); + PARA.location.surface_altitude = getSurfaceAltitude( PARA, GRID ); + PARA.location.active_layer_depth_altitude = getActiveLayerDepthAltitude( PARA, GRID, T ); + PARA.location.water_table_altitude = getWaterTableAltitude(T, wc, GRID, PARA); + + %------- update threshold variables if no lateral exchange processes occur, otherwise updated at sync time + if ~PARA.modules.lateral + PARA.location.absolute_maxWater_altitude = PARA.location.altitude + PARA.soil.relative_maxWater; + if isempty( PARA.snow.maxSnow ) + %tsvd if isempty( PARA.ensemble.immobile_snow_height ) + PARA.location.absolute_maxSnow_altitude = []; + else + PARA.location.absolute_maxSnow_altitude = [ PARA.ensemble.altitude + PARA.snow.relative_maxSnow ]; + end + end + %------- water balance calculations ----------------------------------- + % rainfall + BALANCE.water.dp_rain = BALANCE.water.dp_rain + FORCING.i.rainfall.*timestep; %sum up rainfall in [mm] per output interval + % snowfall + BALANCE.water.dp_snow = BALANCE.water.dp_snow + FORCING.i.snowfall.*timestep; %sum up snowfall in [mm] SWE per output interval + + %------- lateral exchange module -------------------------------------- + % all functions called in this block should go into /modules/cryoGridLateral + % calling PARA.ensemble is only allowed here + if PARA.modules.lateral + if t==TEMPORARY.syncTime %communication between workers + disp('CryoGridLateral: sync - start'); + labBarrier(); %common start + PARA = updateAuxiliaryVariablesAndCommonThresholds(T, wc, GRID, PARA) ; + + % heat exchange module + if PARA.modules.exchange_heat + labBarrier(); + heat_fluxes = zeros( number_of_realizations, 1); + % check preconditions + precondition_heatExchange = true; %no specific conditions so far + if precondition_heatExchange + %WRAPPER + disp('sync - exchanging heat'); + % calculate lateral heat fluxes + dE_dt_lateral = zeros( length(GRID.general.cT_grid), 1); %in [J/m^3/s] + % zzz not needed!? dE_dt_lateral_j_cor = zeros( length(GRID.general.cT_grid), 1); %in [J/m^3/s] + % xxx = zeros( length(GRID.general.cT_grid), 1); %in [J/m^3/s] + PACKAGE_heatExchange.T = T; + PACKAGE_heatExchange.cT_grid = GRID.general.cT_grid; + PACKAGE_heatExchange.k_cTgrid = k_cTgrid; + for j=1:number_of_realizations + if j~=index + labSend( PACKAGE_heatExchange, j, 1); + end + end + for j=1:number_of_realizations + if j~=index + PACKAGE_heatExchange_j = labReceive(j, 1); + [dE_dt_lateral_j, BALANCE] = calculateLateralHeatFluxes(T, k_cTgrid, PACKAGE_heatExchange_j,GRID, PARA, BALANCE, j); % contribution from worker j to worker index + % now down after correction heat_fluxes(j) = nansum( dE_dt_lateral_j .* GRID.general.K_delta ); % in [J / m^2 s ], only for tracking the overall heat fluxes + % dE_dt_lateral = dE_dt_lateral + dE_dt_lateral_j; % sum up contributions from all realizations + end + end +%tsvd correct for biases in dE_dt_lateral (due to interpolation?), by using mean value for pairwise heat exchange + labBarrier(); + for j=1:number_of_realizations + if j~=index + %yyy2=dE_dt_lateral_j + labSend( nansum(dE_dt_lateral_j), j, 2); + end + end + for j=1:number_of_realizations + if j~=index + xxx = labReceive(j, 2); %ttt rm xxx yyy .... if not needed... + %dE_dt_lateral_j_cor = -xxx/nansum(dE_dt_lateral_j) * dE_dt_lateral_j; % scale...xxx (fluxes have opposite sign) + dE_dt_lateral_j_cor = (-xxx+nansum(dE_dt_lateral_j))/(2*nansum(dE_dt_lateral_j)) * dE_dt_lateral_j; % scale dE_dt lateral to mean value between workers (fluxes have opposite sign) + heat_fluxes(j) = nansum( dE_dt_lateral_j_cor .* GRID.general.K_delta ); % in [J / m^2 s ], only for tracking the overall heat fluxes +% % dE_dt_lateral = dE_dt_lateral + dE_dt_lateral_j; % sum up contributions from all realizations + % dE_dt_lateral_cor = dE_dt_lateral_cor + dE_dt_lateral_j_cor; % sum up contributions from all realizations - here on two workers... + dE_dt_lateral = dE_dt_lateral_j_cor; % if more than two workers, need to sum up here.... xxx zzz + end + end + end + end + + % water exchange module + if PARA.modules.exchange_water + labBarrier(); + water_fluxes = zeros( number_of_realizations, 1 ); % in [m/s] + % check preconditions + precondition_waterExchange = checkPreconditionWaterExchange( T, GRID ); + if precondition_waterExchange + % WRAPPER + disp('sync - exchanging water'); + % calculate lateral water fluxes + PACKAGE_waterExchange.water_table_altitude = PARA.ensemble.water_table_altitude(index); + PACKAGE_waterExchange.active_layer_depth_altitude = PARA.ensemble.active_layer_depth_altitude(index); + PACKAGE_waterExchange.infiltration_condition = T(GRID.soil.cT_domain_ub)>0 && isempty(GRID.snow.cT_domain_ub); %jjj zzz + for j=1:number_of_realizations + if j~=index + labSend( PACKAGE_waterExchange, j, 3); + end + end + for j=1:number_of_realizations + if j~=index + PACKAGE_waterExchange_j = labReceive(j, 3); + % JAN: for now: assume DarcyFlux and distribute over sync time step (no check for available water, missing water tracked in BALANCE.dm_lacking) + water_fluxes(j) = calculateLateralWaterFluxes( T, PACKAGE_waterExchange_j, GRID, PARA, j); + end + end + % for debugging: print water flux per column + waterflux = nansum( water_fluxes.*PARA.technical.syncTimeStep.*24.*3600 ); % in m + fprintf('Water flux to worker %d = %f mm \n', [index, waterflux*1000] ); + end + + end + + % snow exchange module + if PARA.modules.exchange_snow + labBarrier(); + % check preconditions + precondition_snowExchange = checkPreconditionSnowExchange( GRID, PARA ); + if precondition_snowExchange + disp('sync - exchanging snow'); + % calculate terrain index with updated surface_altitudes + PARA.ensemble.terrain_index_snow = calculateTerrainIndexSnow(PARA.ensemble.surface_altitude, PARA.ensemble.weight); + + % calculate mobile snow + % WRAPPER + mobile_snow = zeros( 1, number_of_realizations ); + my_mobile_snow = 0; + meltingConditions_index = sum(GRID.snow.Snow_w)>0 || sum(T(GRID.snow.cT_domain)>0)>0; %snow is assumed to be immobile under melting conditions CHECK WITH SNOW MODULE + if ~isempty(GRID.snow.cT_domain_ub) && ~meltingConditions_index % current realization has snow cover and no melting conditions + i=0; + while (abs( GRID.general.K_grid(GRID.snow.cT_domain_ub+i)-GRID.general.K_grid(GRID.snow.cT_domain_lb+1) )... % snow only mobile above realization-specific threshold + > PARA.ensemble.immobile_snow_height(index)+GRID.general.K_delta(GRID.snow.cT_domain_ub)) ... % if upper cell is drifted away, immobile snow height remains + && (PARA.ensemble.initial_altitude(index)-GRID.general.K_grid(GRID.snow.cT_domain_ub+i) ... % snow only mobile above lowermost surface altitude + snowCellSize (to prevent oscillations) + - (min( PARA.ensemble.surface_altitude )+GRID.snow.snowCellSize) > 1e-6 ) + my_mobile_snow = my_mobile_snow + (GRID.snow.Snow_i(GRID.snow.cT_domain_ub+i)); %only "ice" mobile + i=i+1; + end + end + mobile_snow(index) = my_mobile_snow; + % exchange mobile snow amounts + for j=1:number_of_realizations + if j~=index + % send mobile snow amount in [m SWE] + labSend( mobile_snow(index), j, 4 ); + end + end + for j=1:number_of_realizations + if j~=index + % receive mobile snow amount [m SWE] + mobile_snow(j) = labReceive(j, 4); + end + end + % calculate lateral snow fluxes + my_snow_change = calculateLateralSnowFluxes2( mobile_snow, PARA ); + % apply lateral snow fluxes directly + if my_snow_change ~= 0 + [T, GRID] = applyLateralSnowFluxes( T, PARA, GRID, FORCING, my_snow_change ); + [GRID, T, BALANCE] = updateGRID_snow(T, GRID, PARA, BALANCE); + BALANCE.water.dr_lateralSnow = BALANCE.water.dr_lateralSnow + my_snow_change ; + end + + snow_fluxes = zeros( numlabs , 1 ); + snow_fluxes(index) = my_snow_change ./ (PARA.technical.syncTimeStep.*3600.*24); %this is only to have comparable output to other fluxes + fprintf('Snow flux to worker %d = %f mm SWE \n', [index, my_snow_change*1000] ); + + end + end + + labBarrier(); + PARA = updateAuxiliaryVariablesAndCommonThresholds(T, wc, GRID, PARA) ; + + TEMPORARY.syncTime=round((TEMPORARY.syncTime + PARA.technical.syncTimeStep)./PARA.technical.syncTimeStep).*PARA.technical.syncTimeStep; + disp('sync - done'); + end + end + + + %------- next time step ----------------------------------------------- + t=t+timestep; + %---------- sum up + OUTPUT ------------------------------------------- + %nnn + [TEMPORARY, OUT, BALANCE] = sum_up_output_store(t, T, wc, lwc_cTgrid(GRID.soil.cT_domain), timestep, TEMPORARY, BALANCE, PARA, GRID, SEB, OUT, saveDir, run_number, water_fluxes, snow_fluxes, heat_fluxes); + end + + % save final state and output at t=endtime +%iSaveOUT( [ saveDir '/' run_number '/' run_number '_realization' num2str(index) '_output' datestr(t,'yyyy') '.mat'], OUT) +%iSaveState( [ saveDir '/' run_number '/' run_number '_realization' num2str(index) '_finalState' datestr(t,'yyyy') '.mat'], T, wc, t, SEB, PARA, GRID) + +%iSaveOUT( [ saveDir '/' run_number '/' run_number '_output' datestr(t,'yyyy') '.mat'], OUT) +%iSaveState( [ saveDir '/' run_number '/' run_number '_finalState' datestr(t,'yyyy') '.mat'], T, wc, t, SEB, PARA, GRID) + +%iPlotAltitudes( [ saveDir '/' run_number '/' run_number '_realization' num2str(index) '_altitudes_vs_time_' datestr(t,'yyyy') '.png'], OUT, PARA ); +end + +if number_of_realizations>1 + delete(gcp('nocreate')) +end +%tsvd +%dsave([ saveDir '/' run_number '/' run_number '_Workspace']) % save all distributed variables + +disp('Done.'); + diff --git a/CryoGrid3_xice_mpi.m b/CryoGrid3_xice_mpi.m new file mode 100755 index 0000000..05ff2d4 --- /dev/null +++ b/CryoGrid3_xice_mpi.m @@ -0,0 +1,579 @@ +% ------------------------------------------------------------------------- +% CryoGRID3 +% main script for running the model +% +% Developed by: S. Westermann and M. Langer 2015 + +% +% Extended by J. Nitzborn (infiltration of soils, lateral exchange of heat, water, snow) + +% Extended by T. Schneider von Deimling (coupling with FLAKE (based on version M. Langer) +% ---------------------------------------------------------------------------------------- + +%clear all; close all; profile off + +% runs modes +debug_mode=0 % if set to 1, timestep = timestepMin for debugging (avoid of NaN for timestep calculation) +par_mode = 1; % parallel mode off/on + +if(par_mode==1) + delete(gcp('nocreate')) % useful to restart from a crash +end + +add_modules; %adds required modules + +number_of_realizations=2; % specify number of workers +if number_of_realizations>1 + parpool(number_of_realizations); +end + +spmd %zzz use function calls to calls below to enable debugging in par mode! + index=labindex; %number identifying the process; change this to e.g. 1 for single realization (non-parallel) run +%nnn index=1 + +%---------------define input parameters------------------------------------ + % here you provide the ground stratigraphy + % z w/i m o type porosity + + % default stratigraphy used in publication: + PARA.soil.layer_properties=[ 0.0 0.60 0.10 0.15 1 0.75;... + 0.15 0.65 0.3 0.05 2 0.65;... + 0.9 0.65 0.3 0.05 1 0.65;... + 9.0 0.30 0.70 0.00 1 0.30 ]; + % soil stratigraphy + % column 1: start depth of layer (first layer must start with 0) - each layer extends until the beginning of the next layer, the last layer extends until the end of the model domain + % column 2: volumetric water+ice content; column 3: volumetric mineral content; column 4: volumetric organic content; + % column 5: code for soil type: 1: sand, 2: silt + % column 6: natural porosity - should be the same as 1-mineral-organic if no ground subsidence/thermokarst occurs + + %------ model parameters -------------------------------------------------- + % parameters related to soil + PARA.soil.albedo=0.2; % albedo snow-free surface + PARA.soil.epsilon=0.97; % emissvity snow-free surface + PARA.soil.z0=1e-3; % roughness length [m] snow-free surface + PARA.soil.rs=50; % surface resistance against evapotransiration [m^-1] snow-free surface + PARA.soil.Qgeo=0.05; % geothermal heat flux [W/m2] + PARA.soil.kh_bedrock=3.0; % thermal conductivity of the mineral soil fraction [W/mK] + + % parameters related to hydrology scheme + PARA.soil.fieldCapacity=0.5; %water holding capacity of the soil - this must be adapted to fit the upperlost layers!! + PARA.soil.evaporationDepth=0.1; %depth to which evaporation occurs - place on grid cell boundaries + PARA.soil.rootDepth=0.2; %depth affected by transpiration - place on grid cell boundaries + PARA.soil.wiltingPoint=0.2; %point at which transpiration shuts off + PARA.soil.residualWC=0.05; %water always remaining in the soil, not accessible to evaporation + PARA.soil.ratioET=0.5; % 1: only transpiration; 0: only evaporation, values in between must be made dependent on LAI, etc. + %tsvd PARA.soil.externalWaterFlux=2e-3; %external water flux / drainage in [m/day] + PARA.soil.externalWaterFlux=0.; %external water flux / drainage in [m/day] + PARA.soil.convectiveDomain=[]; % soil domain where air convection due to buoyancy is possible -> start and end [m] - if empty no convection is possible + PARA.soil.mobileWaterDomain=[0 10.0]; % soil domain where water from excess ice melt is mobile -> start and end [m] - if empty water is not mobile + PARA.soil.relative_maxWater=0.; % depth at which a water table will form [m] - above excess water is removed, below it pools up jjj zzz + PARA.soil.hydraulic_conductivity = 1e-5; + PARA = loadSoilTypes( PARA ); + + % parameters related to snow + PARA.snow.max_albedo=0.85; % albedo of fresh snow + PARA.snow.min_albedo=0.5; % albedo of old snow + PARA.snow.epsilon=0.99; % surface emissivity snow + PARA.snow.z0=5e-4; % roughness length surface [m] + PARA.snow.rs=0.0; % surface resistance -> should be 0 for snow + PARA.snow.rho_snow=200.0; % density in [kg/m3] + PARA.snow.tau_1=86400.0; % time constants of snow albedo change (according to ECMWF reanalysis) [sec] + PARA.snow.tau_a=0.008; % [per day] + PARA.snow.tau_f=0.24; % [per day] + PARA.snow.relative_maxSnow= [0.1]; %ttt maximum snow depth that can be reached [m] - excess snow is removed in the model - if empty, no snow threshold + PARA.snow.extinction=25.0; % light extinction coefficient of snow + %tsvd add lake parameters + % parameters related to lake + PARA.water.albedo=0.05; % albedo water (parameterization after Wayne and Burt (1954) in surfaceCondition.m) + PARA.water.epsilon=0.99; % surface emissivity water + PARA.water.rs=0.; % surface resistance -> should be 0 for water + %tsvd + PARA.water.z0=5e-4; % roughness length surface [m] % JAN: value for summer / vegetation + %tsvd PARA.water.z0=1e-4; % roughness length surface [m] - gets overridden by value calculated by function flake_roughnessLength.m version Flake + PARA.water.extinction=1.2; % light extinction coefficient of water + PARA.water.depth=0.; + PARA.water.fetch=20; + + PARA.ice.albedo =0.20; % albedo ice / Lei et al. (2011) shows a range of 0.1 to 0.35 + PARA.ice.epsilon=0.98; % surface emissivity snow + PARA.ice.rs=0.0; % surface resistance -> should be 0 for ice + PARA.ice.z0=5e-4; % roughness length surface [m] % JAN: value for snow + PARA.ice.extinction=4.5; % [m^-1] light extinction coefficient of ice / Lei et al. (2011) shows a range of 1 to 5 m^-1 + + PARA.technical.z=2.0; % height of input air temperature above ground in [m] - assumed constant even when snow depth increases + PARA.technical.SWEperCell=0.005; % SWE per grid cell in [m] - determines size of snow grid cells + PARA.technical.maxSWE=0.4; % in [m] SWE + PARA.technical.arraySizeT=5002; % number of values in the look-up tables for conductivity and capacity + PARA.technical.starttime=datenum(1979, 7, 1); % starttime of the simulation - if empty start from first value of time series + PARA.technical.endtime=datenum(1980, 6, 30); % endtime of the simulation - if empty end at last value of time series + PARA.technical.minTimestep=0.1 ./ 3600 ./ 24; % smallest possible time step in [days] - here 0.1 seconds + PARA.technical.maxTimestep=300 ./ 3600 ./ 24; % largest possible time step in [days] - here 300 seconds + %tsvd PARA.technical.targetDeltaE=1e5; % maximum energy change of a grid cell between time steps in [J/m3] %1e5 corresponds to heating of pure water by 0.025 K + PARA.technical.targetDeltaE=1e6; % maximum energy change of a grid cell between time steps in [J/m3] %1e5 corresponds to heating of pure water by 0.025 K lll DeltaE increased by 1 order of M + PARA.technical.outputTimestep= 3 ./ 24.0 ; % output time step in [days] - here three hours +% PARA.technical.syncTimeStep = 12 ./ 24.0 ; % output time step in [days] - here three hours + PARA.technical.syncTimeStep = 3 ./ 24.0 ; % output time step in [days] - here three hours + PARA.technical.saveDate='01.01.'; % date of year when output file is written - no effect if "saveInterval" is empty + PARA.technical.saveInterval=[]; % interval [years] in which output files are written - if empty the entire time series is written - minimum is 1 year + PARA.technical.waterCellSize=0.02; % default size of a newly added water cell when water ponds below water table [m] + + %default grid used for publications and testing of water balance: + PARA.technical.subsurfaceGrid = [[0:0.02:4], [4.1:0.1:10], [10.2:0.2:20], [21:1:30], [35:5:50], [60:10:100], [200:100:1000]]'; % the subsurface K-grid in [m] + %PARA.technical.subsurfaceGrid = [[0:0.02:10], [10.1:0.1:20], [20.2:0.2:30], [31:1:40], [45:5:60], [70:10:100], [200:100:1000]]'; % the subsurface K-grid in [m] + + PARA.location.area=1.0; + PARA.location.initial_altitude=20.0; + % JAN: the following quantities are dynamic and should hence be moved to another struct, e.g. "STATE" + PARA.location.altitude = PARA.location.initial_altitude; % used to generate pressure forcing based on barometric altitude formula, if pressure forcing is not given; excluding snow domain + PARA.location.soil_altitude=PARA.location.initial_altitude; + PARA.location.surface_altitude=PARA.location.initial_altitude; % this is dynamic and refers to the surface including snow + PARA.location.active_layer_depth_altitude = nan; % defined at runtime + PARA.location.water_table_altitude = nan; % defined at runtime + % thresholds + PARA.location.absolute_maxWater_altitude = PARA.location.altitude + PARA.soil.relative_maxWater; + if isempty( PARA.snow.relative_maxSnow ) + PARA.location.absolute_maxSnow_altitude = []; + else + PARA.location.absolute_maxSnow_altitude = [ PARA.location.altitude + PARA.snow.relative_maxSnow ]; + end +%tsvd + PARA.location.latitude = 72.376; % [deg] < - + PARA.location.longitude = 126.491; % [deg] < - + %PARA.location.GridCellSize = 1e6; % [m^2] < - + %PARA.location.TileFraction = 1e-2; + % parameters related to the site location + PARA.location.water_table_altitude = nan; % defined at runtime + + %initial temperature profile -> first column depth [m] -> second column temperature [degree C] +% PARA.Tinitial = [ -2 5 ;... +% 0 0 ;... +% 2 -5 ;... +% 10 -10 ;... +% 25 -9 ;... +% 100 -9 ;... +% 2000 10 ]; + PARA.Tinitial = [ -2 5 ;... + 0 0 ;... + 2 -2 ;... + 5 -7 ;... + 10 -9 ;... + 25 -9 ;... + 100 -8 ;... + 1100 10.2 ]; % the geothermal gradient for Qgeo=0.05W/m² and K=2.746W/Km is about 18.2 K/km + +% load natural constants (given in SI units) to PARA.constants + PARA = loadConstants( PARA ); + + %FORCING data mat-file + %PARA.forcing.filename='samoylov_ERA_obs_fitted_1979_2014_spinup.mat'; %must be in subfolder "forcing" and follow the conventions for CryoGrid 3 forcing files + PARA.forcing.filename='Samoylov_rcp85_1901_2300_CryoGrid_windModified.mat'; + %PARA.forcing.filename='CG3_CCLM_forcing_90_101'; + PARA.forcing.rain_fraction=1; + PARA.forcing.snow_fraction=1; + + % switches for modules + PARA.modules.infiltration=1; % true if infiltration into unfrozen ground occurs + PARA.modules.xice=0; % true if thaw subsicdence is enabled + PARA.modules.lateral=1; % true if adjacent realizations are run (this does not require actual lateral fluxes) +%tsvd extended for lateral switched off + if ~PARA.modules.lateral + PARA.modules.exchange_heat = 0; + PARA.modules.exchange_water = 0; + PARA.modules.exchange_snow = 0; + PARA.snow.maxSnow= [] ; % maximum snow depth that can be reached [m] - excess snow is removed in the model - if empty, no snow threshold + % in par mode this is replaced by PARA.ensemble.immobile_snow_height + elseif PARA.modules.lateral + % switches for lateral processes + PARA.modules.exchange_heat = 1; + PARA.modules.exchange_water = 0; %ttt + PARA.modules.exchange_snow = 0; %ttt + + %---------overwrites variables for each realization-------------------- + % this function must define everything that is realization-specific or dependent of all realizations + PARA = get_parallel_variables( PARA ); + end + + disp('Running experiment with xxxx -> indicate switches here') + % ------make output directory (name depends on parameters) ---------------- + saveDir = './runs'; +% run_number = sprintf( [ 'Lake-MPI_' datestr( PARA.technical.starttime, 'yyyymm' ) '-' datestr(PARA.technical.endtime, 'yyyymm' ) , ... +% PARA.modules.exchange_heat, PARA.modules.exchange_water, PARA.modules.exchange_snow, PARA.forcing.rain_fraction, PARA.forcing.snow_fraction, index ] ) + %run_number= sprintf( 'SSW-NL_xH%d_xW%d_xS%d_infil%d_xice%d_rF%d_sF%d_i%d' , ... + + run_number= sprintf( 'TEST_xH%d_xW%d_xS%d_infil%d_xice%d_rF%d_sF%d_i%d' , ... + [PARA.modules.exchange_heat, PARA.modules.exchange_water, PARA.modules.exchange_snow, ... + PARA.modules.infiltration, PARA.modules.xice, PARA.forcing.rain_fraction, PARA.forcing.snow_fraction , index ] ) + + mkdir([ saveDir '/' run_number]); + + %tsvd ------redirect command line output to logfile --------------------------- + % if createLogFile +% diary(['./' run_number '/' run_number '_log.mat']); + diary([ saveDir '/' run_number '/' run_number '_log.mat']); + % end + + %-------------------------------------------------------------------------- + %-----------do not modify from here onwards-------------------------------- + %-------------------------------------------------------------------------- + [FORCING, success]=load_forcing_from_file(PARA); % load FORCING mat-file + + if ~success + warning('A problem with the Forcing occured.'); + end + + PARA = initializeParameters(PARA, FORCING); %set start time, etc. + + %----------------create and initialize the grids -------------------------- + GRID=makeGrids(PARA); %create all grids + GRID=createStratigraphy(PARA,GRID); %interpolate input stratigraphy to the soil grid (GRID.soil.*) + + %----- initializie excess ground ice -------------------------------------- + [GRID,PARA] = initializeExcessIce2(GRID,PARA); + + %----- initializie soil thermal properties -------------------------------- + GRID = initializeSoilThermalProperties(GRID, PARA); + + %------ initializie snow properties---------------------------------------- + GRID = initializeSnow(GRID); + + %---- initialize the surface energy balance struct ------------------------ + SEB = initializeSEB(); +%tsvd replace by new call lake scheme +% %---- initialize the water body module ------------------------------------ +% GRID = initializeLAKE(GRID); + +%---- initialize the lake scheme structs ---------------------------------- + [FLAKE GRID] = initializeLAKE(GRID, PARA); + %---- initialize temperature profile -------------------------------------- + T = inititializeTemperatureProfile_simple(GRID, PARA, FORCING); + + %---- modification for infiltration + wc=GRID.soil.cT_water; + GRID.soil.E_lb = find(PARA.soil.evaporationDepth==GRID.soil.soilGrid(:,1))-1; + GRID.soil.T_lb= find(PARA.soil.rootDepth==GRID.soil.soilGrid(:,1))-1; + + %---- preallocate temporary arrays for capacity and conductivity----------- + [c_cTgrid, k_cTgrid, k_Kgrid, lwc_cTgrid] = initializeConductivityCapacity(T,wc, GRID, PARA); % this is basically the same as "getThermalProperties" during integration, but without interpolation to K grid + + %---- energy and water balance initialization ----------------------------- + BALANCE = initializeBALANCE(T, wc, c_cTgrid, lwc_cTgrid, GRID, PARA); + + %---- temporary arrays for storage of lateral fluxes --> these could go into a LATERAL struct or TEMPORARY? +%nnn comment out for single mode... + water_fluxes = zeros( numlabs, 1 ); % total water flux in [m/s] from each worker to worker index + snow_fluxes = zeros( numlabs, 1 ); % total snow flux in [m SWE] per output interval from each worker to worker index + heat_fluxes = zeros( numlabs, 1); % total heat flux in [J] per output interval of all workers to worker index + dE_dt_lateral = zeros( length(GRID.general.cT_grid), 1); % cell-wise heat flux in [W/m^3]? of all workers to worker index + + %__________________________________________________________________________ + %-------- provide arrays for data storage --------------------------------- + [t, TEMPORARY] = generateTemporary(T, PARA); + OUT = generateOUT(); + + disp('initialization successful'); + %%% nnn + iSaveSettings( [ saveDir '/' run_number '/' run_number '_realization' num2str(index) '_settings.mat'] , FORCING, PARA, GRID) %tsvd non-interpolated forcing data saved! + + %% ________________________________________________________________________ + % Time Integration Routine I + % I + %_________________________________________________________________________I + + while t 0.5 * min( GRID.general.K_delta.^2 .* c_cTgrid ./ k_cTgrid ./ (GRID.soil.cT_domain + GRID.snow.cT_domain) ) ./ (24.*3600) + warning( 'numerical stability not guaranteed' ); + end + + %------ update T array ------------------------------------------------ + % account for vertical heat fluxes from ground heat flux and heat conduction + T = T + SEB.dE_dt./c_cTgrid./GRID.general.K_delta.*timestep.*24.*3600; + % account for lateral heat fluxes + T = T + dE_dt_lateral./c_cTgrid.*timestep.*24.*3600; % no division by K_delta necessary as dE_dt_lateral in [ J / m^3 / s ] + % set grid cells in air to air temperature + T(GRID.air.cT_domain)=FORCING.i.Tair; + + if max((T<-100))==1; disp('dwd'); end + + %------- water body module -------------------------------------------- +%tsvd T = mixingWaterBody(T, GRID); % zzz check whether this is ok to switch off + %------- snow cover module -------------------------------------------- + [T, GRID, PARA, SEB, BALANCE] = CryoGridSnow(T, GRID, FORCING, SEB, PARA, c_cTgrid, timestep, BALANCE); + [GRID, T, BALANCE] = updateGRID_snow(T, GRID, PARA, BALANCE); + + %------- infiltration module------------------------------------------- + if PARA.modules.infiltration + lateral_water_flux = nansum( water_fluxes ); % lateral fluxes to/from other workers in [m/s] + [wc, GRID, BALANCE] = CryoGridInfiltration(T, wc, dwc_dt, timestep, GRID, PARA, FORCING, BALANCE, lateral_water_flux); + end +%tsvd + %------- water body module -------------------------------------------- + if timestep>0 && ( ~isempty(GRID.lake.water.cT_domain_ub) || ~isempty(GRID.lake.ice.cT_domain_ub) ) + %ice cover + [T GRID FLAKE] = cryoGridIceCover(GRID, SEB, FLAKE, T, c_cTgrid, timestep); + %FLAKE + if isempty(GRID.lake.ice.cT_domain_ub) && sum(GRID.lake.water.cT_domain)>=3 + %FLAKE is used if no ice cells exist + [T GRID FLAKE] = cryoGridFLAKE(FLAKE, GRID, SEB, PARA, T, T_old, timestep, k_Kgrid, SEB.dE_dt, SEB.dE_dt_SEB); + else + %updated FLAKE variables also when FLAKE is not used + FLAKE.t_bot_n_flk = T(GRID.lake.water.cT_domain_lb) + 273.15; + %set mixed layerdepth to zero + FLAKE.h_ml_n_flk = 0; + end + GRID = updateGRID_flake(GRID); + end + %--------- buoyancy calculation in block fields------------------------ + %T = convectionInBlocks(T, c_cTgrid, GRID) %not available + % @ ALEX: here the calculations of the excess ice module take place + % (uncomment this line to "activate" the module). Note that the + % stratigraphy must be modified such that excess ground ice is present + % in order to make the module "work" + + %------- excess ice module -------------------------------------------- + if PARA.modules.xice && ~PARA.modules.infiltration + warning( 'energy and water balances are not correct for this combination of modules'); % zzz ... + [GRID, PARA] = excessGroundIce(T, GRID, PARA); +%tsvd version Flake [GRID, PARA, wc] = excessGroundIce(T, GRID, PARA); + % assure wc has correct length + wc = wc( end-sum(GRID.soil.cT_domain)+1 : end ); +%tsvd wc( end-sum(GRID.soil.cT_domain)+1 : end ); % make vector dims consistent + elseif PARA.modules.xice && PARA.modules.infiltration + [GRID, PARA, wc, meltwaterGroundIce] = excessGroundIceInfiltration(T, wc, GRID, PARA); +%tsvd [GRID, PARA, wc] = excessGroundIceInfiltration(T, wc, GRID, PARA); + GRID = updateGRID_excessiceInfiltration2(meltwaterGroundIce, GRID); + end + + %------- update Lstar for next time step ------------------------------ + SEB = L_star(FORCING, PARA, SEB); + + %------- update auxiliary state variables + PARA.location.altitude = getAltitude( PARA, GRID ); + PARA.location.surface_altitude = getSurfaceAltitude( PARA, GRID ); + PARA.location.active_layer_depth_altitude = getActiveLayerDepthAltitude( PARA, GRID, T ); + PARA.location.water_table_altitude = getWaterTableAltitude(T, wc, GRID, PARA); + + %------- update threshold variables if no lateral exchange processes occur, otherwise updated at sync time + if ~PARA.modules.lateral + PARA.location.absolute_maxWater_altitude = PARA.location.altitude + PARA.soil.relative_maxWater; + if isempty( PARA.snow.maxSnow ) + %tsvd if isempty( PARA.ensemble.immobile_snow_height ) + PARA.location.absolute_maxSnow_altitude = []; + else + PARA.location.absolute_maxSnow_altitude = [ PARA.ensemble.altitude + PARA.snow.relative_maxSnow ]; + end + end + %------- water balance calculations ----------------------------------- + % rainfall + BALANCE.water.dp_rain = BALANCE.water.dp_rain + FORCING.i.rainfall.*timestep; %sum up rainfall in [mm] per output interval + % snowfall + BALANCE.water.dp_snow = BALANCE.water.dp_snow + FORCING.i.snowfall.*timestep; %sum up snowfall in [mm] SWE per output interval + + %------- lateral exchange module -------------------------------------- + % all functions called in this block should go into + % /modules/cryoGridLateral + % calling PARA.ensemble is only allowed here + if PARA.modules.lateral + if t==TEMPORARY.syncTime %communication between workers + disp('CryoGridLateral: sync - start'); + labBarrier(); %common start + PARA = updateAuxiliaryVariablesAndCommonThresholds(T, wc, GRID, PARA) ; %ddd commenting out (here and below) does not help prevent snow grid crash + + % heat exchange module + if PARA.modules.exchange_heat + labBarrier(); + heat_fluxes = zeros( number_of_realizations, 1); + % check preconditions + precondition_heatExchange = true; %no specific conditions so far + if precondition_heatExchange + %WRAPPER + disp('sync - exchanging heat'); + % calculate lateral heat fluxes + dE_dt_lateral = zeros( length(GRID.general.cT_grid), 1); %in [J/m^3/s] + PACKAGE_heatExchange.T = T; + PACKAGE_heatExchange.cT_grid = GRID.general.cT_grid; + PACKAGE_heatExchange.k_cTgrid = k_cTgrid; + for j=1:number_of_realizations + if j~=index + labSend( PACKAGE_heatExchange, j, 1); + end + end + for j=1:number_of_realizations + if j~=index + PACKAGE_heatExchange_j = labReceive(j, 1); + [dE_dt_lateral_j, BALANCE] = calculateLateralHeatFluxes(T, k_cTgrid, PACKAGE_heatExchange_j,GRID, PARA, BALANCE, j); % contribution from worker j to worker index + heat_fluxes(j) = nansum( dE_dt_lateral_j .* GRID.general.K_delta ); % in [J / m^2 s ], only for tracking the overall heat fluxes + dE_dt_lateral = dE_dt_lateral + dE_dt_lateral_j; % sum up contributions from all realizations + end + end + end + end + + % water exchange module + if PARA.modules.exchange_water + labBarrier(); + water_fluxes = zeros( number_of_realizations, 1 ); % in [m/s] + % check preconditions + precondition_waterExchange = checkPreconditionWaterExchange( T, GRID ); + if precondition_waterExchange + % WRAPPER + disp('sync - exchanging water'); + % calculate lateral water fluxes + PACKAGE_waterExchange.water_table_altitude = PARA.ensemble.water_table_altitude(index); + PACKAGE_waterExchange.active_layer_depth_altitude = PARA.ensemble.active_layer_depth_altitude(index); + PACKAGE_waterExchange.infiltration_condition = T(GRID.soil.cT_domain_ub)>0 && isempty(GRID.snow.cT_domain_ub); %jjj zzz + for j=1:number_of_realizations + if j~=index + labSend( PACKAGE_waterExchange, j, 2); + end + end + for j=1:number_of_realizations + if j~=index + PACKAGE_waterExchange_j = labReceive(j, 2); + % JAN: for now: assume DarcyFlux and distribute over sync time step (no check for available water, missing water tracked in BALANCE.dm_lacking) + water_fluxes(j) = calculateLateralWaterFluxes( T, PACKAGE_waterExchange_j, GRID, PARA, j); + end + end + % for debugging: print water flux per column + waterflux = nansum( water_fluxes.*PARA.technical.syncTimeStep.*24.*3600 ); % in m + fprintf('Water flux to worker %d = %f mm \n', [index, waterflux*1000] ); + end + + end + + % snow exchange module + if PARA.modules.exchange_snow + labBarrier(); + % check preconditions + precondition_snowExchange = checkPreconditionSnowExchange( GRID, PARA ); + if precondition_snowExchange + disp('sync - exchanging snow'); + % calculate terrain index with updated surface_altitudes + PARA.ensemble.terrain_index_snow = calculateTerrainIndexSnow(PARA.ensemble.surface_altitude, PARA.ensemble.weight); + + % calculate mobile snow + % WRAPPER + mobile_snow = zeros( 1, number_of_realizations ); + my_mobile_snow = 0; + meltingConditions_index = sum(GRID.snow.Snow_w)>0 || sum(T(GRID.snow.cT_domain)>0)>0; %snow is assumed to be immobile under melting conditions CHECK WITH SNOW MODULE + if ~isempty(GRID.snow.cT_domain_ub) && ~meltingConditions_index % current realization has snow cover and no melting conditions + i=0; + while (abs( GRID.general.K_grid(GRID.snow.cT_domain_ub+i)-GRID.general.K_grid(GRID.snow.cT_domain_lb+1) )... % snow only mobile above realization-specific threshold + > PARA.ensemble.immobile_snow_height(index)+GRID.general.K_delta(GRID.snow.cT_domain_ub)) ... % if upper cell is drifted away, immobile snow height remains + && (PARA.ensemble.initial_altitude(index)-GRID.general.K_grid(GRID.snow.cT_domain_ub+i) ... % snow only mobile above lowermost surface altitude + snowCellSize (to prevent oscillations) + - (min( PARA.ensemble.surface_altitude )+GRID.snow.snowCellSize) > 1e-6 ) + my_mobile_snow = my_mobile_snow + (GRID.snow.Snow_i(GRID.snow.cT_domain_ub+i)); %only "ice" mobile + i=i+1; + end + end + mobile_snow(index) = my_mobile_snow; + % exchange mobile snow amounts + for j=1:number_of_realizations + if j~=index + % send mobile snow amount in [m SWE] + labSend( mobile_snow(index), j, 4 ); + end + end + for j=1:number_of_realizations + if j~=index + % receive mobile snow amount [m SWE] + mobile_snow(j) = labReceive(j, 4); + end + end + % calculate lateral snow fluxes + my_snow_change = calculateLateralSnowFluxes2( mobile_snow, PARA ); + % apply lateral snow fluxes directly + if my_snow_change ~= 0 + [T, GRID] = applyLateralSnowFluxes( T, PARA, GRID, FORCING, my_snow_change ); + [GRID, T, BALANCE] = updateGRID_snow(T, GRID, PARA, BALANCE); + BALANCE.water.dr_lateralSnow = BALANCE.water.dr_lateralSnow + my_snow_change ; + end + + snow_fluxes = zeros( numlabs , 1 ); + snow_fluxes(index) = my_snow_change ./ (PARA.technical.syncTimeStep.*3600.*24); %this is only to have comparable output to other fluxes + fprintf('Snow flux to worker %d = %f mm SWE \n', [index, my_snow_change*1000] ); + + end + end + + labBarrier(); + PARA = updateAuxiliaryVariablesAndCommonThresholds(T, wc, GRID, PARA) ; + + TEMPORARY.syncTime=round((TEMPORARY.syncTime + PARA.technical.syncTimeStep)./PARA.technical.syncTimeStep).*PARA.technical.syncTimeStep; + disp('sync - done'); + end + end + + + %------- next time step ----------------------------------------------- + t=t+timestep; + %---------- sum up + OUTPUT ------------------------------------------- + %nnn + [TEMPORARY, OUT, BALANCE] = sum_up_output_store(t, T, wc, lwc_cTgrid(GRID.soil.cT_domain), timestep, TEMPORARY, BALANCE, PARA, GRID, SEB, OUT, saveDir, run_number, water_fluxes, snow_fluxes, heat_fluxes); + end + + % save final state and output at t=endtime +iSaveOUT( [ saveDir '/' run_number '/' run_number '_realization' num2str(index) '_output' datestr(t,'yyyy') '.mat'], OUT) +iSaveState( [ saveDir '/' run_number '/' run_number '_realization' num2str(index) '_finalState' datestr(t,'yyyy') '.mat'], T, wc, t, SEB, PARA, GRID) +%iPlotAltitudes( [ saveDir '/' run_number '/' run_number '_realization' num2str(index) '_altitudes_vs_time_' datestr(t,'yyyy') '.png'], OUT, PARA ); +end + +if number_of_realizations>1 + delete(gcp('nocreate')) +end + +disp('Done.'); + diff --git a/README.md b/README.md index bdb6e63..ce6f878 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,2 @@ # CryoGrid3 CryoGrid 3 is a simple land-surface scheme dedicated to modeling of ground temperatures in permafrost environments. -Test -test jan diff --git a/add_modules.m b/add_modules.m index 09a0800..e1b45c5 100644 --- a/add_modules.m +++ b/add_modules.m @@ -1,10 +1,12 @@ % %clear all -if ~paraFromFile - clearvars -except paraFromFile %added by JAN to enable running with or without config file from same script -end -close all -profile off -dbclear if error +% if ~paraFromFile +% clearvars -except paraFromFile %added by JAN to enable running with or without config file from same script +% end + +%clear all +%close all + +%profile off %import CryoGrid modules (matlab functions) @@ -13,4 +15,10 @@ addpath('modules/cryoGridSEB/') addpath('modules/cryoGridSoil/') addpath('modules/cryoGridSnow/') -addpath('modules/CryoGridInfiltrationUnfrozenSoil') +addpath('modules/CryoGridInfiltrationUnfrozenSoil/') +%tsvd new modules added +addpath('modules/cryoGridExcessIce/') +addpath('modules/cryoGridExcessIceInfiltration') +addpath('modules/cryoGridLateral') +%tsvd +addpath('modules/cryoGridLake/') \ No newline at end of file diff --git a/analysis/Tprofiles_04.m b/analysis/Tprofiles_04.m new file mode 100644 index 0000000..c38e0da --- /dev/null +++ b/analysis/Tprofiles_04.m @@ -0,0 +1,67 @@ +% T_NL and T_L profiles + +%filespec='MSW_xH1_xW0_xS0_infil1_xice0_rF1_sF1'; +%filespec_b='MSW_xH0_xW0_xS0_infil1_xice0_rF1_sF1'; % no lateral heat exchange +filespec='xH1'; +filespec_b='xH0'; +yearspec='1971'; + +Tini_ssw=[0 1 2 10 20 100 2000; 10 -3 -4 -7 -10 -10 10; 6 6 0 -5 -10 -10 10]; +Tini_msw=[0 5 8 20 100 2000; 10 -3 -6 -10 -10 10; 5 5 0 -9 -10 10]; +Tini=Tini_ssw'; + +%% Non_Lake +% with lateral exchange +%outputfile1 = ['../runs/',filespec,'_i1/',filespec,'_i1_realization1_output',yearspec]; configfile1 = ['../runs/',filespec,'_i1/',filespec,'_i1_settings']; +outputfile1 = ['../runs/WD0_',filespec,'/WD0_',filespec,'_output',yearspec]; configfile1 = ['../runs/WD0_',filespec,'/WD0_',filespec,'_settings']; +load(outputfile1); load(configfile1); +Soil_NL_ub = GRID.soil.cT_domain_ub; Soil_NL_lb = GRID.soil.cT_domain_lb; +zgeneral_NL=GRID.general.cT_grid; zsoil_NL = GRID.general.cT_grid(Soil_NL_ub:Soil_NL_lb); +[~, z10m_NL]=min(abs(zgeneral_NL-10)); [~, z15m_NL]=min(abs(zgeneral_NL-15)); [~, z20m_NL]=min(abs(zgeneral_NL-20)); + +T_NL=OUT.cryoGrid3; T_NLtm = mean(T_NL,2); + +% wo lateral exchange +%outputfile1b = ['../runs/noLateral/runs/',filespec_b,'_i1/',filespec_b,'_i1_realization1_output',yearspec]; configfile1b = ['../runs/noLateral/runs/',filespec_b,'_i1/',filespec_b,'_i1_settings']; +outputfile1b = ['../runs/noLateral/runs/WD0_',filespec_b,'/WD0_',filespec_b,'_output',yearspec]; configfile1b = ['../runs/noLateral/runs/WD0_',filespec_b,'/WD0_',filespec_b,'_settings']; +load(outputfile1b); load(configfile1b); + +T_NL_xH0=OUT.cryoGrid3; T_NLtm_xH0 = mean(T_NL_xH0,2); % no lateral heat exchange + +%% LAKE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%outputfile2 = ['../runs/',filespec,'_i2/',filespec,'_i2_realization2_output',yearspec]; configfile2 = ['../runs/',filespec,'_i2/',filespec,'_i2_settings']; +outputfile2 = ['../runs/WD1_',filespec,'/WD1_',filespec,'_output',yearspec]; configfile2 = ['../runs/WD1_',filespec,'/WD1_',filespec,'_settings']; +load(outputfile2); load(configfile2) + +n_LakeLayers=nansum(GRID.lake.water.cT_domain)+nansum(GRID.lake.ice.cT_domain); +Lake_ub = GRID.soil.cT_domain_ub-n_LakeLayers; Lake_lb = GRID.soil.cT_domain_ub-1; +Soil_L_ub = GRID.soil.cT_domain_ub; +zgeneral_L=GRID.general.cT_grid; +zsoil = GRID.general.cT_grid(GRID.soil.cT_domain_ub:GRID.soil.cT_domain_lb); +[~, z10m_L]=min(abs(zgeneral_L-10)); [~, z15m_L]=min(abs(zgeneral_L-15)); [~, z20m_L]=min(abs(zgeneral_L-20)); + +T_L=OUT.cryoGrid3; T_Ltm = mean(T_L,2); + +% wo lateral exchange +outputfile2b = ['../runs/noLateral/runs/WD1_',filespec_b,'/WD1_',filespec_b,'_output',yearspec]; configfile2b = ['../runs/noLateral/runs/WD1_',filespec_b,'/WD1_',filespec_b,'_settings']; +load(outputfile2b); load(configfile2b); + +T_L_xH0=OUT.cryoGrid3; T_Ltm_xH0 = mean(T_L_xH0,2); + +%% +ti=100; +figure + subplot(1,2,1) +plot(T_L(Lake_ub:z20m_L,ti),zgeneral_L(Lake_ub:z20m_L),'b',T_NL(Soil_NL_ub:z20m_NL,ti),zgeneral_NL(Soil_NL_ub:z20m_NL),'g', ... + T_L_xH0(Lake_ub:z20m_L,ti),zgeneral_L(Lake_ub:z20m_L),'b--',T_NL_xH0(Soil_NL_ub:z20m_NL,ti),zgeneral_NL(Soil_NL_ub:z20m_NL),'g--') +hold on; plot(Tini(1:5,2),Tini(1:5,1),'g*',Tini(1:5,3),Tini(1:5,1),'b*'); hold off + legend('L','NL','Location','SouthWest'); title(['T(z) at ti ',num2str(filespec(1:3)),' ',num2str(yearspec)]) +set(gca,'Ydir','reverse'); xlabel('T (°C)'); ylabel('z (m)'); grid on +axis([-30 10 -inf inf]) + subplot(1,2,2) +plot(T_Ltm(Lake_ub:z20m_L),zgeneral_L(Lake_ub:z20m_L),'b',T_NLtm(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'g', ... + T_Ltm_xH0(Lake_ub:z20m_L),zgeneral_L(Lake_ub:z20m_L),'b--',T_NLtm_xH0(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'g--') +hold on; plot(Tini(1:5,2),Tini(1:5,1),'g*',Tini(1:5,3),Tini(1:5,1),'b*'); hold off +legend('L','NL','Location','SouthWest'); title(['T(z) annual mean ',num2str(filespec(1:3)),' ',num2str(yearspec)]) +set(gca,'Ydir','reverse'); xlabel('T (°C)'); ylabel('z (m)'); grid on +axis([-30 10 -inf inf]) diff --git a/analysis/analysis_Tz_12.m b/analysis/analysis_Tz_12.m new file mode 100644 index 0000000..6afe6af --- /dev/null +++ b/analysis/analysis_Tz_12.m @@ -0,0 +1,181 @@ +% T_NL and T_L profiles + +load_data=1; % load data which were generated on cluster + +if load_data == 1 % zzz ... update data on cluster.... + load Data_Tz +else + Tini_ssw=[0 1 2 10 20 100 2000; 10 -3 -4 -7 -10 -10 10; 6 6 0 -5 -10 -10 10]; + Tini_msw=[0 5 8 20 100 2000; 10 -3 -6 -10 -10 10; 5 5 0 -9 -10 10]; + Tini=Tini_ssw'; + + yearspec='2012'; + + LD=[1 5]; + LR=[10 100]; + LF=[10 25 50]; + + n=0; % run index loop + for i=1:length(LD) % loop over exps + for j=1:length(LR) + for k=1:length(LF) + n=n+1; + expspec_NL=['WD0_LD',num2str(LD(i)),'_LR',num2str(LR(j)),'_LF',num2str(LF(k))]; % experiment specifier non Lake + expspec_L=['WD',num2str(LD(i)),'_LD',num2str(LD(i)),'_LR',num2str(LR(j)),'_LF',num2str(LF(k))]; + % experiment specifier lake + % NL + outputfile_NL = ['../runs/',expspec_NL,'/',expspec_NL,'_',yearspec]; + outputfile_NL_nolat = ['../../CryoGrid3_noLateral/runs/',expspec_NL,'/',expspec_NL,'_',yearspec]; + configfile_NL = ['../runs/',expspec_NL,'/',expspec_NL,'_settings']; + % Lake + outputfile_L = ['../runs/',expspec_L,'/',expspec_L,'_',yearspec]; + outputfile_L_nolat = ['../../CryoGrid3_noLateral/runs/',expspec_L,'/',expspec_L,'_',yearspec]; + configfile_L = ['../runs/',expspec_L,'/',expspec_L,'_settings']; + % 1.) z levels + if(n==1) + load(configfile_NL); % NL + Soil_NL_ub = GRID.soil.cT_domain_ub; Soil_NL_lb = GRID.soil.cT_domain_lb; + zgeneral_NL=GRID.general.cT_grid; zsoil_NL = GRID.general.cT_grid(Soil_NL_ub:Soil_NL_lb); + [~, z20m_NL]=min(abs(zgeneral_NL-30)); + + load(configfile_L); % Lake 1m + n_LakeLayers=nansum(GRID.lake.water.cT_domain)+nansum(GRID.lake.ice.cT_domain); + Lake_1m_ub = GRID.soil.cT_domain_ub-n_LakeLayers; Lake_1m_lb = GRID.soil.cT_domain_ub-1; + zgeneral_L1m=GRID.general.cT_grid; + [~, z20m_L1m]=min(abs(zgeneral_L1m-30)); + elseif(n==7) + load(configfile_L); % Lake 5m + n_LakeLayers=nansum(GRID.lake.water.cT_domain)+nansum(GRID.lake.ice.cT_domain); + Lake_5m_ub = GRID.soil.cT_domain_ub-n_LakeLayers; Lake_5m_lb = GRID.soil.cT_domain_ub-1; + zgeneral_L5m=GRID.general.cT_grid; + [~, z20m_L5m]=min(abs(zgeneral_L5m-30)); + end + % 2.) generate T data + % non Lake + load(outputfile_NL); + T_NL=OUT.cryoGrid3; T_NLtm = mean(T_NL,2); + eval(['T_',expspec_NL,'=T_NLtm;']); + % no lateral heat flux + load(outputfile_NL_nolat); + T_NL=OUT.cryoGrid3; T_NLtm = mean(T_NL,2); + eval(['T_',expspec_NL,'_nolat=T_NLtm;']); + % Lake + load(outputfile_L); + T_L=OUT.cryoGrid3; T_Ltm = mean(T_L,2); + eval(['T_',expspec_L,'=T_Ltm;']); + % no lateral heat flux + load(outputfile_L_nolat); + T_L=OUT.cryoGrid3; T_Ltm = mean(T_L,2); + eval(['T_',expspec_L,'_nolat=T_Ltm;']); + end + end +end +save Data_Tz T* z* Lake_* yearspec expspec* Soil_NL_ub L* +end + +%% +figure(1) % LD 1m + subplot(1,2,1) +% SSW (LR 10m) +hold on +% LF 0.25 +plot(T_WD0_LD1_LR10_LF25(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'k',T_WD1_LD1_LR10_LF25(Lake_1m_ub:z20m_L1m),zgeneral_L1m(Lake_1m_ub:z20m_L1m),'b') +% LF 0.1 +plot(T_WD0_LD1_LR10_LF10(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'k:',T_WD1_LD1_LR10_LF10(Lake_1m_ub:z20m_L1m),zgeneral_L1m(Lake_1m_ub:z20m_L1m),'b:') % WD0 is non Lake! +% LF 0.5 +plot(T_WD0_LD1_LR10_LF50(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'k--',T_WD1_LD1_LR10_LF50(Lake_1m_ub:z20m_L1m),zgeneral_L1m(Lake_1m_ub:z20m_L1m),'b--') +% no lateral exchange +%plot(T_WD0_LD1_LR10_LF25_nolat(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'k',T_WD1_LD1_LR10_LF25_nolat(Lake_1m_ub:z20m_L1m),zgeneral_L1m(Lake_1m_ub:z20m_L1m),'c') +% initial T +%plot(Tini(1:5,2),Tini(1:5,1),'g*',Tini(1:5,3),Tini(1:5,1),'b*') +plot([-20 10],[LD(1) LD(1)],'b:','LineWidth',0.8) % lake depth level +hold off +legend('Soil','Lake','Location','SouthWest'); title(['lake radius ',num2str(LR(1)),'m']) % title(['LD',num2str(LD(1)),' LR',num2str(LR(1))]) +set(gca,'Ydir','reverse'); xlabel('T (°C)'); ylabel('z (m)'); grid on +axis([-20 10 0 10]) + + subplot(1,2,2) +% MSW (LR 100m) +hold on +plot(T_WD0_LD1_LR100_LF25(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'k',T_WD1_LD1_LR100_LF25(Lake_1m_ub:z20m_L1m),zgeneral_L1m(Lake_1m_ub:z20m_L1m),'b') +plot(T_WD0_LD1_LR100_LF10(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'k:',T_WD1_LD1_LR100_LF10(Lake_1m_ub:z20m_L1m),zgeneral_L1m(Lake_1m_ub:z20m_L1m),'b:') +plot(T_WD0_LD1_LR100_LF50(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'k--',T_WD1_LD1_LR100_LF50(Lake_1m_ub:z20m_L1m),zgeneral_L1m(Lake_1m_ub:z20m_L1m),'b--') +% no lateral exchange +%plot(T_WD0_LD1_LR100_LF25_nolat(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'k',T_WD1_LD1_LR100_LF25_nolat(Lake_1m_ub:z20m_L1m),zgeneral_L1m(Lake_1m_ub:z20m_L1m),'c') +%plot(Tini(1:5,2),Tini(1:5,1),'g*',Tini(1:5,3),Tini(1:5,1),'b*') +plot([-20 10],[LD(1) LD(1)],'b:','LineWidth',0.8) % lake depth level +hold off +legend('Soil','Lake','Location','SouthWest'); title(['lake radius ',num2str(LR(2)),'m']) % title(['LD',num2str(LD(1)),' LR',num2str(LR(2))]) +set(gca,'Ydir','reverse'); xlabel('T (°C)'); ylabel('z (m)'); grid on +axis([-20 10 0 10]) + +print -dtiff Tz_LD1m + + +%% +figure(2) % LD 5m + subplot(1,2,1) +% SSW (LR 10m) +hold on +% LF 0.25 +plot(T_WD0_LD5_LR10_LF25(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'k',T_WD5_LD5_LR10_LF25(Lake_5m_ub:z20m_L5m),zgeneral_L5m(Lake_5m_ub:z20m_L5m),'b') +% LF 0.1 +plot(T_WD0_LD5_LR10_LF10(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'k:',T_WD5_LD5_LR10_LF10(Lake_5m_ub:z20m_L5m),zgeneral_L5m(Lake_5m_ub:z20m_L5m),'b:') % WD0 is non Lake! +% LF 0.5 +plot(T_WD0_LD5_LR10_LF50(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'k--',T_WD5_LD5_LR10_LF50(Lake_5m_ub:z20m_L5m),zgeneral_L5m(Lake_5m_ub:z20m_L5m),'b--') +% no lateral exchange +%plot(T_WD0_LD5_LR10_LF25_nolat(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'k',T_WD5_LD5_LR10_LF25_nolat(Lake_5m_ub:z20m_L5m),zgeneral_L5m(Lake_5m_ub:z20m_L5m),'c') +% initial T +%plot(Tini(1:5,2),Tini(1:5,1),'g*',Tini(1:5,3),Tini(1:5,1),'b*') +plot([-20 10],[LD(2) LD(2)],'b:','LineWidth',0.8) % lake depth level +hold off +legend('Soil','Lake','Location','SouthWest'); title(['lake radius ',num2str(LR(1)),'m']) % title(['LD',num2str(LD(1)),' LR',num2str(LR(1))]) +set(gca,'Ydir','reverse'); xlabel('T (°C)'); ylabel('z (m)'); grid on +axis([-20 10 0 10]) + + subplot(1,2,2) +% MSW (LR 100m) +hold on +plot(T_WD0_LD5_LR100_LF25(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'k',T_WD5_LD5_LR100_LF25(Lake_5m_ub:z20m_L5m),zgeneral_L5m(Lake_5m_ub:z20m_L5m),'b') +plot(T_WD0_LD5_LR100_LF10(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'k:',T_WD5_LD5_LR100_LF10(Lake_5m_ub:z20m_L5m),zgeneral_L5m(Lake_5m_ub:z20m_L5m),'b:') +plot(T_WD0_LD5_LR100_LF50(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'k--',T_WD5_LD5_LR100_LF50(Lake_5m_ub:z20m_L5m),zgeneral_L5m(Lake_5m_ub:z20m_L5m),'b--') +% no lateral exchange +%plot(T_WD0_LD5_LR100_LF25_nolat(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'k',T_WD1_LD1_LR100_LF25_nolat(Lake_5m_ub:z20m_L5m),zgeneral_L5m(Lake_5m_ub:z20m_L5m),'c') +%plot(Tini(1:5,2),Tini(1:5,1),'g*',Tini(1:5,3),Tini(1:5,1),'b*') +plot([-20 10],[LD(2) LD(2)],'b:','LineWidth',0.8) % lake depth level +hold off +legend('Soil','Lake','Location','SouthWest'); title(['lake radius ',num2str(LR(2)),'m']) % title(['LD',num2str(LD(1)),' LR',num2str(LR(2))]) +set(gca,'Ydir','reverse'); xlabel('T (°C)'); ylabel('z (m)'); grid on +axis([-20 10 0 10]) + +print -dtiff Tz_LD5m + + +%% +figure(3) % LD 1m + subplot(1,2,1) +% SSW (LR 10m) +hold on +% LF 0.25 +plot(T_WD0_LD1_LR10_LF25(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'k',T_WD1_LD1_LR10_LF25(Lake_1m_ub:z20m_L1m),zgeneral_L1m(Lake_1m_ub:z20m_L1m),'b') +% no lateral exchange +plot(T_WD0_LD1_LR10_LF25_nolat(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'k--',T_WD1_LD1_LR10_LF25_nolat(Lake_1m_ub:z20m_L1m),zgeneral_L1m(Lake_1m_ub:z20m_L1m),'b--') +plot([-20 10],[LD(1) LD(1)],'b:','LineWidth',0.8) % lake depth level +hold off +legend('Soil','Lake','Location','SouthWest'); title(['lake radius ',num2str(LR(1)),'m']) % title(['LD',num2str(LD(1)),' LR',num2str(LR(1))]) +set(gca,'Ydir','reverse'); xlabel('T (°C)'); ylabel('z (m)'); grid on +axis([-20 10 0 10]) + + subplot(1,2,2) +% MSW (LR 100m) +hold on +plot(T_WD0_LD1_LR100_LF25(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'k',T_WD1_LD1_LR100_LF25(Lake_1m_ub:z20m_L1m),zgeneral_L1m(Lake_1m_ub:z20m_L1m),'b') +% no lateral exchange +plot(T_WD0_LD1_LR100_LF25_nolat(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'k--',T_WD1_LD1_LR100_LF25_nolat(Lake_1m_ub:z20m_L1m),zgeneral_L1m(Lake_1m_ub:z20m_L1m),'b--') +plot([-20 10],[LD(1) LD(1)],'b:','LineWidth',0.8) % lake depth level +hold off +legend('Soil','Lake','Location','SouthWest'); title(['lake radius ',num2str(LR(2)),'m']) % title(['LD',num2str(LD(1)),' LR',num2str(LR(2))]) +set(gca,'Ydir','reverse'); xlabel('T (°C)'); ylabel('z (m)'); grid on +axis([-20 10 0 10]) + +print -dtiff Tz_LD1m_nolat diff --git a/analysis/cm_blueautumn.mat b/analysis/cm_blueautumn.mat new file mode 100644 index 0000000..523e346 Binary files /dev/null and b/analysis/cm_blueautumn.mat differ diff --git a/analysis/plot_T_14.m b/analysis/plot_T_14.m new file mode 100644 index 0000000..3afe27c --- /dev/null +++ b/analysis/plot_T_14.m @@ -0,0 +1,210 @@ +% plot T timeseries for various depth + +%filespec='MSW_xH1_xW0_xS0_infil1_xice0_rF1_sF1'; +%filespec='MSW_xH1'; +filespec='LF25_xH1' +yearspec='2010'; + +% NL +outputfile1 = ['../runs/WD0_',filespec,'/WD0_',filespec,'_',yearspec]; +configfile1 = ['../runs/WD0_',filespec,'/WD0_',filespec,'_settings']; +% Lake +outputfile2 = ['../runs/WD5_',filespec,'/WD5_',filespec,'_',yearspec]; +configfile2 = ['../runs/WD5_',filespec,'/WD5_',filespec,'_settings']; + +% % NL +% outputfile1 = ['../runs/',filespec,'_i1/',filespec,'_i1_output',yearspec]; +% configfile1 = ['../runs/',filespec,'_i1/',filespec,'_i1_settings']; +% %Lake +% outputfile2 = ['../runs/',filespec,'_i2/',filespec,'_i2_output',yearspec]; +% configfile2 = ['../runs/',filespec,'_i2/',filespec,'_i2_settings']; + + +% outputfile1 = ['../runs/noLateral/runs/',filespec,'_i2/',filespec,'_i2_output',yearspec]; +% configfile1 = ['../runs/noLateral/runs/',filespec,'_i2/',filespec,'_i2_settings']; +% % Lake +% outputfile2 = ['../runs/noLateral/runs/',filespec,'_i1/',filespec,'_i1_output',yearspec]; +% configfile2 = ['../runs/noLateral/runs/',filespec,'_i1/',filespec,'_i1_settings']; + +%% Non_Lake +load(outputfile1); load(configfile1); +txt_setting=['Infil: ',num2str(PARA.modules.infiltration),' Xice: ',num2str(PARA.modules.xice),' heat ex: ',num2str(PARA.modules.exchange_heat),' water ex: ',num2str(PARA.modules.exchange_water),' snow ex: ',num2str(PARA.modules.exchange_snow), ' LakeDepth: ',num2str(PARA.water.depth)]; +txt_forcing=[PARA.forcing.filename(1:end-4),' (',num2str(PARA.forcing.rain_fraction),' ',num2str(PARA.forcing.snow_fraction),')']; + +%PARA=p1; GRID=g1; OUT=o1; +%z_soil=GRID.soil.soilGrid; ub_soil=GRID.soil.cT_domain_ub; +Soil_NL_ub = GRID.soil.cT_domain_ub; Soil_NL_lb = GRID.soil.cT_domain_lb; +zgeneral_NL=GRID.general.cT_grid; +zsoil_NL = GRID.general.cT_grid(Soil_NL_ub:Soil_NL_lb); +[~, z10m_NL]=min(abs(zgeneral_NL-10)); [~, z15m_NL]=min(abs(zgeneral_NL-15)); [~, z20m_NL]=min(abs(zgeneral_NL-20)); +dl=10; % number of lines to plot +dz= round(length(zgeneral_NL)/dl); +tstamp=OUT.timestamp; +T_NL=OUT.cryoGrid3; T_NLtm = mean(T_NL,2); +TSoil_NL=T_NL(Soil_NL_ub:Soil_NL_lb,:); +%TAir=T_NL(GRID.air.cT_domain_ub:GRID.air.cT_domain_lb,:); +TSnow=T_NL(GRID.snow.cT_domain_ub:GRID.snow.cT_domain_lb,:); +TSnow_lb=T_NL(GRID.lake.water.cT_domain_ub-1,:); +%OUT.soil.topPosition +% Forcing +tspan=FORCING.data.t_span; [ ~ ,idx1 ] = min( abs( tstamp(1) - tspan )); [ ~ ,idx2 ] = min( abs( tstamp(end) - tspan )); +tstamp_forcing=FORCING.data.t_span(idx1:idx2); +Tair=FORCING.data.Tair(idx1:idx2); + +Qlat_NL=OUT.EB.Q_lateral; Qlat_NLtm = mean(Qlat_NL,2); + +%% +co=cool(dl); % new Color Order +set(groot,'defaultAxesColorOrder',co) + + figure(1) +plot(tstamp,T_NL) +grid on; datetick; xlabel('timestamp'); ylabel('T_{NL} (°C)'); grid on; +xxx=xlim; yyy=ylim; text(xxx(1),0.9*yyy(2),txt_setting) +title(txt_forcing, 'Interpreter', 'none') + + figure(2) +%plot(tstamp,TAir,'b', +%tstamp,TSnow,'y',tstamp,TLakeWater,'b',tstamp,TLakeIce,'c',tstamp,TSoil,'g');plot(tstamp,TSnow,'k--',tstamp,TSoil(1:dz:end,:),'g') +plot(tstamp,TSoil_NL(1:dz:end,:),'g') +%hold on +% if(~isempty(GRID.lake.water.cT_domain_ub)); plot(tstamp,TLakeWater(1:dz:end,:),'b'); end +% if(~isempty(GRID.lake.ice.cT_domain_ub)); plot(tstamp,TLakeIce(1:dz:end,:),'c'); end +% hold off +grid on; datetick; xlabel('timestamp'); ylabel('T Soil (°C)'); grid on; + +%% lateral heat fluxes +figure(4) +colormap(cool) +plot(tstamp,Qlat_NL(Soil_NL_ub:dz:end,:)) +%plot(tstamp,Qlat_NL(Soil_NL_ub:Soil_NL_ub+2,:)') +grid on; datetick; ylabel('Qlat NL (W/m2)') + +% figure(5) +% ti=100; z1=83; z2=300; +% subplot(1,2,1) +% plot(Qlat_NL(z1:z2,ti),zgeneral(z1:z2)) +% hold on; plot(Qlat_NLtm(z1:z2),zgeneral(z1:z2),'--'); hold off +% set(gca,'Ydir','reverse'); xlabel('Qlat Non-LAKE (W/m2)'); ylabel('z (m)'); grid on +% subplot(1,2,2) +% plot(T_L(z1:z2,ti)-T_NL(z1:z2,ti),zgeneral(z1:z2)) +% hold on; plot(T_Ltm(z1:z2)-T_NLtm(z1:z2),zgeneral(z1:z2),'--'); hold off +% set(gca,'Ydir','reverse'); xlabel('T_L-T_{NL} (°C)'); ylabel('z (m)'); grid on +% +% T_NL and T_L profiles +% figure(6) +% subplot(1,2,1) +% plot(T_L(z1:z2,ti),zgeneral(z1:z2)) +% hold on; plot(T_Ltm(z1:z2),zgeneral(z1:z2),'--'); hold off +% set(gca,'Ydir','reverse'); xlabel('T_L (°C)'); ylabel('z (m)'); grid on +% axis([-10 10 -inf inf]) +% subplot(1,2,2) +% plot(T_NL(z1:z2,ti),zgeneral(z1:z2)) +% hold on; plot(T_NLtm(z1:z2),zgeneral(z1:z2),'--'); hold off +% set(gca,'Ydir','reverse'); xlabel('T_{NL} (°C)'); ylabel('z (m)'); grid on +% axis([-10 10 -inf inf]) + + +%% LAKE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +load(outputfile2); load(configfile2); +txt_setting=['Infil: ',num2str(PARA.modules.infiltration),' Xice: ',num2str(PARA.modules.xice),' heat ex: ',num2str(PARA.modules.exchange_heat),' water ex: ',num2str(PARA.modules.exchange_water),' snow ex: ',num2str(PARA.modules.exchange_snow), ' LakeDepth: ',num2str(PARA.water.depth)]; +txt_forcing=[PARA.forcing.filename(1:end-4),' (',num2str(PARA.forcing.rain_fraction),' ',num2str(PARA.forcing.snow_fraction),')']; + +%PARA=p2; GRID=g2; OUT=o2; +n_LakeLayers=nansum(GRID.lake.water.cT_domain)+nansum(GRID.lake.ice.cT_domain); +Lake_ub = GRID.soil.cT_domain_ub-n_LakeLayers; +Lake_lb = GRID.soil.cT_domain_ub-1; +Soil_L_ub = GRID.soil.cT_domain_ub; +zgeneral_L=GRID.general.cT_grid; +zsoil = GRID.general.cT_grid(GRID.soil.cT_domain_ub:GRID.soil.cT_domain_lb); +[~, z10m_L]=min(abs(zgeneral_L-10)); [~, z15m_L]=min(abs(zgeneral_L-15)); [~, z20m_L]=min(abs(zgeneral_L-20)); +tstamp=OUT.timestamp; + +T_L=OUT.cryoGrid3; T_Ltm = mean(T_L,2); +TSoil_L=T_L(GRID.soil.cT_domain_ub:GRID.soil.cT_domain_lb,:); %TLakeWater=T_L(GRID.lake.water.cT_domain_ub:GRID.lake.water.cT_domain_lb,:); TLakeIce=T_L(GRID.lake.ice.cT_domain_ub:GRID.lake.ice.cT_domain_lb,:); +%TAir=T_L(GRID.air.cT_domain_ub:GRID.air.cT_domain_lb,:); +TSnow=T_L(GRID.snow.cT_domain_ub:GRID.snow.cT_domain_lb,:); +TSnow_lb=T_L(GRID.lake.water.cT_domain_ub-1,:); +TLake = T_L(Lake_ub:Lake_lb,:); +TLake_ub = T_L(GRID.soil.cT_domain_ub-n_LakeLayers,:); % uppermost lake layer +TLake_lb = T_L(GRID.soil.cT_domain_ub-1,:); % lowermost lake layer +TaboveLake = T_L(GRID.soil.cT_domain_ub-(n_LakeLayers+1),:); +TSoil_L_ub = T_L(GRID.soil.cT_domain_ub,:); + +Qlat_L=OUT.EB.Q_lateral; Qlat_Ltm = mean(Qlat_L,2); + +%% + figure(11) +plot(tstamp,T_L) +grid on; datetick; xlabel('timestamp'); ylabel('T (°C)'); grid on; +xxx=xlim; yyy=ylim; text(xxx(1),0.9*yyy(2),txt_setting) +title(txt_forcing, 'Interpreter', 'none') + + figure(12) % T Soil and T Lake +%plot(tstamp,TAir,'b', tstamp,TSnow,'y',tstamp,TLakeWater,'b',tstamp,TLakeIce,'c',tstamp,TSoil_L,'g') +%plot(tstamp,TSnow,'k--',tstamp,TSoil_L(1:dz:end,:),'g') +plot(tstamp,TSoil_L(1:dz:end,:),'g') +hold on +if(~isempty(GRID.lake.water.cT_domain_ub)); plot(tstamp,TLake(1:dz:end,:),'b'); end +%if(~isempty(GRID.lake.water.cT_domain_ub)); plot(tstamp,TLakeWater(1:dz:end,:),'b'); end +%if(~isempty(GRID.lake.ice.cT_domain_ub)); plot(tstamp,TLakeIce(1:dz:end,:),'c'); end +hold off +grid on; datetick; xlabel('timestamp'); ylabel('T (soil & lake)(°C)'); +xxx=xlim; yyy=ylim; text(xxx(1),0.9*yyy(2),txt_setting) +title(txt_forcing, 'Interpreter', 'none') + +figure(13) % T Lake +%plot(tstamp,TLake(1:49:end,:),'b',tstamp,TaboveLake,'g',tstamp,TSoil_L_ub,'r') +plot(tstamp,TLake_ub,'b',tstamp,TLake_lb,'c',tstamp,TaboveLake,'mx',tstamp,TSoil_L_ub,'g') +legend('Tlake ub','Tlake lb','TaboveLake','TSoil ub') +hold on; plot(tstamp,TLake(1:10:end,:),'b--'); hold off +grid on; datetick; xlabel('timestamp'); ylabel('T (lake + ...)(°C)'); +xxx=xlim; yyy=ylim; text(xxx(1),0.9*yyy(2),txt_setting) +title(txt_forcing, 'Interpreter', 'none') + + +%% lateral heat fluxes +ti=100; + +figure(14) +plot(tstamp,Qlat_L(Lake_ub:dz:end,:)') +grid on; datetick; ylabel('Qlat LAKE (W/m2)') +legend('1','2','3','4','5','6','7','8','9','10') + +figure(15) + subplot(1,2,1) +plot(Qlat_L(Lake_ub:end,ti),zgeneral_L(Lake_ub:end),'b--',Qlat_Ltm(Lake_ub:end),zgeneral_L(Lake_ub:end),'b'); +legend('ti','tm') +set(gca,'Ydir','reverse'); xlabel('Qlat LAKE (W/m2)'); ylabel('z (m)'); grid on; title([filespec,' ',yearspec]) + subplot(1,2,2) +% plot(T_L(z1:z2,ti)-T_NL(z1:z2,ti),zgeneral(z1:z2),'b--',T_Ltm(z1:z2)-T_NLtm(z1:z2),zgeneral(z1:z2),'b' is not on same depth!! +%legend('ti','tm') +%set(gca,'Ydir','reverse'); xlabel('T_L-T_{NL} (°C)'); ylabel('z (m)'); grid on + +% T_NL and T_L profiles +figure(16) + subplot(1,3,1) +plot(T_L(Lake_ub:end,ti),zgeneral_L(Lake_ub:end),'b--',T_Ltm(Lake_ub:end),zgeneral_L(Lake_ub:end),'b',T_NL(Soil_NL_ub:end,ti),zgeneral_NL(Soil_NL_ub:end),'g--',T_NLtm(Soil_NL_ub:end),zgeneral_NL(Soil_NL_ub:end),'g') +legend('L(ti)','L(tm)','NL(ti)','NL(tm)') +set(gca,'Ydir','reverse'); xlabel('T (°C)'); ylabel('z (m)'); grid on; title([filespec,' ',yearspec]) +axis([-12 10 -inf inf]) + subplot(1,3,2) +plot(T_L(Lake_ub:z20m_L,ti),zgeneral_L(Lake_ub:z20m_L),'b--',T_Ltm(Lake_ub:z20m_L),zgeneral_L(Lake_ub:z20m_L),'b',T_NL(Soil_NL_ub:z20m_NL,ti),zgeneral_NL(Soil_NL_ub:z20m_NL),'g--',T_NLtm(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'g') +legend('L(ti)','L(tm)','NL(ti)','NL(tm)') +set(gca,'Ydir','reverse'); xlabel('T (°C)'); ylabel('z (m)'); grid on + subplot(1,3,3) +plot(Qlat_L(Lake_ub:z20m_L,ti),zgeneral_L(Lake_ub:z20m_L),'b--',Qlat_Ltm(Lake_ub:z20m_L),zgeneral_L(Lake_ub:z20m_L),'b',-Qlat_NL(Soil_NL_ub:z20m_NL,ti),zgeneral_NL(Soil_NL_ub:z20m_NL),'g--',-Qlat_NLtm(Soil_NL_ub:z20m_NL),zgeneral_NL(Soil_NL_ub:z20m_NL),'g') +legend('L(ti)','L(tm)','-NL(ti)','-NL(tm)') +set(gca,'Ydir','reverse'); xlabel('Qlat (W/m2)'); ylabel('z (m)'); grid on + + +figure(17) % uppermost soil layer vs. uppermost lake layer +%plot(tstamp,T_NL(GRID.soil.cT_domain_ub-10:GRID.soil.cT_domain_ub,:),'g',tstamp,TLake_ub,'b') +plot(tstamp,TSoil_NL(1,:),'g',tstamp,T_L(Lake_ub,:),'b') +legend('top T(NL)','top T(lake)'); grid on; ylabel('T (°C)'); datetick + +figure(21) % check whether Qlat(L) = - Qlat(NL) +plot(Qlat_NL(Soil_NL_ub:Soil_NL_lb,ti)+Qlat_L(Lake_ub:Soil_NL_lb,ti),zgeneral_L(Lake_ub:Soil_NL_lb),'b--',Qlat_NLtm(Soil_NL_ub:Soil_NL_lb)+Qlat_Ltm(Lake_ub:Soil_NL_lb),zgeneral_L(Lake_ub:Soil_NL_lb),'b') +legend('ti','tm') +set(gca,'Ydir','reverse'); xlabel('Qlat LAKE + Qlat NL (W/m2)'); ylabel('z (m)'); grid on; %title([filespec,' ',yearspec]) +title(['Diff not on same level!!!! z integrated Qlat L+NL at ti ',num2str(nansum(Qlat_L(:,ti)) + nansum(Qlat_NL(:,ti)) ),' W/m2']) diff --git a/analysis/plot_output.m b/analysis/plot_output.m new file mode 100644 index 0000000..2833134 --- /dev/null +++ b/analysis/plot_output.m @@ -0,0 +1,239 @@ +% Plotting script for temperature and water content fields +% Author: Jan Nitzbon +% +%function plot_output(dirname, runname, number_of_realizations) + +clear all +close all + + %tsvd dirname = '/home/jnitzbon/CryoGrid/github/GITHUB_CryoGrid3_infiltration_xice_mpi/runs/'; + %runname = 'testrunMPI_POOL_xH1_xW1_xS1_infil1_xice1_rF1.000000_sF1.000000_realization'; + dirname = 'E:\CryoGrid3\runs'; +% runname = 'testrunMPI_POOL_xH1_xW1_xS1_infil1_xice1_rF1_sF1_realization2'; +%runname = ' TESTRUN-MPI_197906-198007_stratSAM_geomHEX_extFluxT-0.005_xH1_xW1_xS0_rf1_sf1'; +%runname ='TESTRUN-MPI_197906-198007_stratSAM_geomHEX_extFluxT-0.005_xH0_xW0_xS0_rf1_sf1'; +%runname='TESTRUN-MPI_197906-198007_stratSAM_geomHEX_extFluxT-0.005_xH0_xW0_xS0_rf1_sf1'; +runname='LAKE-MPI_xH0_xW0_xS0_infil0_xice0_rF1_sF1_i1_realization' +number_of_realizations = 2 +% + + %% load output data and settings from files of all workers + + + dir = dirname;%'/home/jnitzbon/gls1/CryoGrid/CryoGrid3_infiltration_xice_mpi_DEV/'; + %cm=load( [ './analysis/cm_blueautumn.mat' ] ); + + infil=0; + xice=0; + rainFrac=0; + snowFrac=0; + wt = 0.0; + + OUTS = {}; + + for i=1:number_of_realizations + run = runname; %sprintf('testrun_mpi_waterExchange_noGroundHeatFlux_realization' ); + if number_of_realizations>1 + run = [ run num2str(i) ]; + end + %tsvd outputfile = [dir run '/' run '_output1979.mat']; + % configfile = [dir run '/' run '_settings.mat']; + + %outputfile = [dir run '\' run '_output1979.mat']; + %configfile = [dir run '\' run '_settings.mat']; + + outputfile = ['..\runs\' run '_output1980.mat']; + configfile = ['..\runs\' run '_settings.mat']; + + %outputfile = 'E:\CryoGrid3\runs\testrunMPI_POOL_xH1_xW1_xS1_infil1_xice1_rF1_sF1_realization2\testrunMPI_POOL_xH1_xW1_xS1_infil1_xice1_rF1_sF1_realization2_output1979.mat' + %configfile = 'E:\CryoGrid3\runs\testrunMPI_POOL_xH1_xW1_xS1_infil1_xice1_rF1_sF1_realization2\testrunMPI_POOL_xH1_xW1_xS1_infil1_xice1_rF1_sF1_realization2_settings.mat' + + load(outputfile); + load(configfile); + + OUTS{i}.OUT=OUT; + OUTS{i}.PARA = PARA; + OUTS{i}.GRID = GRID; + OUTS{i}.FORCING = FORCING; + + clear OUT PARA GRID FORCING + end + + %% extract relevant vectors + + ts = OUTS{1}.OUT.timestamp(); + zs = {}; + Ts = {}; + LWCs = {}; + WCs = {}; + snowTop = {}; + soilTop = {}; + + for i=1:number_of_realizations + %zs{i} = OUTS{i}.PARA.ensemble.initial_altitude(i)-OUTS{i}.GRID.general.cT_grid; + zs{i} = OUTS{i}.PARA.location.altitude-OUTS{i}.GRID.general.cT_grid; + Ts{i} = OUTS{i}.OUT.cryoGrid3; + LWCs{i} = OUTS{i}.OUT.liquidWater; + WCs{i} = OUTS{i}.OUT.water; + snowTop{i} = OUTS{i}.OUT.soil.topPosition; + end + %lakeFloor = OUT.soil.lakeFloor(); + %lakeFloor = [ NaN(length(soilTop)-length(lakeFloor),1); lakeFloor ]; + + % limits + minz = min(OUTS{1}.PARA.location.altitude - 1); + maxz = max(OUTS{1}.PARA.location.altitude + 0.5); + + mint = min(ts); + maxt = max(ts); + + + %% plot evolution of simple ensemlbe variables + + if number_of_realizations>1 + + figure; + + subplot(2,3,1) + hold on + for i=1:number_of_realizations + plot(ts, OUTS{i}.OUT.location.active_layer_depth_altitude(:)) ; %abs(OUTS{1}.OUT.ensemble.altitude(:,i)- + end + datetick; + title('Frost table [m asl]') + hold off + + subplot(2,3,2) + hold on + for i=1:number_of_realizations + plot(ts, OUTS{i}.OUT.location.water_table_altitude(:)); + end + datetick; + title('Water table [m asl]') + hold off + + subplot(2,3,3) + hold on + for i=1:number_of_realizations + plot(ts, OUTS{i}.OUT.location.surface_altitude(:)); + end + datetick; + title('Surface altitude [m asl]') + hold off + + subplot(2,3,4) + hold on + for i=1:number_of_realizations + plot(ts, nansum(OUTS{i}.OUT.lateral.heat_fluxes , 2)); + end + datetick; + title('Heat fluxes [ W / m^2 ]') + hold off + + subplot(2,3,5) + hold on + for i=1:number_of_realizations + plot(ts, nansum(OUTS{i}.OUT.lateral.water_fluxes .*1000 .* 3600 , 2)); + end + datetick; + title('Water fluxes [mm/h]') + hold off + + subplot(2,3,6) + hold on + for i=1:number_of_realizations + plot(ts, nansum(OUTS{i}.OUT.lateral.snow_fluxes .*1000 .* 3600 , 2) ); + end + datetick; + title('Snow fluxes [mm/h]') + hold off + + currentFigure = gcf; + title(currentFigure.Children(end), runname); + + end + + + + %% plot temperature and water content fields + + figure; + + title(runname); + + %temperature fields + for i=1:number_of_realizations + subplot(2, number_of_realizations, i); + pcolor( ts', zs{i}', Ts{i}); + hold on; + shading flat; % do not show grid + %shading interp; + % colormap and colorbar + caxis( [ -40, 20] ); + colormap(gca, cm.Colormap_blueautumn); + if i==1 + cbar=colorbar('location','westoutside'); + end + % layout + axis( [ mint maxt minz maxz ] ); + datetick; + xlabel('time') + ylabel('$z$ [m]', 'Interpreter', 'latex'); + xlabel(cbar, '$T$ [$^\circ$C]', 'Interpreter', 'latex'); + title(sprintf('Temperature - realization %d', i)); + hold off; + end + + % (liquid) water content fields + for i=1:number_of_realizations + subplot(2, number_of_realizations, number_of_realizations+i); + pcolor( ts', zs{i}', WCs{i}); %ax, + hold on; + caxis( [ 0. , 1. ] ); + colormap(gca, 'parula'); + colormap(gca, flipud(colormap)); + if i==1 + cbar=colorbar('location','westoutside'); + end + shading flat; + % layout + axis( [ mint maxt minz maxz ] ); + datetick; + xlabel('time'); + ylabel('$z$ [m]', 'Interpreter', 'latex'); + xlabel(cbar, '$\theta_w$ [-]', 'Interpreter', 'latex'); + title(sprintf('Water content - realization %d', i)); + hold off; + end + + currentFigure = gcf; + title(currentFigure.Children(end), runname); + + + %% plot soil moisture and temperature of requested depth + + % request time series for a certain depths + requestedDepth = 0.3 ; + + % get the altitude of the uppermost soil cell (which indeed contains + % mineral or organic material, i.e. excluding a potential waterbody) + soil_surface_altitude = OUTS{1}.PARA.location.altitude + min( OUTS{1}.OUT.soil.topPosition, OUTS{1}.OUT.soil.lakeFloor ); + + % the static altitude grid + altitude_grid = zs{1}; + + % compute the index of the requested cell for each timestep + A = zeros( length(altitude_grid), length(soil_surface_altitude) ); % matrix to serach in + for j=1:size(A,2) %loop over all timesteps + A(:,j) = soil_surface_altitude(j)- altitude_grid; % distance of each grid cell (first dim) to the surface for each timestep (second dim) + end + [~, indexes] = min( abs( A - requestedDepth ) ); % determine index of closest cell to the requested depth + + % transform column-wise index to linear index of whole matrix + linindexes = sub2ind( [length(altitude_grid),length(ts)], indexes, [1:1:length(ts)] ); + + figure; + plot( ts, LWCs{i}(linindexes) ); + + figure; + plot( ts, Ts{i}(linindexes) ); \ No newline at end of file diff --git a/modules/CryoGridInfiltrationUnfrozenSoil/CryoGridInfiltration.m b/modules/CryoGridInfiltrationUnfrozenSoil/CryoGridInfiltration.m index 459d8fe..473caf6 100644 --- a/modules/CryoGridInfiltrationUnfrozenSoil/CryoGridInfiltration.m +++ b/modules/CryoGridInfiltrationUnfrozenSoil/CryoGridInfiltration.m @@ -1,77 +1,73 @@ -function [wc, GRID, BALANCE] = CryoGridInfiltration(T, wc, dwc_dt, timestep, GRID, PARA, FORCING, BALANCE) - - % possible meltwater contribution from xice - if ~PARA.modules.xice - meltwaterGroundIce = 0; - else - meltwaterGroundIce = GRID.lake.residualWater; - GRID.lake.residualWater=0; - end - +function [wc, GRID, BALANCE] = CryoGridInfiltration(T, wc, dwc_dt, timestep, GRID, PARA, FORCING, BALANCE, lateral_flux_rate) + +% if isempty(GRID.snow.cT_domain_ub) && T(GRID.soil.cT_domain_ub)>0 %no snow cover and uppermost grid cell unfrozen +%lll if isempty(GRID.snow.cT_domain_ub) && T(GRID.soil.cT_domain_ub)>0 || ~isempty (GRID.lake.water.cT_domain_ub) || ~isempty (GRID.lake.ice.cT_domain_ub) %no snow cover and uppermost grid cell unfrozen +% only infiltrate if no snow, unfrozen soil exists, tsvd: no-LAKE case added +if (isempty (GRID.snow.cT_domain_ub) && T(GRID.soil.cT_domain_ub)>0 ) && isempty (GRID.lake.water.cT_domain_ub) && isempty (GRID.lake.ice.cT_domain_ub) % no snow cover and uppermost grid cell unfrozen, no lake +% possible contribution from xice meltwater, snowmelt, rain on frozen ground + residualWater = GRID.lake.residualWater; + GRID.lake.residualWater=0; + % external flux external_flux_rate = PARA.soil.externalWaterFlux; % in m/day - BALANCE.water.dr_subsurface = BALANCE.water.dr_subsurface + external_flux_rate.*timestep.*1000; %in mm + BALANCE.water.dr_external = BALANCE.water.dr_external + external_flux_rate.*timestep.*1000; %in mm - if isempty(GRID.snow.cT_domain_ub) && T(GRID.soil.cT_domain_ub)>0 %no snow cover and uppermost grid cell unfrozen + % lateral flux to/from other workers + lateral_flux_rate = lateral_flux_rate.*3600.*24; % now in m/day + BALANCE.water.dr_lateral = BALANCE.water.dr_lateral + lateral_flux_rate.*timestep.*1000; - %%% step 1: infiltrate rain and meltwater and external flux through bucket scheme - % changes due to evapotranspiration and condensation - dwc_dt=dwc_dt.*timestep.*24.*3600; %now in m water per grid cell - BALANCE.water.de = BALANCE.water.de + sum(dwc_dt)*1000; % in mm accumulated over soil column + %%% step 1: infiltrate rain and meltwater and external flux through bucket scheme + % changes due to evapotranspiration and condensation + dwc_dt=dwc_dt.*timestep.*24.*3600; %now in m water per grid cell + BALANCE.water.de = BALANCE.water.de + sum(dwc_dt)*1000; % in mm accumulated over soil column - % changes due to rainfall - dwc_dt(1)=dwc_dt(1)+FORCING.i.rainfall./1000.*timestep; + % changes due to rainfall + dwc_dt(1)=dwc_dt(1)+FORCING.i.rainfall./1000.*timestep; - % changes due to meltwater from excess ice - dwc_dt(1)=dwc_dt(1)+meltwaterGroundIce; + % changes due to residual water + dwc_dt(1)=dwc_dt(1)+residualWater; - % routing of water - [wc, surface_runoff] = bucketScheme(T, wc, dwc_dt, GRID, PARA, external_flux_rate.*timestep); + % routing of water + [wc, surface_runoff, lacking_water] = bucketScheme(T, wc, dwc_dt, GRID, PARA, (external_flux_rate+lateral_flux_rate).*timestep); - % consistency check - if sum( wc<0 )~=0 - warning('negative water content occured'); - %here one could correct the water balance + % consistency check + if sum( wc<0 )~=0 + warning( 'CryoGridInfiltration - negative water content occured after bucket scheme' ); + %here one could correct the water balance + end + % remove water above water table in case of ponding, e.g. through rain (independent of xice module) + if GRID.soil.cT_mineral(1)+GRID.soil.cT_organic(1)<1e-6 && ... + PARA.location.initial_altitude-GRID.general.K_grid(GRID.soil.cT_domain_ub)>PARA.location.absolute_maxWater_altitude %zzz check for lake... + cellSize = GRID.general.K_delta(GRID.soil.cT_domain_ub); + actualWater = wc(1)*cellSize; + h = PARA.location.absolute_maxWater_altitude - (PARA.location.initial_altitude-GRID.general.K_grid(GRID.soil.cT_domain_ub+1)); + if h<0 + warning('h<0. too much water above water table!') end - - % remove water above water table in case of ponding, e.g. through rain (independent of xice module) - if GRID.soil.cT_mineral(1)+GRID.soil.cT_organic(1)<1e-6 && ... - GRID.general.K_grid(GRID.soil.cT_domain_ub)h - disp('infiltration - removing excess water from upper cell'); - wc(1)=h./cellSize; - surface_runoff = surface_runoff + actualWater-h; - end - - + if actualWater>h + disp('infiltration - removing excess water from upper cell'); + wc(1)=h./cellSize; + surface_runoff = surface_runoff + actualWater-h; end + end - %%% step 2: update GRID including reomval of excess water above water table and ponding below water table - [ wc, GRID, surface_runoff ] = updateGRID_infiltration(wc, GRID, PARA, surface_runoff); - +%%% step 2: update GRID including reomval of excess water above water table and ponding below water table + [ wc, GRID, surface_runoff ] = updateGRID_infiltration(wc, GRID, PARA, surface_runoff); - % store remaining surface runoff - BALANCE.water.dr_surface = BALANCE.water.dr_surface - surface_runoff*1000; % in [mm] + % store remaining surface runoff + BALANCE.water.dr_surface = BALANCE.water.dr_surface - surface_runoff*1000; % in [mm] + BALANCE.water.dm_lacking = BALANCE.water.dm_lacking + lacking_water*1000; - end +end +% step 3: LUT update +% JAN:recalculate lookup tables when water content of freezing grid cells +% has changed (infiltrated cells can freeze --> LUT is updated) +if sum(double(wc~=GRID.soil.cT_water & T(GRID.soil.cT_domain)<=0))>0 %zzz jjj should be within if loop line 6...!? + disp('infiltration - reinitializing LUT - freezing of infiltrated cell(s)'); + GRID.soil.cT_water = wc; + GRID = initializeSoilThermalProperties(GRID, PARA); +end - % step 3: LUT update - % JAN:recalculate lookup tables when water content of freezing grid cells - % has changed (infiltrated cells can freeze --> LUT is updated) - if sum(double(wc~=GRID.soil.cT_water & T(GRID.soil.cT_domain)<=0))>0 - disp('infiltration - reinitializing LUT - freezing of infiltrated cell(s)'); - GRID.soil.cT_water = wc; - GRID = initializeSoilThermalProperties(GRID, PARA); - end end \ No newline at end of file diff --git a/modules/CryoGridInfiltrationUnfrozenSoil/bucketScheme.m b/modules/CryoGridInfiltrationUnfrozenSoil/bucketScheme.m index 0706de8..36680ef 100644 --- a/modules/CryoGridInfiltrationUnfrozenSoil/bucketScheme.m +++ b/modules/CryoGridInfiltrationUnfrozenSoil/bucketScheme.m @@ -1,25 +1,42 @@ -function [wc, surface_runoff]=bucketScheme(T, wc, dwc_dt, GRID, PARA, external_flux) +function [wc, surface_runoff, lacking_water]=bucketScheme(T, wc, dwc_dt, GRID, PARA, external_flux) T=T(GRID.soil.cT_domain); K_delta=GRID.general.K_delta(GRID.soil.cT_domain); %in m -porosity=1-GRID.soil.cT_mineral-GRID.soil.cT_organic; %in percent JAN: why not use GRID.soil.cT_natPor here? - -% A=sum(K_delta(1:30).*wc(1:30)+dwc_dt(1:30)) +porosity=1-GRID.soil.cT_mineral-GRID.soil.cT_organic; +soilType = GRID.soil.cT_soilType; + +% to be changed! +fieldCapacity = zeros(size(soilType)); +residualWaterContent = zeros(size(soilType)); +for i=1:size(PARA.soil.soilTypes,1) + fieldCapacity(soilType==i) = PARA.soil.soilTypes( i, 2 ); + residualWaterContent(soilType==i) = PARA.soil.soilTypes( i, 1 ); +end +lacking_water=0; i=1; -i_max=70; % maximum infiltration depth, must be defined somehow before +i_max=200; % maximum infiltration depth, must be defined somehow before, includes also water body on to of soil while T(i)>0 && i<=i_max - max_water=K_delta(i).*PARA.soil.fieldCapacity; %maximum amount of water (in m) that a grid cell can hold - actual_water=wc(i).*K_delta(i)+dwc_dt(i); % should be dwc (already multiplied with timestep) + max_water=K_delta(i).*fieldCapacity(i); %maximum amount of water (in m) that a grid cell can hold + min_water=K_delta(i).*residualWaterContent(i); %minimum amount of water which stays in a cell (independent of soil type, but should be if "freezing = drying") + + actual_water= max( min_water, wc(i).*K_delta(i)+dwc_dt(i) ); % should be dwc (already multiplied with timestep) %JAN: this violates the WB + + lacking_water = lacking_water + (actual_water - (wc(i).*K_delta(i)+dwc_dt(i) ) ); +% if abs(lacking_water)>1e-8 +% warning('bucketScheme - lacking water'); +% end + dwc_dt(i+1)=dwc_dt(i+1) + max(0, actual_water-max_water); %when excess water, move it to next grid cell - wc(i)=min(max_water, actual_water)./K_delta(i); + wc(i)=min(max_water, actual_water)./K_delta(i); i=i+1; end excess_water=dwc_dt(i)+external_flux; %add external flux +excess_water=excess_water - lacking_water; % remove potential mismatches (e.g. when evaporation in cell with low water content) -% B=sum(K_delta(1:30).*wc(1:30))+excess_water +lacking_water = -(excess_water<0)*excess_water; % this accounts for violations of the water balance i=i-1; while i>=1 && excess_water>0 @@ -31,8 +48,4 @@ i=i-1; end - -surface_runoff=excess_water; - -% C=sum(K_delta(1:30).*wc(1:30))+surface_runoff - +surface_runoff=(excess_water>0)*excess_water; % surface runoff only if excess_water>0 \ No newline at end of file diff --git a/modules/CryoGridInfiltrationUnfrozenSoil/capacityUnfrozen.m b/modules/CryoGridInfiltrationUnfrozenSoil/capacityUnfrozen.m index de13d05..689789b 100644 --- a/modules/CryoGridInfiltrationUnfrozenSoil/capacityUnfrozen.m +++ b/modules/CryoGridInfiltrationUnfrozenSoil/capacityUnfrozen.m @@ -4,5 +4,24 @@ c_o = PARA.constants.c_o; % 2.5*10^6; %[J/m�K] c_m = PARA.constants.c_m; % 2*10^6; %[J/m�K] -c_temp = (GRID.soil.cT_mineral+GRID.soil.cT_organic>1e-6) .* (GRID.soil.cT_mineral.*c_m + GRID.soil.cT_organic.*c_o + wc.*c_w) + ... - (GRID.soil.cT_mineral+GRID.soil.cT_organic<=1e-6) .* c_w ; % assume pure water for cells which consist partly of air and water +c_temp = (GRID.soil.cT_mineral.*c_m + GRID.soil.cT_organic.*c_o + wc.*c_w); + + +% adjust for free water +freeWater_domain = GRID.soil.cT_mineral+GRID.soil.cT_organic<1e-6; % cells without soil matrix material +c_temp(freeWater_domain) = c_w; % assume pure water for cells which consist partly of air and water + +% adjust for low soil matrix +lowMinOrg_domain = GRID.soil.cT_mineral+GRID.soil.cT_organic>=1e-6 & ~GRID.soil.excessGroundIce & ( GRID.soil.cT_actPor > GRID.soil.cT_natPor ); % cells with lower soil matrix material than 1-natPor + +if sum(lowMinOrg_domain)>0 + + water = wc; + mineral = GRID.soil.cT_mineral; + organic = GRID.soil.cT_organic; + matrix = mineral + organic; + mineral(lowMinOrg_domain) = mineral(lowMinOrg_domain) ./ matrix(lowMinOrg_domain) .* (1 - GRID.soil.cT_natPor(lowMinOrg_domain)) ; + organic(lowMinOrg_domain) = organic(lowMinOrg_domain) ./ matrix(lowMinOrg_domain) .* (1 - GRID.soil.cT_natPor(lowMinOrg_domain)) ; + water(lowMinOrg_domain) = min( water(lowMinOrg_domain), GRID.soil.cT_natPor(lowMinOrg_domain) ) ; + c_temp(lowMinOrg_domain) = water(lowMinOrg_domain) .* c_w + mineral(lowMinOrg_domain) .* c_m + organic(lowMinOrg_domain).* c_o; +end diff --git a/modules/CryoGridInfiltrationUnfrozenSoil/conductivityUnfrozen.m b/modules/CryoGridInfiltrationUnfrozenSoil/conductivityUnfrozen.m index 4ae6465..e48dd24 100644 --- a/modules/CryoGridInfiltrationUnfrozenSoil/conductivityUnfrozen.m +++ b/modules/CryoGridInfiltrationUnfrozenSoil/conductivityUnfrozen.m @@ -1,13 +1,32 @@ -function k_temp = capacityUnfrozen(wc,GRID, PARA) +function k_temp = conductivityUnfrozen(wc,GRID, PARA) ka = PARA.constants.k_a; %0.025; %air [Hillel(1982)] kw = PARA.constants.k_w; %0.57; %water [Hillel(1982)] ko = PARA.constants.k_o; %0.25; %organic [Hillel(1982)] km = PARA.constants.k_m; %soil.kh_bedrock; %mineral +k_freeWater = 5.0; % to ensure fast heat transfer + air=1-wc-GRID.soil.cT_mineral-GRID.soil.cT_organic; -k_temp = (GRID.soil.cT_mineral+GRID.soil.cT_organic>1e-6) .* (wc.* kw.^0.5 + GRID.soil.cT_mineral.* km.^0.5 + GRID.soil.cT_organic.* ko.^0.5 + air.* ka.^0.5).^2 + ... - (GRID.soil.cT_mineral+GRID.soil.cT_organic<=1e-6) .* kw; % assume pure water for cells which consist partly of air and water +k_temp = (wc.* kw.^0.5 + GRID.soil.cT_mineral.* km.^0.5 + GRID.soil.cT_organic.* ko.^0.5 + air.* ka.^0.5).^2 ; + +% adjust for free water +freeWater_domain = GRID.soil.cT_mineral+GRID.soil.cT_organic<1e-6; % cells without soil matrix material +k_temp(freeWater_domain) = k_freeWater; % assume pure water for cells which consist partly of air and water + +% adjust for low soil matrix +lowMinOrg_domain = GRID.soil.cT_mineral+GRID.soil.cT_organic>=1e-6 & ~GRID.soil.excessGroundIce & ( GRID.soil.cT_actPor > GRID.soil.cT_natPor ); % cells with lower soil matrix material than 1-natPor +if sum(lowMinOrg_domain)>0 + water = wc; + mineral = GRID.soil.cT_mineral; + organic = GRID.soil.cT_organic; + matrix = mineral + organic; + mineral(lowMinOrg_domain) = mineral(lowMinOrg_domain) ./ matrix(lowMinOrg_domain) .* (1 - GRID.soil.cT_natPor(lowMinOrg_domain)) ; + organic(lowMinOrg_domain) = organic(lowMinOrg_domain) ./ matrix(lowMinOrg_domain) .* (1 - GRID.soil.cT_natPor(lowMinOrg_domain)) ; + water(lowMinOrg_domain) = min( water(lowMinOrg_domain), GRID.soil.cT_natPor(lowMinOrg_domain) ) ; + air(lowMinOrg_domain) = 1-mineral(lowMinOrg_domain)-organic(lowMinOrg_domain)-water(lowMinOrg_domain); + k_temp(lowMinOrg_domain) = (water(lowMinOrg_domain) .* kw.^0.5 + mineral(lowMinOrg_domain) .* km.^0.5 + organic(lowMinOrg_domain).* ko.^0.5 + air(lowMinOrg_domain) .* ka.^0.5).^2 ; +end \ No newline at end of file diff --git a/modules/CryoGridInfiltrationUnfrozenSoil/getET_fraction.m b/modules/CryoGridInfiltrationUnfrozenSoil/getET_fraction.m index 40b496b..4e4399d 100644 --- a/modules/CryoGridInfiltrationUnfrozenSoil/getET_fraction.m +++ b/modules/CryoGridInfiltrationUnfrozenSoil/getET_fraction.m @@ -2,4 +2,4 @@ %fraction=double(T>0).*min(1, max(0, (wc-zero_reached)./(start_reduction-zero_reached))); -fraction=double(T>0).*(double(wc>=start_reduction) + double(wc0).*(double(wc>=start_reduction) + double(wc wiltingPoint and residualWC have no effect currently \ No newline at end of file diff --git a/modules/CryoGridInfiltrationUnfrozenSoil/getThermalPropertiesInfiltration.m b/modules/CryoGridInfiltrationUnfrozenSoil/getThermalPropertiesInfiltration.m index 86fc6ad..c42acbf 100644 --- a/modules/CryoGridInfiltrationUnfrozenSoil/getThermalPropertiesInfiltration.m +++ b/modules/CryoGridInfiltrationUnfrozenSoil/getThermalPropertiesInfiltration.m @@ -18,6 +18,10 @@ k_temp(GRID.soil.cT_domain) = double(T(GRID.soil.cT_domain)<=0).*k_temp(GRID.soil.cT_domain) + double(T(GRID.soil.cT_domain)>0).* conductivityUnfrozen(wc,GRID,PARA); lwc_temp(GRID.soil.cT_domain) = double(T(GRID.soil.cT_domain)<=0).*lwc_temp(GRID.soil.cT_domain) + double(T(GRID.soil.cT_domain)>0).* wc; + %-------- set higher conductivity for free water ---------------------- + % now done in conductivityUnfrozen + + %------- snow domain -------------------------------------------------- c_temp(GRID.snow.cT_domain) = cap_snow(GRID.snow.Snow_i(GRID.snow.cT_domain),... GRID.snow.Snow_w(GRID.snow.cT_domain),... @@ -30,6 +34,16 @@ lwc_temp(GRID.snow.cT_domain) = GRID.snow.Snow_w(GRID.snow.cT_domain)./GRID.general.K_delta(GRID.snow.cT_domain); + %tsvd------ lake domain -------------------------------------------------- + c_temp(GRID.lake.water.cT_domain) = PARA.constants.c_w; + k_temp(GRID.lake.water.cT_domain) = 5.46E-01 ; % in loadConstants.m 0.57 is used! FLAKE: Molecular heat conductivity of water [J m^{-1} s^{-1} K^{-1}] zzz + lwc_temp(GRID.lake.water.cT_domain) = 1.; + + c_temp(GRID.lake.ice.cT_domain) = PARA.constants.c_i; + k_temp(GRID.lake.ice.cT_domain) = 2.29; ; % in loadConstants.m 2.2 is used! FLAKE: Molecular heat conductivity of ice [J m^{-1} s^{-1} K^{-1}] zzz + lwc_temp(GRID.lake.ice.cT_domain) = 0.; + + %------- interpolate conductivity to K-grid --------------------------- k_eff(2:end-1) = GRID.general.K_delta(1:end-1)./(2.*GRID.general.cT_delta) .* (1./k_temp(1:end-1)).^2 ... + GRID.general.K_delta(2:end) ./(2.*GRID.general.cT_delta) .* (1./k_temp(2:end)).^2; diff --git a/modules/CryoGridInfiltrationUnfrozenSoil/surfaceEnergyBalanceInfiltration.m b/modules/CryoGridInfiltrationUnfrozenSoil/surfaceEnergyBalanceInfiltration.m index 5515e2b..5b4f740 100644 --- a/modules/CryoGridInfiltrationUnfrozenSoil/surfaceEnergyBalanceInfiltration.m +++ b/modules/CryoGridInfiltrationUnfrozenSoil/surfaceEnergyBalanceInfiltration.m @@ -15,81 +15,97 @@ %______here SW radiation is calculated_____________________________________ dE_dt=GRID.general.cT_grid.*0; Qsolar=GRID.general.cT_grid.*0; - -dE_dt(GRID.air.cT_domain_lb+1)=(1-PARA.surf.albedo).*FORCING.i.Sin; +%tsvd + Sin_water=0; +%tsvd dE_dt(GRID.air.cT_domain_lb+1)=(1-PARA.surf.albedo).*FORCING.i.Sin; + dE_dt(GRID.air.cT_domain_lb+1,1)=(1-PARA.surf.albedo).*FORCING.i.Sin; %------ snow surface (solid state green house effect) --------------------- -if ~isempty(GRID.snow.cT_domain_ub) - beta=PARA.snow.extinction; +if ~isempty(GRID.snow.cT_domain_ub) + beta=PARA.snow.extinction; Qsolar(GRID.snow.cT_domain_ub:GRID.snow.cT_domain_lb+1) = dE_dt(GRID.snow.cT_domain_ub) .* exp(-beta.*(GRID.general.K_grid(GRID.snow.cT_domain_ub:GRID.snow.cT_domain_lb+1)-GRID.general.K_grid(GRID.snow.cT_domain_ub))); dE_dt(GRID.snow.cT_domain_ub:GRID.snow.cT_domain_lb) = -Qsolar(GRID.snow.cT_domain_ub+1:GRID.snow.cT_domain_lb+1) + Qsolar(GRID.snow.cT_domain_ub:GRID.snow.cT_domain_lb); %put the rest to cell below snow - dE_dt(GRID.snow.cT_domain_lb+1) = Qsolar(GRID.snow.cT_domain_lb+1); + dE_dt(GRID.snow.cT_domain_lb+1) = Qsolar(GRID.snow.cT_domain_lb+1); end -% JAN : here a modification for water bodies would be needed (extinction), -% but this would probably make no difference for summer due to mixing, -% maybe different in winter/spring - - +%tsvd also consider LAKE case +%------- ice surface (solid state green house effect) --------------------- +if ~isempty(GRID.lake.ice.cT_domain_ub) +%%%if GRID.lake.ice.z_ice>0 + beta=PARA.ice.extinction; +% if GRID.lake.ice.melt_flag +% %increse light extinction coeff under melt conditions +% beta=PARA.ice.extinction.*2; +% end + Qsolar(GRID.lake.ice.cT_domain_ub:GRID.lake.ice.cT_domain_lb+1) = dE_dt(GRID.lake.ice.cT_domain_ub) .* exp(-beta.*(GRID.general.K_grid(GRID.lake.ice.cT_domain_ub:GRID.lake.ice.cT_domain_lb+1)-GRID.general.K_grid(GRID.lake.ice.cT_domain_ub))); + dE_dt(GRID.lake.ice.cT_domain_ub:GRID.lake.ice.cT_domain_lb) = -Qsolar(GRID.lake.ice.cT_domain_ub+1:GRID.lake.ice.cT_domain_lb+1) + Qsolar(GRID.lake.ice.cT_domain_ub:GRID.lake.ice.cT_domain_lb); + %put the rest to cell below ice cover + dE_dt(GRID.lake.ice.cT_domain_lb+1) = Qsolar(GRID.lake.ice.cT_domain_lb+1); +end +%------- water domain ----------------------------------------------------- +if ~isempty(GRID.lake.water.cT_domain_ub) + beta=PARA.water.extinction; + Qsolar(GRID.lake.water.cT_domain_ub:GRID.lake.water.cT_domain_lb+1) = dE_dt(GRID.lake.water.cT_domain_ub) .* exp(-beta.*(GRID.general.K_grid(GRID.lake.water.cT_domain_ub:GRID.lake.water.cT_domain_lb+1)-GRID.general.K_grid(GRID.lake.water.cT_domain_ub))); + dE_dt(GRID.lake.water.cT_domain_ub :GRID.lake.water.cT_domain_lb) = -Qsolar(GRID.lake.water.cT_domain_ub+1:GRID.lake.water.cT_domain_lb+1) + Qsolar(GRID.lake.water.cT_domain_ub:GRID.lake.water.cT_domain_lb); + %put the rest to cell below water body + dE_dt(GRID.lake.water.cT_domain_lb+1) = Qsolar(GRID.lake.water.cT_domain_lb+1); + %SW output for FLAKE radiation scheme + Sin_water = Qsolar(GRID.lake.water.cT_domain_ub); +end %__________________________________________________________________________ Sout = PARA.surf.albedo*FORCING.i.Sin; Lout = PARA.surf.epsilon.*sigma.*(T(GRID.air.cT_domain_lb+1)+273.15).^4 + (1-PARA.surf.epsilon).*FORCING.i.Lin; Qnet = FORCING.i.Sin-Sout + FORCING.i.Lin - Lout ; +%calculate ET - -%calculate ET -if PARA.modules.infiltration - - %snow cover or uppermost grid cell frozen --> no ET ; JAN: this includes the case of a frozen water body - if ~isempty(GRID.snow.cT_domain_ub) || T(GRID.soil.cT_domain_ub)<=0 + %snow cover or uppermost grid cell frozen, or lake ice --> no ET +%tsvd LAKE case added +if PARA.modules.infiltration + if ~isempty(GRID.snow.cT_domain_ub) || T(GRID.soil.cT_domain_ub)<=0 || ~isempty(GRID.lake.ice.cT_domain_ub) % water replaced by ice %snow cover or uppermost grid cell frozen --> no ET tsvd: LAKE case added +%%% if ~isempty(GRID.snow.cT_domain_ub) || T(GRID.soil.cT_domain_ub)<=0 || GRID.lake.ice.z_ice>0 % snow or lake ice cover, or uppermost grid cell frozen --> no ET zzz case of flooding of basin not captured correctly... Qe=real(Q_eq(FORCING.i.wind, z, PARA.surf.z0, FORCING.i.q, FORCING.i.Tair, T(GRID.air.cT_domain_lb+1), Lstar, PARA.surf.rs, FORCING.i.p, PARA)); - % unfrozen water body at surface - elseif GRID.lake.unfrozenWaterSurface + % unfrozen water body at surface + %tsvd elseif GRID.lake.unfrozenWaterSurface + %lll elseif ~isempty(GRID.lake.water.cT_domain_ub) + elseif GRID.lake.water.cT_domain(GRID.air.cT_domain_lb+1)==1 % water surface Qe=real(Q_eq(FORCING.i.wind, z, PARA.surf.z0, FORCING.i.q, FORCING.i.Tair, T(GRID.air.cT_domain_lb+1), Lstar, PARA.surf.rs, FORCING.i.p, PARA)); - dwc_dt(1)=-Qe./L; %in m water per sec, this can be evaporation or condensation - - % JAN: this is the "default" case of an unfrozen soil surface + %lll dwc_dt(1)=-Qe./L; %in m water per sec, this can be evaporation or condensation zzz dwc_dt is for soil domain!! need to adapt... + % put here lake bucket for calculating lake level change zzz + % unfrozen soil surface else Qe_pot=real(Q_eq(FORCING.i.wind, z, PARA.surf.z0, FORCING.i.q, FORCING.i.Tair, T(GRID.air.cT_domain_lb+1), Lstar, 0, FORCING.i.p, PARA)); %potential ET - if Qe_pot>0 - fraction_T=getET_fraction(T(GRID.soil.cT_domain_ub:GRID.soil.cT_domain_ub+GRID.soil.T_lb-1), wc(1:GRID.soil.T_lb), PARA.soil.fieldCapacity, PARA.soil.wiltingPoint); +%tsvd if Qe_pot>0 + if Qe_pot>0 && GRID.soil.cT_domain(GRID.air.cT_domain_lb+1)==1 + fraction_T=getET_fraction(T(GRID.soil.cT_domain_ub:GRID.soil.cT_domain_ub+GRID.soil.T_lb-1), wc(1:GRID.soil.T_lb), PARA.soil.fieldCapacity, PARA.soil.wiltingPoint); %zzz -1? fraction_E=getET_fraction(T(GRID.soil.cT_domain_ub:GRID.soil.cT_domain_ub+GRID.soil.E_lb-1), wc(1:GRID.soil.E_lb), PARA.soil.fieldCapacity, PARA.soil.residualWC); fraction_ET = fraction_T.*PARA.soil.ratioET; fraction_ET(1:GRID.soil.E_lb) = fraction_ET(1:GRID.soil.E_lb) + fraction_E.*(1-PARA.soil.ratioET); Qe=sum(fraction_ET.*GRID.general.K_delta(GRID.soil.cT_domain_ub:GRID.soil.cT_domain_ub+GRID.soil.T_lb-1))./sum(GRID.general.K_delta(GRID.soil.cT_domain_ub:GRID.soil.cT_domain_ub+GRID.soil.T_lb-1)).*Qe_pot; fraction_ET=fraction_ET.*GRID.general.K_delta(GRID.soil.cT_domain_ub:GRID.soil.cT_domain_ub+GRID.soil.T_lb-1)./sum(fraction_ET.*GRID.general.K_delta(GRID.soil.cT_domain_ub:GRID.soil.cT_domain_ub+GRID.soil.T_lb-1)); - % sum(fraction_ET) is always 1 + % sum(fraction_ET) is always 1 dwc_dt(1:GRID.soil.T_lb)=-Qe./L.*fraction_ET; %in m water per sec else %condensation Qe=Qe_pot; dwc_dt(1)=-Qe./L; %in m water per sec, put everything in uppermost grid cell end end + else % this is identical to case with snow cover or frozen ground Qe=real(Q_eq(FORCING.i.wind, z, PARA.surf.z0, FORCING.i.q, FORCING.i.Tair, T(GRID.air.cT_domain_lb+1), Lstar, PARA.surf.rs, FORCING.i.p, PARA)); end %ground heat flux Qg = Qnet-Qh-Qe; -%surface heat flux (into upper cell, ground heat flux regards also other -%grid cells, should be identical if no snow cover and no evapotranspiration -%occur +%surface heat flux (into upper cell, ground heat flux regards also other grid cells, should be identical if no snow cover and no evapotranspiration occur dE_dt(GRID.air.cT_domain_lb+1) = dE_dt(GRID.air.cT_domain_lb+1) ... - + PARA.surf.epsilon.*FORCING.i.Lin ... - - PARA.surf.epsilon.*sigma.*(T(GRID.air.cT_domain_lb+1)+273.15).^4 ... - - Qh - Qe; % Qe positive: cooling of soil => evaporation/subl. => loss of SWE - - - -% fluxes are in [ W / m^2 ] -SEB.Qsurf = dE_dt(GRID.air.cT_domain_lb+1); - -% if abs( SEB.Qsurf-Qg ) > 1e-6 -% warning ( ' Qsurf != Qg ' ); -% end - + + PARA.surf.epsilon.*FORCING.i.Lin ... + - PARA.surf.epsilon.*sigma.*(T(GRID.air.cT_domain_lb+1)+273.15).^4 ... + - Qh - Qe; % Qe positive: cooling of soil => evaporation/subl. => loss of SWE + +% fluxes are in [ W / m^2 ] +SEB.Qsurf = dE_dt(GRID.air.cT_domain_lb+1); SEB.dE_dt_SEB = dE_dt; SEB.Qnet = Qnet; SEB.Qh = Qh; @@ -97,5 +113,12 @@ SEB.Qg = Qg; SEB.Sout = Sout; SEB.Lout = Lout; +%tsvd +SEB.Sin_water=Sin_water; + +assert(~isnan(SEB.Qe),'Qe is NAN!') +assert(~isnan(SEB.Qh),'Qh is NAN!') +assert(~isnan(SEB.Qnet),'Qnet is NAN!') + +end - \ No newline at end of file diff --git a/modules/CryoGridInfiltrationUnfrozenSoil/updateGRID_infiltration.m b/modules/CryoGridInfiltrationUnfrozenSoil/updateGRID_infiltration.m index 1bc8a06..210cca7 100644 --- a/modules/CryoGridInfiltrationUnfrozenSoil/updateGRID_infiltration.m +++ b/modules/CryoGridInfiltrationUnfrozenSoil/updateGRID_infiltration.m @@ -1,51 +1,59 @@ function [ wc, GRID, surface_runoff ] = updateGRID_infiltration(wc, GRID, PARA, surface_runoff) - %%% step 2: GRID update - %%% TODO: add a function updateGRID_infiltration - - soilGRIDsizeOld = sum(GRID.soil.cT_domain); - - %%% step 2a) remove cells filled with air (e.g. due to evaporation - %%% of uppermost grid cell ) - while (GRID.soil.cT_mineral(1)+GRID.soil.cT_organic(1)+wc(1)<=0) - disp('infiltration - update GRID - removing air cell') - - % adjust air and soil domains and boundaries - GRID.air.cT_domain(GRID.soil.cT_domain_ub)=1; - GRID.air.K_domain(GRID.soil.K_domain_ub)=1; - GRID.air.cT_domain_lb=GRID.air.cT_domain_lb+1; - GRID.air.K_domain_lb=GRID.air.K_domain_lb+1; - GRID.soil.cT_domain(GRID.soil.cT_domain_ub)=0; - GRID.soil.K_domain(GRID.soil.K_domain_ub)=0; - GRID.soil.cT_domain_ub=GRID.soil.cT_domain_ub+1; - GRID.soil.K_domain_ub=GRID.soil.K_domain_ub+1; - GRID.soil.soilGrid(1)=[]; - - wc(1)=[]; +% create log files before run execution manually... - GRID.soil.cT_organic(1)=[]; - GRID.soil.cT_natPor(1)=[]; - GRID.soil.cT_mineral(1)=[]; - GRID.soil.cT_soilType(1)=[]; - % K fields are not used currently -% GRID.soil.K_water(1)=[]; -% GRID.soil.K_organic(1)=[]; -% GRID.soil.K_mineral(1)=[]; -% GRID.soil.K_soilType(1)=[]; - GRID.soil.excessGroundIce(1)=[]; +% fileID = fopen('exp.txt','w'); +% logfile=['log_updateGridInfil_',num2str(labindex)]; + +%%% step 2: GRID update + %%% TODO: add a function updateGRID_infiltration + soilGRIDsizeOld = sum(GRID.soil.cT_domain); %zzz use grid + lake? + + %%% step 2a) remove cells filled with air (e.g. due to evaporation of uppermost grid cell ) + + if(isempty(GRID.lake.water.cT_domain_ub)) %tsvd only update grid when no lake exists zzz include case when lake level is dynamic! + while (GRID.soil.cT_mineral(1)+GRID.soil.cT_organic(1)+wc(1)<=0) +%ttt disp('infiltration - update GRID - updating air cell') +% if labindex==1 +% disp('fileID1') +% fileID1 +% fprintf(fileID1,['infiltration - update GRID - updating air cell for worker ',num2str(labindex)]) +% elseif labindex==2 +% fprintf(fileID2,['infiltration - update GRID - updating air cell for worker ',num2str(labindex)]) +% end + % adjust air and soil domains and boundaries zzz check how to adapt for lake... + GRID.air.cT_domain(GRID.soil.cT_domain_ub)=1; + GRID.air.K_domain(GRID.soil.K_domain_ub)=1; + GRID.air.cT_domain_lb=GRID.air.cT_domain_lb+1; + GRID.air.K_domain_lb=GRID.air.K_domain_lb+1; + GRID.soil.cT_domain(GRID.soil.cT_domain_ub)=0; + GRID.soil.K_domain(GRID.soil.K_domain_ub)=0; + GRID.soil.cT_domain_ub=GRID.soil.cT_domain_ub+1; + GRID.soil.K_domain_ub=GRID.soil.K_domain_ub+1; + GRID.soil.soilGrid(1)=[]; + + wc(1)=[]; + + GRID.soil.cT_organic(1)=[]; + GRID.soil.cT_natPor(1)=[]; + GRID.soil.cT_actPor(1)=[]; + GRID.soil.cT_mineral(1)=[]; + GRID.soil.cT_soilType(1)=[]; + + GRID.soil.excessGroundIce(1)=[]; + end end - + %%% step 2b) ponding of surface runoff below water table while surface_runoff>1e-6 && ... % not >0 as sometimes numerical errors occur during calculation of surface_runoff - GRID.general.K_grid(GRID.soil.K_domain_ub)>PARA.soil.waterTable %&& ... + PARA.location.initial_altitude-GRID.general.K_grid(GRID.soil.cT_domain_ub)=1 % this prevents a bug for very small %surface_runoff when upper cell not filled // but this %does not allow ponding on top of actual soil disp('infiltration - update GRID - ponding of water below water table') - h = GRID.general.K_grid(GRID.soil.K_domain_ub)-PARA.soil.waterTable; % this is guruanteed to be >0 - + h = PARA.location.absolute_maxWater_altitude - ( PARA.location.initial_altitude - GRID.general.K_grid(GRID.soil.cT_domain_ub) ) ; % this is guruanteed to be >0 % create new water cell / change GRID domains GRID.soil.cT_domain(GRID.air.cT_domain_lb)=1; @@ -58,7 +66,6 @@ GRID.air.K_domain_lb=GRID.air.K_domain_lb-1; % fill new water cell - %cellSize = GRID.general.K_delta(GRID.soil.cT_domain_ub); cellSize = PARA.technical.waterCellSize; waterAdded = min( [surface_runoff, cellSize, h] ); % add water until water table is reached or surface_runoff "empty" wc = [ waterAdded./cellSize ; wc ]; @@ -67,13 +74,10 @@ % update remaining soil fields with exception of cT_water GRID.soil.cT_organic = [ 0 ; GRID.soil.cT_organic ]; GRID.soil.cT_natPor = [ GRID.soil.cT_natPor(1); GRID.soil.cT_natPor ]; % take natPor of cell below + GRID.soil.cT_actPor = [ 1; GRID.soil.cT_actPor ]; % set actual porosity to 1 GRID.soil.cT_mineral = [ 0 ; GRID.soil.cT_mineral ]; - GRID.soil.cT_soilType = [ 1; GRID.soil.cT_soilType]; % assume sand as soil type for water cell - % K fields are not used currently - %GRID.soil.K_water = [ wc(1); GRID.soil.K_water ]; - %GRID.soil.K_organic = [ 0 ; GRID.soil.K_organic ]; - %GRID.soil.K_mineral = [ 0 ; GRID.soil.K_mineral ]; - %GRID.soil.K_soilType = [ GRID.soil.K_soilType(1); GRID.soil.K_soilType]; + GRID.soil.cT_soilType = [ 3; GRID.soil.cT_soilType]; % soilType 3 = pond (sand freeze curve, field cap =0 ) + GRID.soil.excessGroundIce = [ 0 ; GRID.soil.excessGroundIce ]; % update GRID spacings @@ -83,19 +87,33 @@ GRID.general.cT_delta = (- GRID.general.cT_grid(1:end-1,1)+ GRID.general.cT_grid(2:end,1)); GRID.general.K_delta = (- GRID.general.K_grid(1:end-1,1)+ GRID.general.K_grid(2:end,1)); GRID.soil.soilGrid = [ GRID.general.K_grid(GRID.soil.cT_domain_ub) ; GRID.soil.soilGrid ]; - - end - - + end - %%% step 2c) check if soil/air domains changed --> LUT update +% %%% step 2c) check if soil/air domains changed --> LUT update +% soilGRIDsizeNew = sum(GRID.soil.cT_domain); +% if soilGRIDsizeOld~=soilGRIDsizeNew +% disp(['infiltration - reinitializing LUT - soil/air domains changed on worker ',num2str(labindex)]); +% GRID.soil.cT_water = wc; +% GRID = initializeSoilThermalProperties(GRID, PARA); +% end + +%tsvd %%% step 2c) check if soil/air domains changed --> LUT update soilGRIDsizeNew = sum(GRID.soil.cT_domain); - if soilGRIDsizeOld~=soilGRIDsizeNew - disp('infiltration - reinitializing LUT - soil/air domains changed'); + cellsChanged = soilGRIDsizeNew - soilGRIDsizeOld; + if cellsChanged > 0 + disp('infiltration - reinitializing LUT - new water cell(s)'); GRID.soil.cT_water = wc; GRID = initializeSoilThermalProperties(GRID, PARA); + elseif cellsChanged < 0 + disp('infiltration - shortening LUT - removed water cell(s)'); + GRID.soil.cT_water(1) = []; + GRID.soil.cT_frozen(1) = []; + GRID.soil.cT_thawed(1) = []; + GRID.soil.K_frozen(1) = []; + GRID.soil.K_thawed(1) = []; + GRID.soil.conductivity(1,:) = []; + GRID.soil.capacity(1,:) = []; + GRID.soil.liquidWaterContent(1,:) = []; end - - - + end \ No newline at end of file diff --git a/modules/cryoGridExcessIce/conductivityFreeWater.m b/modules/cryoGridExcessIce/conductivityFreeWater.m new file mode 100644 index 0000000..31ff8b8 --- /dev/null +++ b/modules/cryoGridExcessIce/conductivityFreeWater.m @@ -0,0 +1,9 @@ +function k_old=conductivityFreeWater(k_old, T, soilWater) + +k_freeWater=5; %set conductivity for mobile water to 5 +i=1; + +while T(i)>0 && soilWater(i)>=1 + k_old(i)=k_freeWater; + i=i+1; +end \ No newline at end of file diff --git a/modules/cryoGridExcessIce/excessGroundIce.m b/modules/cryoGridExcessIce/excessGroundIce.m new file mode 100644 index 0000000..32e6732 --- /dev/null +++ b/modules/cryoGridExcessIce/excessGroundIce.m @@ -0,0 +1,8 @@ +function [GRID, PARA] = excessGroundIce(T, GRID, PARA) + +if ~isempty(PARA.soil.mobileWaterDomain) && (sum(double(T(GRID.soil.cT_domain)>0 & GRID.soil.excessGroundIce==1))~=0) && isempty(GRID.snow.cT_domain_ub) + disp('excess ice thawing'); + GRID.soil.excessGroundIce = GRID.soil.excessGroundIce==1 & T(GRID.soil.cT_domain)<=0; %remove the thawed cell from the list + [GRID meltwaterGroundIce PARA] = excessGroundIceThaw4(T, GRID, PARA); %meltwaterGroundIce could be read out, but is not yet implemented + GRID = updateGRID_excessice(GRID); +end \ No newline at end of file diff --git a/modules/cryoGridExcessIce/excessGroundIceThaw4.m b/modules/cryoGridExcessIce/excessGroundIceThaw4.m new file mode 100644 index 0000000..8ac0650 --- /dev/null +++ b/modules/cryoGridExcessIce/excessGroundIceThaw4.m @@ -0,0 +1,140 @@ +function [GRID, meltwaterGroundIce, PARA]=excessGroundIceThaw4(T, GRID, PARA) + +%disp('rearranging grid cells due to ground ice thaw') + +waterLevel=PARA.soil.waterTable; %remove supersaturation only when there is no snow on top of soil!!!!!!! + + +mineral=GRID.general.K_delta(GRID.soil.cT_domain).*GRID.soil.cT_mineral; %calculates amounts in [m] +organic=GRID.general.K_delta(GRID.soil.cT_domain).*GRID.soil.cT_organic; +water=GRID.general.K_delta(GRID.soil.cT_domain).*GRID.soil.cT_water; +natPor=GRID.general.K_delta(GRID.soil.cT_domain).*GRID.soil.cT_natPor; + +cT_grid=GRID.general.cT_grid(GRID.soil.cT_domain); +K_delta=GRID.general.K_delta(GRID.soil.cT_domain); + + +mobileWater = double(T(GRID.soil.cT_domain)>0) .* (water-natPor) .* double(water>natPor); %zzz +[startCell ~]= LayerIndex(mobileWater~=0); %this is faster + +%move solids down +for i=startCell:-1:1 + F_solid_down=K_delta(i)-mineral(i)-organic(i)-natPor(i); + j=i-1; + while j>0 && F_solid_down>0 + mineralDown = min(mineral(j), mineral(j)./(mineral(j)+organic(j)).*F_solid_down); + organicDown = min(organic(j), organic(j)./(mineral(j)+organic(j)).*F_solid_down); + mineral(i)=mineral(i)+mineralDown; + organic(i)=organic(i)+organicDown; + mineral(j)=mineral(j)-mineralDown; + organic(j)=organic(j)-organicDown; + F_solid_down=F_solid_down-mineralDown-organicDown; + j=j-1; + end +end + +%adjust the natural porosity +natPor(1:startCell)=K_delta(1:startCell)-mineral(1:startCell)-organic(1:startCell); + +%move water up +mobileWater=0; +for i=startCell:-1:1 + totalWater=water(i)+mobileWater; + mobileWater=totalWater-natPor(i); + mobileWater=max(0,mobileWater); + water(i)=totalWater-mobileWater; +end + +%clean up grid cells with non-zero+non-unity water content in domains without soil matrix +mobileWater=0; +for i=1:startCell + if mineral(i)+organic(i)==0 + mobileWater=mobileWater+water(i); + water(i)=0; + end +end +for i=startCell:-1:1 + if mineral(i)+organic(i)==0 + water(i)=min(K_delta(i), mobileWater); + mobileWater=mobileWater-water(i); + water(i)=round(water(i)./K_delta(i)).*K_delta(i); %this violates the water balance, but ensures that no grid cells with partly water and partly air can exist; zzz + end +end + + +GRID.soil.cT_mineral=mineral./K_delta; +GRID.soil.cT_organic=organic./K_delta; +GRID.soil.cT_water=water./K_delta; +GRID.soil.cT_natPor=natPor./K_delta; +test=mineral+water+organic; +GRID.soil.cT_soilType(water(:,1)==1,1)=1; %sets sand freeze curve for all water grid cells (comment: find was replaced!!!) + +GRID.soil.K_mineral(1)=GRID.soil.cT_mineral(1); +GRID.soil.K_mineral(2:startCell+1)=(GRID.soil.cT_mineral(2:startCell+1)+GRID.soil.cT_mineral(1:startCell))/2 ; +GRID.soil.K_organic(1)=GRID.soil.cT_organic(1); +GRID.soil.K_organic(2:startCell+1)=(GRID.soil.cT_organic(2:startCell+1)+GRID.soil.cT_organic(1:startCell))/2 ; +GRID.soil.K_water(1)=GRID.soil.cT_water(1); +GRID.soil.K_water(2:startCell+1)=(GRID.soil.cT_water(2:startCell+1)+GRID.soil.cT_water(1:startCell))/2 ; +% GRID.soil.K_natPor(1)=GRID.soil.cT_natPor(1); +% GRID.soil.K_natPor(2:startCell+1)=(GRID.soil.cT_natPor(2:startCell+1)+GRID.soil.cT_natPor(1:startCell))/2 ; +GRID.soil.K_soilType(1)=GRID.soil.cT_soilType(1); +GRID.soil.K_soilType(2:startCell+1)=round((GRID.soil.cT_soilType(2:startCell+1)+GRID.soil.cT_soilType(1:startCell))/2) ; + +meltwaterGroundIce=0; + +while (GRID.soil.cT_mineral(1)+GRID.soil.cT_organic(1)+GRID.soil.cT_water(1)==0) || (GRID.soil.cT_water(1)==1 && GRID.general.K_grid(GRID.soil.K_domain_ub)=PARA.soil.mobileWaterDomain(1) & GRID.general.cT_grid(GRID.soil.cT_domain)<=PARA.soil.mobileWaterDomain(2); + GRID.soil.cT_natPor(~mobileWaterDomain)=GRID.soil.cT_water(~mobileWaterDomain); % why??? +end + +% lower the water table if air is present above the excess ground ice +GRID.soil.excessGroundIce = GRID.soil.cT_water>GRID.soil.cT_natPor; +firstCellExcessIce=find(GRID.soil.excessGroundIce(:,1)==1, 1, 'first'); + +if ~isempty(firstCellExcessIce) && firstCellExcessIce>1 + PARA.soil.waterTable=max(PARA.soil.waterTable,... + sum((1 - GRID.soil.cT_water(1:firstCellExcessIce-1) - GRID.soil.cT_mineral(1:firstCellExcessIce-1) - GRID.soil.cT_organic(1:firstCellExcessIce-1))... + .*GRID.general.K_delta(GRID.soil.cT_domain_ub:GRID.soil.cT_domain_ub+firstCellExcessIce-2))); +end \ No newline at end of file diff --git a/modules/cryoGridExcessIce/initializeExcessIce2.m b/modules/cryoGridExcessIce/initializeExcessIce2.m new file mode 100644 index 0000000..cf3aea8 --- /dev/null +++ b/modules/cryoGridExcessIce/initializeExcessIce2.m @@ -0,0 +1,23 @@ +function [GRID,PARA] = initializeExcessIce2(GRID,PARA) + +GRID.soil.excessGroundIce = GRID.soil.cT_water>GRID.soil.cT_natPor; + + +%JAN: I think modifying cT_natPor is not necessary as long as it is initalized correctly in the PARA.soilLayerProperties struct +% set the natural porosity that only water in "mobilewaterDomain" is mobile +%if isempty(PARA.soil.mobileWaterDomain) +% GRID.soil.cT_natPor=GRID.soil.cT_water; % why??? +%else +% mobileWaterDomain = GRID.general.cT_grid(GRID.soil.cT_domain)>=PARA.soil.mobileWaterDomain(1) & GRID.general.cT_grid(GRID.soil.cT_domain)<=PARA.soil.mobileWaterDomain(2); +% GRID.soil.cT_natPor(~mobileWaterDomain)=GRID.soil.cT_water(~mobileWaterDomain); % why??? +%end + +%JAN: I commented this out, as I am not sure why this modification of +%waterTable is necessary +% lower the water table if air is present above the excess ground ice +%firstCellExcessIce=find(GRID.soil.excessGroundIce(:,1)==1, 1, 'first'); +%if ~isempty(firstCellExcessIce) && firstCellExcessIce>1 +% PARA.soil.waterTable=max(PARA.soil.waterTable,... +% sum((1 - GRID.soil.cT_water(1:firstCellExcessIce-1) - GRID.soil.cT_mineral(1:firstCellExcessIce-1) - GRID.soil.cT_organic(1:firstCellExcessIce-1))... +% .*GRID.general.K_delta(GRID.soil.cT_domain_ub:GRID.soil.cT_domain_ub+firstCellExcessIce-2))); +%end \ No newline at end of file diff --git a/modules/cryoGridExcessIce/mixingWaterBody.m b/modules/cryoGridExcessIce/mixingWaterBody.m new file mode 100644 index 0000000..2790ebb --- /dev/null +++ b/modules/cryoGridExcessIce/mixingWaterBody.m @@ -0,0 +1,14 @@ +function [T] = mixingWaterBody(T, GRID) + + % mixing of temperatures in unfrozen water domain + if GRID.soil.cT_organic(1)+GRID.soil.cT_mineral(1)<=1e-6 && T(GRID.soil.cT_domain_ub)>0 + unfrozenWaterBody = GRID.soil.cT_organic+GRID.soil.cT_mineral<=1e-6 & T(GRID.soil.cT_domain)>0; + waterCells = sum( unfrozenWaterBody ); + if waterCells > 1 + Tav = sum( T(GRID.soil.cT_domain_ub:GRID.soil.cT_domain_ub+waterCells-1) .* GRID.general.K_delta(GRID.soil.cT_domain_ub:GRID.soil.cT_domain_ub+waterCells-1) ) ... + ./ sum( GRID.general.K_delta(GRID.soil.cT_domain_ub:GRID.soil.cT_domain_ub+waterCells-1) ) ; + T(GRID.soil.cT_domain_ub:GRID.soil.cT_domain_ub+waterCells-1) = Tav; + end + end + +end \ No newline at end of file diff --git a/modules/cryoGridExcessIce/moveWater2Top.m b/modules/cryoGridExcessIce/moveWater2Top.m new file mode 100644 index 0000000..64c860a --- /dev/null +++ b/modules/cryoGridExcessIce/moveWater2Top.m @@ -0,0 +1,44 @@ +function [cT_water, cT_mineral, cT_organic, K_water, K_mineral, K_organic]=moveWater2Top(T, cT_water, cT_mineral, cT_organic, cT_natPor, K_water, K_mineral, K_organic, K_delta, cT_firstGroundCell) + superSaturatedCells=find(cT_water(:,1)>cT_natPor(:,1) & T(:,1)>0); + cellsChanged=superSaturatedCells; + for i=1:size(superSaturatedCells,1) + mobileWater=(cT_water(superSaturatedCells(i),1)-cT_natPor(superSaturatedCells(i),1)).*K_delta(superSaturatedCells(i),1); %in [m] + j=cT_firstGroundCell; + while mobileWater>0 && j<=size(cT_water,1) + waterAdded=min(mobileWater, K_delta(j,1).*(1-cT_water(j,1))); + cT_water(j,1)=cT_water(j,1)+waterAdded./K_delta(j,1); + cT_water(superSaturatedCells(i),1)= cT_water(superSaturatedCells(i),1)-waterAdded./K_delta(superSaturatedCells(i),1); + if cT_organic(j,1)+cT_mineral(j,1)>0 + organicSubtracted=waterAdded.*cT_organic(j,1)./(cT_organic(j,1)+cT_mineral(j,1)); + mineralSubtracted=waterAdded.*cT_mineral(j,1)./(cT_organic(j,1)+cT_mineral(j,1)); + else + organicSubtracted=0; + mineralSubtracted=0; + end + cT_organic(j,1)=cT_organic(j,1)-organicSubtracted./K_delta(j,1); + cT_organic(superSaturatedCells(i),1)= cT_organic(superSaturatedCells(i),1)+organicSubtracted./K_delta(superSaturatedCells(i),1); + + cT_mineral(j,1)=cT_mineral(j,1)-mineralSubtracted./K_delta(j,1); + cT_mineral(superSaturatedCells(i),1)= cT_mineral(superSaturatedCells(i),1)+mineralSubtracted./K_delta(superSaturatedCells(i),1); + + mobileWater=mobileWater-waterAdded; + cellsChanged=[cellsChanged; j]; + j=j+1; + end + end + + for i=cellsChanged' + if i==cT_firstGroundCell + K_water(i,1)=cT_water(i,1); + K_mineral(i,1)=cT_mineral(i,1); + K_organic(i,1)=cT_organic(i,1); + else + K_water(i,1)=(cT_water(i-1,1)+cT_water(i,1))/2; + K_mineral(i,1)=(cT_mineral(i-1,1)+cT_mineral(i,1))/2; + K_organic(i,1)=(cT_organic(i-1,1)+cT_organic(i,1))/2; + + K_water(i+1,1)=(cT_water(i+1,1)+cT_water(i,1))/2; + K_mineral(i+1,1)=(cT_mineral(i+1,1)+cT_mineral(i,1))/2; + K_organic(i+1,1)=(cT_organic(i+1,1)+cT_organic(i,1))/2; + end + end \ No newline at end of file diff --git a/modules/cryoGridExcessIce/removeWater.m b/modules/cryoGridExcessIce/removeWater.m new file mode 100644 index 0000000..e2e6a78 --- /dev/null +++ b/modules/cryoGridExcessIce/removeWater.m @@ -0,0 +1,36 @@ +function [cT_water, cT_mineral, cT_organic, K_water, K_mineral, K_organic, K_grid]=... + removeWater(T, cT_water, cT_mineral, cT_organic, cT_natPor, K_water, K_mineral, K_organic, K_delta, K_grid, targetSaturation) + + +%function[cT_water, cT_mineral, cT_organic, K_grid]=... + % removeWater(T, cT_water, cT_mineral, cT_organic, cT_natPor, K_delta, K_grid, targetSaturation) + + superSaturatedCells=find(cT_water(:,1)>cT_natPor(:,1) & T(:,1)>0); + for i=superSaturatedCells + + newGridCellSize=(cT_mineral(i,1)+cT_organic(i,1)).*K_delta(i,1)./(1-cT_natPor(i,1)); %in [m] + K_grid(i+1:end)=K_grid(i+1:end)-(K_delta(i,1)-newGridCellSize); + cT_water(i,1)=cT_natPor(i,1).*targetSaturation; + cT_mineral(i,1)=cT_mineral(i,1).*K_delta(i,1)./newGridCellSize; + cT_organic(i,1)=cT_organic(i,1).*K_delta(i,1)./newGridCellSize; + % mobileWater=(cT_water(i,1)-cT_natPor(i,1)).*K_delta(i,1); %in [m] + + % K_grid(i+1:end)=K_grid(i+1:end)-mobileWater; + + + + K_water(i,1)=(cT_water(i-1,1)+cT_water(i,1))/2; + K_mineral(i,1)=(cT_mineral(i-1,1)+cT_mineral(i,1))/2; + K_organic(i,1)=(cT_organic(i-1,1)+cT_organic(i,1))/2; + + K_water(i+1,1)=(cT_water(i+1,1)+cT_water(i,1))/2; + K_mineral(i+1,1)=(cT_mineral(i+1,1)+cT_mineral(i,1))/2; + K_organic(i+1,1)=(cT_organic(i+1,1)+cT_organic(i,1))/2; + + + end + + + %mineral+org content (cT_mineral(i,1)+cT_organic(i,1)).*K_delta(i,1) + + %soll (1-cT_natPor).*newK_delta ergeben \ No newline at end of file diff --git a/modules/cryoGridExcessIce/updateGRID_excessice.m b/modules/cryoGridExcessIce/updateGRID_excessice.m new file mode 100644 index 0000000..64053c7 --- /dev/null +++ b/modules/cryoGridExcessIce/updateGRID_excessice.m @@ -0,0 +1,78 @@ +function GRID = updateGRID_excessice(GRID) +%--- update soil and water grid after subsidence ---------------------- + +% change soil with 100% water to water cell +soilGRIDsize = sum(GRID.soil.cT_domain); + + +% JAN: set all cells above without soil to air (water runs off and no snow cover +% present) +% this needs to be improved!!! +GRID.air.cT_domain(GRID.soil.cT_domain) = (GRID.soil.cT_organic==0 & GRID.soil.cT_mineral==0); +GRID.air.K_domain(GRID.soil.K_domain) = (GRID.soil.K_organic==0 & GRID.soil.K_mineral==0); + +GRID.soil.cT_domain(GRID.soil.cT_domain) = (GRID.soil.cT_organic>0 | GRID.soil.cT_mineral>0); +GRID.soil.K_domain(GRID.soil.K_domain) = (GRID.soil.K_organic>0 | GRID.soil.K_mineral>0); + + + +if soilGRIDsize ~= sum(GRID.soil.cT_domain) + + disp('subsidence - updating grid information'); + + %water_bucket = GRID.soil.cT_water(1); + cT_no_water = (GRID.soil.cT_organic>0 | GRID.soil.cT_mineral>0); + K_no_water = (GRID.soil.K_organic>0 | GRID.soil.K_mineral>0); + + [GRID.soil.cT_domain_lb GRID.soil.cT_domain_ub] = LayerIndex(GRID.soil.cT_domain); + [GRID.soil.K_domain_lb GRID.soil.K_domain_ub] = LayerIndex(GRID.soil.K_domain); + + [GRID.air.cT_domain_lb GRID.air.cT_domain_ub] = LayerIndex(GRID.air.cT_domain); + [GRID.air.K_domain_lb GRID.air.K_domain_ub] = LayerIndex(GRID.air.K_domain); + + +% GRID.water.cT_domain(max([GRID.air.cT_domain_lb+1 GRID.ice.cT_domain_lb+1]) : GRID.soil.cT_domain_ub-1) = 1; +% GRID.water.K_domain(max([GRID.air.K_domain_lb+1 GRID.ice.K_domain_lb+1]) : GRID.soil.K_domain_ub-1) = 1; +% +% [GRID.water.cT_domain_lb GRID.water.cT_domain_ub] = LayerIndex(GRID.water.cT_domain); +% [GRID.water.K_domain_lb GRID.water.K_domain_ub] = LayerIndex(GRID.water.K_domain); + + %-- update all other soil grid infos if size has changed + + + + % adjust cT grid fields + GRID.soil.cT_water = GRID.soil.cT_water(cT_no_water); + GRID.soil.cT_mineral = GRID.soil.cT_mineral(cT_no_water); + GRID.soil.cT_organic = GRID.soil.cT_organic(cT_no_water); + GRID.soil.cT_soilType = GRID.soil.cT_soilType(cT_no_water); + GRID.soil.cT_natPor = GRID.soil.cT_natPor(cT_no_water); + GRID.soil.excessGroundIce = GRID.soil.excessGroundIce(cT_no_water); + GRID.soil.conductivity = GRID.soil.conductivity(cT_no_water, :); + GRID.soil.capacity = GRID.soil.capacity(cT_no_water, :); + GRID.soil.liquidWaterContent = GRID.soil.liquidWaterContent(cT_no_water, :); + GRID.soil.cT_frozen = GRID.soil.cT_frozen(cT_no_water); + GRID.soil.cT_thawed = GRID.soil.cT_thawed(cT_no_water); + GRID.soil.K_frozen = GRID.soil.cT_frozen; % this is ok since K_frozen and cT_frozen + GRID.soil.K_thawed = GRID.soil.cT_thawed; % are the same from the start (see initialize.m) + + % adjust K grid fields + GRID.soil.soilGrid = GRID.soil.soilGrid(K_no_water); + GRID.soil.K_water = GRID.soil.K_water(K_no_water); + GRID.soil.K_mineral = GRID.soil.K_mineral(K_no_water); + GRID.soil.K_organic = GRID.soil.K_organic(K_no_water); + GRID.soil.K_soilType = GRID.soil.K_soilType(K_no_water); + + % s = fieldnames(GRID.soil); +% for i=1:length(s) +% if isempty(strfind(char(s(i)),'domain')) && isempty(strfind(char(s(i)),'K_frozen')) && isempty(strfind(char(s(i)),'K_thawed'))% exclude all with name 'domain' +% if isempty(strfind(char(s(i)),'K_')) && isempty(strfind(char(s(i)),'soilGrid')) +% %evaluate on cT grid +% eval(['GRID.soil.' char(s(i)) '=' 'GRID.soil.' char(s(i)) '(cT_no_water,:);']); +% else +% %evaluate on K grid +% eval(['GRID.soil.' char(s(i)) '=' 'GRID.soil.' char(s(i)) '(K_no_water,:);']); +% end +% end +% end +end \ No newline at end of file diff --git a/modules/cryoGridExcessIceInfiltration/excessGroundIceInfiltration.m b/modules/cryoGridExcessIceInfiltration/excessGroundIceInfiltration.m new file mode 100644 index 0000000..c580be3 --- /dev/null +++ b/modules/cryoGridExcessIceInfiltration/excessGroundIceInfiltration.m @@ -0,0 +1,18 @@ +function [GRID, PARA, wc, meltwaterGroundIce] = excessGroundIceInfiltration(T, wc, GRID, PARA) +meltwaterGroundIce=0; +if ~isempty(PARA.soil.mobileWaterDomain) && (sum(double(T(GRID.soil.cT_domain)>0 & GRID.soil.excessGroundIce==1))~=0) && isempty(GRID.snow.cT_domain_ub) + disp('excessGroundIceInfiltration - excess ice thawing'); + GRID.soil.excessGroundIce = GRID.soil.excessGroundIce==1 & T(GRID.soil.cT_domain)<=0; %remove the thawed cell from the list + [GRID, meltwaterGroundIce, wc] = excessGroundIceThaw4Infiltration(T, wc, GRID, PARA); %meltwaterGroundIce could be read out, but is not yet implemented + + %[GRID, wc] = updateGRID_excessiceInfiltration(wc, GRID); + %JAN: updateGRID not necessary as long as no distinct water domain, the + %regridding of soil/air domain happens already in the Thaw4 function + + % modification due to infiltration --> finally change cT_water to wc + %GRID.soil.cT_water = wc; + % JAN: do NOT update cT_water as this stores the latest frozen water + % content and is used to check whether the LUT needs to be updated in + % the infiltration module + +end \ No newline at end of file diff --git a/modules/cryoGridExcessIceInfiltration/excessGroundIceThaw4Infiltration.m b/modules/cryoGridExcessIceInfiltration/excessGroundIceThaw4Infiltration.m new file mode 100644 index 0000000..b7269cd --- /dev/null +++ b/modules/cryoGridExcessIceInfiltration/excessGroundIceThaw4Infiltration.m @@ -0,0 +1,147 @@ +function [GRID, meltwaterGroundIce, wc]=excessGroundIceThaw4Infiltration(T, wc, GRID, PARA) + +meltwaterGroundIce=0; % in [m] + +%calculates amounts of soil constituents in [m] +mineral=GRID.general.K_delta(GRID.soil.cT_domain).*GRID.soil.cT_mineral; +organic=GRID.general.K_delta(GRID.soil.cT_domain).*GRID.soil.cT_organic; +natPor=GRID.general.K_delta(GRID.soil.cT_domain).*GRID.soil.cT_natPor; +actPor=GRID.general.K_delta(GRID.soil.cT_domain).*GRID.soil.cT_actPor; + +% modification for infiltration +water=GRID.general.K_delta(GRID.soil.cT_domain).*wc; + +K_delta=GRID.general.K_delta(GRID.soil.cT_domain); + +mobileWater = double(T(GRID.soil.cT_domain)>0) .* (water-natPor) .* double(water>natPor); +[startCell ~]= LayerIndex(mobileWater~=0); + +%move solids down +for i=startCell:-1:1 + F_solid_down=K_delta(i)-mineral(i)-organic(i)-natPor(i); + j=i-1; + while j>0 && F_solid_down>0 + mineralDown = min(mineral(j), mineral(j)./(mineral(j)+organic(j)).*F_solid_down); + organicDown = min(organic(j), organic(j)./(mineral(j)+organic(j)).*F_solid_down); + mineral(i)=mineral(i)+mineralDown; + organic(i)=organic(i)+organicDown; + mineral(j)=mineral(j)-mineralDown; + organic(j)=organic(j)-organicDown; + F_solid_down=F_solid_down-mineralDown-organicDown; + j=j-1; + end +end + +%adjust the actual porosity +%natPor(1:startCell)=K_delta(1:startCell)-mineral(1:startCell)-organic(1:startCell); +actPor(1:startCell)=K_delta(1:startCell)-mineral(1:startCell)-organic(1:startCell); + +%move water up +mobileWater=0; +for i=startCell:-1:1 + totalWater=water(i)+mobileWater; + mobileWater=totalWater-actPor(i); + mobileWater=max(0,mobileWater); + water(i)=totalWater-mobileWater; +end + +%clean up grid cells with non-zero+non-unity water content in domains without soil matrix +mobileWater=0; +for i=1:startCell + if mineral(i)+organic(i)==0 + mobileWater=mobileWater+water(i); + water(i)=0; + end +end +for i=startCell:-1:1 + if mineral(i)+organic(i)==0 + water_temp=min( [ K_delta(i), mobileWater ] ); + mobileWater=mobileWater-water_temp; + %water(i)=round(water_temp./K_delta(i)).*K_delta(i); %this violates the water balance, but ensures that no grid cells with partly water and partly air can exist; + water(i)=water_temp; + %water_mismatch = water_temp-water(i); + %GRID.lake.residualWater = GRID.lake.residualWater + water_mismatch; + %meltwaterGroundIce=meltwaterGroundIce+water_mismatch; % this corrects the violated water balance: if round=floor then water_mismatch>=0 and this is added to runoff + end +end + +wc=water./K_delta; + +GRID.soil.cT_mineral=mineral./K_delta; +GRID.soil.cT_organic=organic./K_delta; +GRID.soil.cT_natPor=natPor./K_delta; +GRID.soil.cT_actPor=actPor./K_delta; +GRID.soil.cT_soilType( (GRID.soil.cT_mineral+GRID.soil.cT_organic)<=1e-6 )=1; %sets sand freeze curve for all water grid cells + +soilGRIDsizeOld = sum( GRID.soil.cT_domain ); % to check if LUT update necessary + +% remove air cells and mixed air/water cells above water table and adjust the GRID domains +while GRID.soil.cT_mineral(1)+GRID.soil.cT_organic(1)+wc(1)<=0 || ... % upper cell filled with pure air + (GRID.soil.cT_mineral(1)+GRID.soil.cT_organic(1)<=1e-6 && ( PARA.location.initial_altitude - GRID.general.K_grid(GRID.soil.cT_domain_ub+1) > PARA.location.absolute_maxWater_altitude) ) + + disp('xice - update GRID - removing grid cell ...') + if wc(1)==0 + disp('... upper cell wc=0') + elseif wc(1)==1 + disp('... upper cell wc=1') + else + disp('... upper cell 0 PARA.location.absolute_maxWater_altitude + + disp('xice - checking upper cell for excess water'); + + actualWater = wc(1)*K_delta(1); + h = PARA.location.absolute_maxWater_altitude - (PARA.location.initial_altitude-GRID.general.K_grid(GRID.soil.cT_domain_ub+1)); + + if h<0 + warning('xice - h<0. too much water above water table!') + end + + if actualWater>h + wc(1)=h./K_delta(1); + meltwaterGroundIce = meltwaterGroundIce + actualWater-h; + end + +end + +soilGRIDsizeNew = sum (GRID.soil.cT_domain ); +% update look up tables since soil water contents changed +% --> only if grid cells freeze, otherwise not necessary ????? +% if sum(double(wc~=GRID.soil.cT_water & T(GRID.soil.cT_domain)<=0))>0 +if soilGRIDsizeOld~=soilGRIDsizeNew + disp('xice - reinitializing LUT - soil/air domains changed'); + GRID.soil.cT_water=wc; + GRID = initializeSoilThermalProperties(GRID, PARA); +end \ No newline at end of file diff --git a/modules/cryoGridExcessIceInfiltration/updateGRID_excessiceInfiltration.m b/modules/cryoGridExcessIceInfiltration/updateGRID_excessiceInfiltration.m new file mode 100644 index 0000000..e2dc7a5 --- /dev/null +++ b/modules/cryoGridExcessIceInfiltration/updateGRID_excessiceInfiltration.m @@ -0,0 +1,89 @@ +function [GRID, wc] = updateGRID_excessiceInfiltration(wc, GRID) +%--- update soil and water grid after subsidence ---------------------- + +% JAN: I think this whole function is not necessary as long as water cells +% below water table are treated as "soil" + + +% change soil with 100% water to water cell +soilGRIDsize = sum(GRID.soil.cT_domain); + + +% JAN: set all cells without mineral or organic to air (water runs off and no snow cover +% present) +% this needs to be improved!!! +%GRID.air.cT_domain(GRID.soil.cT_domain) = (GRID.soil.cT_organic==0 && GRID.soil.cT_mineral==0); +%GRID.air.K_domain(GRID.soil.K_domain) = (GRID.soil.K_organic==0 && GRID.soil.K_mineral==0); + +%GRID.soil.cT_domain(GRID.soil.cT_domain) = (GRID.soil.cT_organic>0 || GRID.soil.cT_mineral>0); +%GRID.soil.K_domain(GRID.soil.K_domain) = (GRID.soil.K_organic>0 || GRID.soil.K_mineral>0); + + + +if soilGRIDsize ~= sum(GRID.soil.cT_domain) + + disp('subsidence - updating grid information'); + + %water_bucket = GRID.soil.cT_water(1); + %water_bucket = wc(1); + + cT_no_water = (GRID.soil.cT_organic>0 || GRID.soil.cT_mineral>0); + K_no_water = (GRID.soil.K_organic>0 || GRID.soil.K_mineral>0); + + [GRID.soil.cT_domain_lb, GRID.soil.cT_domain_ub] = LayerIndex(GRID.soil.cT_domain); + [GRID.soil.K_domain_lb, GRID.soil.K_domain_ub] = LayerIndex(GRID.soil.K_domain); + + [GRID.air.cT_domain_lb, GRID.air.cT_domain_ub] = LayerIndex(GRID.air.cT_domain); + [GRID.air.K_domain_lb, GRID.air.K_domain_ub] = LayerIndex(GRID.air.K_domain); + + %JAN : add here new domain specifications. what about soil domain? + +% GRID.water.cT_domain(max([GRID.air.cT_domain_lb+1 GRID.ice.cT_domain_lb+1]) : GRID.soil.cT_domain_ub-1) = 1; +% GRID.water.K_domain(max([GRID.air.K_domain_lb+1 GRID.ice.K_domain_lb+1]) : GRID.soil.K_domain_ub-1) = 1; +% +% [GRID.water.cT_domain_lb GRID.water.cT_domain_ub] = LayerIndex(GRID.water.cT_domain); +% [GRID.water.K_domain_lb GRID.water.K_domain_ub] = LayerIndex(GRID.water.K_domain); + + %-- update all other soil grid infos if size has changed + + + + % adjust cT grid fields + % modification due to infiltration + wc = wc(cT_no_water); + %GRID.soil.cT_water = GRID.soil.cT_water(cT_no_water); + + GRID.soil.cT_mineral = GRID.soil.cT_mineral(cT_no_water); + GRID.soil.cT_organic = GRID.soil.cT_organic(cT_no_water); + GRID.soil.cT_soilType = GRID.soil.cT_soilType(cT_no_water); + GRID.soil.cT_natPor = GRID.soil.cT_natPor(cT_no_water); + GRID.soil.cT_actPor = GRID.soil.cT_actPor(cT_no_water); + GRID.soil.excessGroundIce = GRID.soil.excessGroundIce(cT_no_water); + GRID.soil.conductivity = GRID.soil.conductivity(cT_no_water, :); + GRID.soil.capacity = GRID.soil.capacity(cT_no_water, :); + GRID.soil.liquidWaterContent = GRID.soil.liquidWaterContent(cT_no_water, :); + GRID.soil.cT_frozen = GRID.soil.cT_frozen(cT_no_water); + GRID.soil.cT_thawed = GRID.soil.cT_thawed(cT_no_water); + GRID.soil.K_frozen = GRID.soil.cT_frozen; % this is ok since K_frozen and cT_frozen + GRID.soil.K_thawed = GRID.soil.cT_thawed; % are the same from the start (see initialize.m) + + % adjust K grid fields + GRID.soil.soilGrid = GRID.soil.soilGrid(K_no_water); + GRID.soil.K_water = GRID.soil.K_water(K_no_water); + GRID.soil.K_mineral = GRID.soil.K_mineral(K_no_water); + GRID.soil.K_organic = GRID.soil.K_organic(K_no_water); + GRID.soil.K_soilType = GRID.soil.K_soilType(K_no_water); + + % s = fieldnames(GRID.soil); +% for i=1:length(s) +% if isempty(strfind(char(s(i)),'domain')) && isempty(strfind(char(s(i)),'K_frozen')) && isempty(strfind(char(s(i)),'K_thawed'))% exclude all with name 'domain' +% if isempty(strfind(char(s(i)),'K_')) && isempty(strfind(char(s(i)),'soilGrid')) +% %evaluate on cT grid +% eval(['GRID.soil.' char(s(i)) '=' 'GRID.soil.' char(s(i)) '(cT_no_water,:);']); +% else +% %evaluate on K grid +% eval(['GRID.soil.' char(s(i)) '=' 'GRID.soil.' char(s(i)) '(K_no_water,:);']); +% end +% end +% end +end \ No newline at end of file diff --git a/modules/cryoGridExcessIceInfiltration/updateGRID_excessiceInfiltration2.m b/modules/cryoGridExcessIceInfiltration/updateGRID_excessiceInfiltration2.m new file mode 100644 index 0000000..b15f35e --- /dev/null +++ b/modules/cryoGridExcessIceInfiltration/updateGRID_excessiceInfiltration2.m @@ -0,0 +1,45 @@ +function [GRID] = updateGRID_excessiceInfiltration2(meltwaterGroundIce, GRID) + +%tsvd GRID.lake.cT_domain and GRID.lake.K_domain replaced by GRID.lake.water.* + + % pass excess meltwater to storage variable + GRID.lake.residualWater = GRID.lake.residualWater + meltwaterGroundIce; + + % update GRID domains of water body zzz +%tsvd if GRID.soil.cT_organic(1)+GRID.soil.cT_mineral(1)<=1e-6 % upper soil cell pure air/water (and no lake exists) + if GRID.soil.cT_organic(1)+GRID.soil.cT_mineral(1)<=1e-6 && isempty(GRID.lake.water.cT_domain_ub) && isempty(GRID.lake.ice.cT_domain_ub) % upper soil cell pure air/water or no lake + + % general water body extent zzz + cT_waterBody = GRID.soil.cT_organic+GRID.soil.cT_mineral<=1e-6; + GRID.lake.water.cT_domain(logical(GRID.air.cT_domain+GRID.snow.cT_domain)) = 0; + GRID.lake.water.cT_domain(GRID.soil.cT_domain) = cT_waterBody; + [GRID.lake.water.cT_domain_lb, GRID.lake.water.cT_domain_ub] = LayerIndex(GRID.lake.water.cT_domain); + GRID.lake.water.K_domain(logical(GRID.air.K_domain+GRID.snow.K_domain)) = 0; + GRID.lake.water.K_domain(GRID.lake.water.cT_domain_ub:GRID.lake.water.cT_domain_lb+1) = 1; + GRID.lake.water.K_domain(GRID.lake.water.cT_domain_lb+2:end) = 0; + [GRID.lake.water.K_domain_lb, GRID.lake.water.K_domain_ub] = LayerIndex(GRID.lake.water.K_domain); + +% % distinction frozen and unfrozen parts +% unfrozenWaterBody = GRID.soil.cT_organic+GRID.soil.cT_mineral<=1e-6 & T(GRID.soil.cT_domain)>0; +% frozenWaterBody = GRID.soil.cT_organic+GRID.soil.cT_mineral<=1e-6 & T(GRID.soil.cT_domain)<=0; +% GRID.lake.water.cT_domain = GRID.lake.water.cT_domain & T>0; +% [GRID.lake.water.cT_domain_lb, GRID.lake.water.cT_domain_ub] = LayerIndex(GRID.lake.water.cT_domain); +% GRID.lake.ice.cT_domain = GRID.lake.water.cT_domain & T<=0; +% [GRID.lake.ice.cT_domain_lb, GRID.lake.ice.cT_domain_ub] = LayerIndex(GRID.lake.ice.cT_domain); %these might be two domains + +% % K domains not implemented so far + +%tsvd + GRID.lake.water.cT_domain(max([GRID.air.cT_domain_lb+1 GRID.lake.ice.cT_domain_lb+1]) : GRID.soil.cT_domain_ub-1) = 1; + GRID.lake.water.K_domain(max([GRID.air.K_domain_lb+1 GRID.lake.ice.K_domain_lb+1]) : GRID.soil.K_domain_ub-1) = 1; + + [GRID.lake.water.cT_domain_lb GRID.lake.water.cT_domain_ub] = LayerIndex(GRID.lake.water.cT_domain); + [GRID.lake.water.K_domain_lb GRID.lake.water.K_domain_ub] = LayerIndex(GRID.lake.water.K_domain); + else + GRID.lake.water.cT_domain = false(size(GRID.general.cT_grid)); + GRID.lake.water.K_domain = false(size(GRID.general.K_grid)); + [GRID.lake.water.cT_domain_lb, GRID.lake.water.cT_domain_ub] = LayerIndex(GRID.lake.water.cT_domain); + [GRID.lake.water.K_domain_lb, GRID.lake.water.K_domain_ub] = LayerIndex(GRID.lake.water.K_domain); + end + +end \ No newline at end of file diff --git a/modules/cryoGridInitialize/generateTemporary.m b/modules/cryoGridInitialize/generateTemporary.m index 68db4dd..dda3304 100644 --- a/modules/cryoGridInitialize/generateTemporary.m +++ b/modules/cryoGridInitialize/generateTemporary.m @@ -1,8 +1,10 @@ function [t, TEMPORARY] = generateTemporary(T, PARA) - t=PARA.technical.starttime; + TEMPORARY.outputTime=t+PARA.technical.outputTimestep; +TEMPORARY.syncTime=t+PARA.technical.syncTimeStep; + if ~isempty(PARA.technical.saveInterval) TEMPORARY.saveTime=datenum(str2num(datestr(t,'yyyy'))+1, str2num(PARA.technical.saveDate(4:5)), str2num(PARA.technical.saveDate(1:2)))-PARA.technical.outputTimestep; else @@ -14,6 +16,19 @@ TEMPORARY.Qnet_sum=0; TEMPORARY.Qg_sum=0; +%for EB checks +TEMPORARY.Qsurf_sum = 0; +TEMPORARY.dE_dt_SEB_sum = 0.*T; +TEMPORARY.dE_dt_cond_sum = 0.*T; + +TEMPORARY.dE_soil_sens = 0; +TEMPORARY.dE_soil_lat = 0; +TEMPORARY.dE_soil = 0; +TEMPORARY.dE_snow_sens = 0; +TEMPORARY.dE_snow_lat = 0; +TEMPORARY.dE_snow = 0; + + TEMPORARY.timestep_sum=0; TEMPORARY.T_sum=0.*T; diff --git a/modules/cryoGridInitialize/initializeBALANCE.m b/modules/cryoGridInitialize/initializeBALANCE.m index af8b6ec..c4e6d79 100644 --- a/modules/cryoGridInitialize/initializeBALANCE.m +++ b/modules/cryoGridInitialize/initializeBALANCE.m @@ -18,6 +18,9 @@ BALANCE.energy.dE_snow_lat = 0; BALANCE.energy.dE_snow = 0; + BALANCE.energy.Q_lateral = zeros( length(GRID.general.cT_grid) , 1 ); + + % WATER balance % water content soil domain in [m] BALANCE.water.W_soil = nansum( wc .* GRID.general.K_delta(GRID.soil.cT_domain) ); @@ -35,8 +38,12 @@ BALANCE.water.ds=0; % runoff BALANCE.water.dr_surface=0; - BALANCE.water.dr_subsurface=0; + BALANCE.water.dr_external=0; BALANCE.water.dr_snowmelt=0; BALANCE.water.dr_excessSnow=0; + BALANCE.water.dr_lateralSnow=0; BALANCE.water.dr_rain=0; % this is only rain on frozen ground + BALANCE.water.dr_lateral=0; + % mismatch + BALANCE.water.dm_lacking=0; end \ No newline at end of file diff --git a/modules/cryoGridInitialize/initializeConductivityCapacity.m b/modules/cryoGridInitialize/initializeConductivityCapacity.m index 95ff467..50d9fea 100644 --- a/modules/cryoGridInitialize/initializeConductivityCapacity.m +++ b/modules/cryoGridInitialize/initializeConductivityCapacity.m @@ -24,7 +24,6 @@ k_temp(GRID.soil.cT_domain) = double(T(GRID.soil.cT_domain)<=0).*k_temp(GRID.soil.cT_domain) + double(T(GRID.soil.cT_domain)>0).* conductivityUnfrozen(wc,GRID,PARA); lwc_temp(GRID.soil.cT_domain) = double(T(GRID.soil.cT_domain)<=0).*lwc_temp(GRID.soil.cT_domain) + double(T(GRID.soil.cT_domain)>0).* wc; - %------- snow domain -------------------------------------- c_temp(GRID.snow.cT_domain) = cap_snow(GRID.snow.Snow_i(GRID.snow.cT_domain),... GRID.snow.Snow_w(GRID.snow.cT_domain),... @@ -36,3 +35,12 @@ GRID.snow.Snow_a(GRID.snow.cT_domain)); lwc_temp(GRID.snow.cT_domain) = GRID.snow.Snow_w(GRID.snow.cT_domain)./GRID.general.K_delta(GRID.snow.cT_domain); + +%tsvd------ lake domain -------- +c_temp(GRID.lake.water.cT_domain) = PARA.constants.c_w; +k_temp(GRID.lake.water.cT_domain) = 5.46E-01 ; % in loadConstants.m 0.57 is used! FLAKE: Molecular heat conductivity of water [J m^{-1} s^{-1} K^{-1}] zzz +lwc_temp(GRID.lake.water.cT_domain) = 1.; + +c_temp(GRID.lake.ice.cT_domain) = PARA.constants.c_i; +k_temp(GRID.lake.ice.cT_domain) = 2.29; ; % in loadConstants.m 2.2 is used! FLAKE: Molecular heat conductivity of ice [J m^{-1} s^{-1} K^{-1}] zzz +lwc_temp(GRID.lake.ice.cT_domain) = 0.; diff --git a/modules/cryoGridInitialize/initializeLAKE_version_Jan.m b/modules/cryoGridInitialize/initializeLAKE_version_Jan.m new file mode 100644 index 0000000..00d40c9 --- /dev/null +++ b/modules/cryoGridInitialize/initializeLAKE_version_Jan.m @@ -0,0 +1,31 @@ +function [GRID] = initializeLAKE(GRID); + +GRID.lake.unfrozenWaterSurface = false; +GRID.lake.residualWater = 0; % the water content stored "mixed" cells of air and water if a water body is present + +% %---- flake initialization ------------------------------------------------ +% FLAKE.t_snow_n_flk=0+273.15; +% FLAKE.t_ice_n_flk=0+273.15; +% FLAKE.t_wml_n_flk=6+273.15; +% FLAKE.t_mnw_n_flk=5+273.15; +% FLAKE.t_bot_n_flk=4+273.15; +% FLAKE.t_b1_n_flk=7+273.15; +% FLAKE.h_snow_n_flk=0; +% FLAKE.h_ice_n_flk=0; +% FLAKE.h_ml_n_flk=3; +% FLAKE.h_b1_n_flk=10; +% FLAKE.c_t_n_flk=0; +% FLAKE.h_ice_n_flk=0; +% +% FLAKE.i_w_flk=0; +% FLAKE.i_bot_flk=0; +% FLAKE.q_w_flk=0; +% FLAKE.q_bot_flk=0; +% +% FLAKE.fetch=100; +% FLAKE.depth_w=PARA.water.depth; +% +% FLAKE.d_h_ice_dt = 0; +% FLAKE.q_ice_water = 0; +% FLAKE.extincoef_water_typ=PARA.water.extinction; +% FLAKE.latitude=PARA.location.latitude; \ No newline at end of file diff --git a/modules/cryoGridLake/FlakeConstants.m b/modules/cryoGridLake/FlakeConstants.m new file mode 100644 index 0000000..3222cdd --- /dev/null +++ b/modules/cryoGridLake/FlakeConstants.m @@ -0,0 +1,89 @@ +function varargout = FlakeConstants + + c_Karman = 0.40 ; % The von Karman constant + Pr_neutral = 1.0 ; % Turbulent Prandtl number at neutral static stability + Sc_neutral = 1.0 ; % Turbulent Schmidt number at neutral static stability + c_MO_u_stab = 5.0 ; % Constant of the MO theory (wind, stable stratification) + c_MO_t_stab = 5.0 ; % Constant of the MO theory (temperature, stable stratification) + c_MO_q_stab = 5.0 ; % Constant of the MO theory (humidity, stable stratification) + c_MO_u_conv = 15.0 ; % Constant of the MO theory (wind, convection) + c_MO_t_conv = 15.0 ; % Constant of the MO theory (temperature, convection) + c_MO_q_conv = 15.0 ; % Constant of the MO theory (humidity, convection) + c_MO_u_exp = 0.25 ; % Constant of the MO theory (wind, exponent) + c_MO_t_exp = 0.5 ; % Constant of the MO theory (temperature, exponent) + c_MO_q_exp = 0.5 ; % Constant of the MO theory (humidity, exponent) + z0u_ice_rough = 1.0E-03 ; % Aerodynamic roughness of the ice surface [m] (rough flow) + c_z0u_smooth = 0.1 ; % Constant in the expression for z0u (smooth flow) + c_z0u_rough = 1.23E-02 ; % The Charnock constant in the expression for z0u (rough flow) + c_z0u_rough_l = 1.00E-01 ; % An increased Charnock constant (used as the upper limit) + c_z0u_ftch_f = 0.70 ; % Factor in the expression for fetch-dependent Charnock parameter + c_z0u_ftch_ex = 0.3333333 ; % Exponent in the expression for fetch-dependent Charnock parameter + c_z0t_rough_1 = 4.0 ; % Constant in the expression for z0t (factor) + c_z0t_rough_2 = 3.2 ; % Constant in the expression for z0t (factor) + c_z0t_rough_3 = 0.5 ; % Constant in the expression for z0t (exponent) + c_z0q_rough_1 = 4.0 ; % Constant in the expression for z0q (factor) + c_z0q_rough_2 = 4.2 ; % Constant in the expression for z0q (factor) + c_z0q_rough_3 = 0.5 ; % Constant in the expression for z0q (exponent) + c_z0t_ice_b0s = 1.250 ; % Constant in the expression for z0t over ice + c_z0t_ice_b0t = 0.149 ; % Constant in the expression for z0t over ice + c_z0t_ice_b1t = -0.550 ; % Constant in the expression for z0t over ice + c_z0t_ice_b0r = 0.317 ; % Constant in the expression for z0t over ice + c_z0t_ice_b1r = -0.565 ; % Constant in the expression for z0t over ice + c_z0t_ice_b2r = -0.183 ; % Constant in the expression for z0t over ice + c_z0q_ice_b0s = 1.610 ; % Constant in the expression for z0q over ice + c_z0q_ice_b0t = 0.351 ; % Constant in the expression for z0q over ice + c_z0q_ice_b1t = -0.628 ; % Constant in the expression for z0q over ice + c_z0q_ice_b0r = 0.396 ; % Constant in the expression for z0q over ice + c_z0q_ice_b1r = -0.512 ; % Constant in the expression for z0q over ice + c_z0q_ice_b2r = -0.180 ; % Constant in the expression for z0q over ice + re_z0s_ice_t = 2.5 ; % Threshold value of the surface Reynolds number + % used to compute z0t and z0q over ice (Andreas 2002) + re_z0u_thresh = 0.1; % Threshold value of the roughness Reynolds number + % [value from Zilitinkevich, Grachev, and Fairall (200), + % currently not used] + +% Dimensionless constants + c_free_conv = 0.14; % Constant in the expressions for fluxes in free convection + +% Dimensionless constants + c_lwrad_emis = 0.99; % Surface emissivity with respect to the long-wave radiation + +% Thermodynamic parameters + tpsf_C_stefboltz = 5.67E-08 ; % The Stefan-Boltzmann constant [W m^{-2} K^{-4}] + tpsf_R_dryair = 2.8705E+02 ; % Gas constant for dry air [J kg^{-1} K^{-1}] + tpsf_R_watvap = 4.6151E+02 ; % Gas constant for water vapour [J kg^{-1} K^{-1}] + tpsf_c_a_p = 1.005E+03 ; % Specific heat of air at constant pressure [J kg^{-1} K^{-1}] + tpsf_L_evap = 2.501E+06 ; % Specific heat of evaporation [J kg^{-1}] + tpsf_nu_u_a = 1.50E-05 ; % Kinematic molecular viscosity of air [m^{2} s^{-1}] + tpsf_kappa_t_a = 2.20E-05 ; % Molecular temperature conductivity of air [m^{2} s^{-1}] + tpsf_kappa_q_a = 2.40E-05 ; % Molecular diffusivity of air for water vapour [m^{2} s^{-1}] + +% Derived thermodynamic parameters + tpsf_Rd_o_Rv = tpsf_R_dryair/tpsf_R_watvap ; % Ratio of gas constants (Rd/Rv) + tpsf_alpha_q = (1.-tpsf_Rd_o_Rv)/tpsf_Rd_o_Rv ; % Diemsnionless ratio + +% Thermodynamic parameters + P_a_ref = 1.0E+05 ; % Reference pressure [N m^{-2} = kg m^{-1} s^{-2}] + + +% Security constants + u_wind_min_sf = 1.0E-02 ; % Minimum wind speed [m s^{-1}] + u_star_min_sf = 1.0E-04 ; % Minimum value of friction velocity [m s^{-1}] + c_accur_sf = 1.0E-07 ; % A small number (accuracy) + c_small_sf = 1.0E-04 ; % A small number (used to compute fluxes) + +% Useful constants + num_1o3_sf = 1./3.; % 1/3 + + + % OUTPUT of all variables + fieldNames=who; + nFields = length(fieldNames); + + for iField = 1:nFields + assignin('caller',fieldNames{iField},eval([fieldNames{iField}])); + end + + + + diff --git a/modules/cryoGridLake/FlakeParameters.m b/modules/cryoGridLake/FlakeParameters.m new file mode 100644 index 0000000..d1caa81 --- /dev/null +++ b/modules/cryoGridLake/FlakeParameters.m @@ -0,0 +1,82 @@ +function varargout = FlakeParameters + +% Dimensionless constants +% in the equations for the mixed-layer depth +% and for the shape factor with respect to the temperature profile in the thermocline + c_cbl_1 = 0.17 ; % Constant in the CBL entrainment equation + c_cbl_2 = 1. ; % Constant in the CBL entrainment equation + c_sbl_zm_n = 0.5 ; % Constant in the ZM1996 equation for the equilibrium SBL depth + c_sbl_zm_s = 10. ; % Constant in the ZM1996 equation for the equilibrium SBL depth + c_sbl_zm_i = 20. ; % Constant in the ZM1996 equation for the equilibrium SBL depth + c_relax_h = 0.030 ; % Constant in the relaxation equation for the SBL depth + c_relax_c = 0.0030 ; % Constant in the relaxation equation for the shape factor + % with respect to the temperature profile in the thermocline + +% Parameters of the shape functions +% Indices refer to T - thermocline, S - snow, I - ice, +% B1 - upper layer of the bottom sediments, B2 - lower layer of the bottom sediments. +% "pr0" and "pr1" denote zeta derivatives of the corresponding shape function +% at "zeta=0" ad "zeta=1", respectively. + c_t_min = 0.5 ; % Minimum value of the shape factor C_T (thermocline) + c_t_max = 0.8 ; % Maximum value of the shape factor C_T (thermocline) + phi_t_pr0_1 = 40./3. ; % Constant in the expression for the T shape-function derivative + phi_t_pr0_2 = 20./3. ; % Constant in the expression for the T shape-function derivative + c_tt_1 = 11./18. ; % Constant in the expression for C_TT (thermocline) + c_tt_2 = 7./45. ; % Constant in the expression for C_TT (thermocline) + c_b1 = 2./3. ; % Shape factor (upper layer of bottom sediments) + c_b2 = 3./5. ; % Shape factor (lower layer of bottom sediments) + phi_b1_pr0 = 2. ; % B1 shape-function derivative + c_s_lin = 0.5 ; % Shape factor (linear temperature profile in the snow layer) + phi_s_pr0_lin = 1. ; % S shape-function derivative (linear profile) + c_i_lin = 0.5 ; % Shape factor (linear temperature profile in the ice layer) + phi_i_pr0_lin = 1. ; % I shape-function derivative (linear profile) + phi_i_pr1_lin = 1. ; % I shape-function derivative (linear profile) + phi_i_ast_mr = 2. ; % Constant in the MR2004 expression for I shape factor + c_i_mr = 1./12. ; % Constant in the MR2004 expression for I shape factor + h_ice_max = 3. ; % Maximum ice tickness in + % the Mironov and Ritter (2004, MR2004) ice model [m] + +% Security constants + h_snow_min_flk = 1.0E-5 ; % Minimum snow thickness [m] + h_ice_min_flk = 1.0E-3 ; % Minimum ice thickness [m] + h_ml_min_flk = 1.0E-2 ; % Minimum mixed-layer depth [m] + h_ml_max_flk = 1.0E+3 ; % Maximum mixed-layer depth [m] + h_b1_min_flk = 1.0E-3 ; % Minimum thickness of the upper layer of bottom sediments [m] + u_star_min_flk = 1.0E-6 ; % Minimum value of the surface friction velocity [m s^{-1}] + +% Security constant(s) + c_small_flk = 1.0E-10; % A small number + +% Thermodynamic parameters + tpl_grav = 9.81 ; % Acceleration due to gravity [m s^{-2}] + tpl_t_r = 277.13 ; % Temperature of maximum density of fresh water [K] + tpl_t_f = 273.15 ; % Fresh water freezing point [K] + tpl_a_T = 1.6509E-05 ; % Constant in the fresh-water equation of state [K^{-2}] + tpl_rho_w_r = 1.0E+03 ; % Maximum density of fresh water [kg m^{-3}] + tpl_rho_i = 9.1E+02 ; % Density of ice [kg m^{-3}] + tpl_rho_s_min = 1.0E+02 ; % Minimum snow density [kg m^{-3}] + tpl_rho_s_max = 4.0E+02 ; % Maximum snow density [kg m^{-3}] + tpl_gamma_rho_s = 2.0E+02 ; % Empirical parameter [kg m^{-4}] + % in the expression for the snow density + tpl_l_f = 3.3E+05 ; % Latent heat of fusion [J kg^{-1}] + tpl_c_w = 4.2E+03 ; % Specific heat of water [J kg^{-1} K^{-1}] + tpl_c_i = 2.1E+03 ; % Specific heat of ice [J kg^{-1} K^{-1}] + tpl_c_s = 2.1E+03 ; % Specific heat of snow [J kg^{-1} K^{-1}] + tpl_kappa_w = 5.46E-01 ; % Molecular heat conductivity of water [J m^{-1} s^{-1} K^{-1}] + tpl_kappa_i = 2.29 ; % Molecular heat conductivity of ice [J m^{-1} s^{-1} K^{-1}] + tpl_kappa_s_min = 0.2 ; % Minimum molecular heat conductivity of snow [J m^{-1} s^{-1} K^{-1}] + tpl_kappa_s_max = 1.5 ; % Maximum molecular heat conductivity of snow [J m^{-1} s^{-1} K^{-1}] + tpl_gamma_kappa_s = 1.3; % Empirical parameter [J m^{-2} s^{-1} K^{-1}] + % in the expression for the snow heat conductivity + +%============================================================================== + + % OUTPUT of all variables + fieldNames=who; + nFields = length(fieldNames); + + for iField = 1:nFields + assignin('caller',fieldNames{iField},eval([fieldNames{iField}])); + end + + diff --git a/modules/cryoGridLake/SolarAzEl.m b/modules/cryoGridLake/SolarAzEl.m new file mode 100644 index 0000000..7917f41 --- /dev/null +++ b/modules/cryoGridLake/SolarAzEl.m @@ -0,0 +1,137 @@ +function [Az El] = SolarAzEl(UTC,Lat,Lon,Alt) %#codegen +%% Revision History: +% Programed by Darin C. Koblick 2/17/2009 +% +% Darin C. Koblick 4/16/2013 Vectorized for Speed +% Allow for MATLAB Datevec input in +% addition to a UTC string. +% Cleaned up comments and code to +% avoid warnings in MATLAB editor. +% +%-------------------------------------------------------------------------- +% External Function Call Sequence: +%[Az El] = SolarAzEl('1991/05/19 13:00:00',50,10,0) +%% Function Description: +% SolarAzEl will ingest a Universal Time, and specific site location on earth +% it will then output the solar Azimuth and Elevation angles relative to that +% site. +% +%% Input Description: +% UTC/DateVec: [N x 1] (Coordinated Universal Time +% yyyy/mm/dd HH:MM:SS) or MATLAB +% Date vector if input is a double +% instead of a cell +% +% Lat: [N x 1] (Site Latitude in degrees +% -90:90 -> S(-) N(+)) +% +% Lon: [N x 1] (Site Longitude in degrees +% -180:180 W(-) E(+)) +% +% Alt: [N x 1] Altitude of the site above sea +% level (km) +% +%% Output Description: +%Az [N x 1] Azimuth location of the sun (deg) +%El [N x 1] Elevation location of the sun (deg) +% +% +%% Source References: +%Solar Position obtained from: +%http://stjarnhimlen.se/comp/tutorial.html#5 +%% Begin Code Sequence + +%compute JD from UTC or datevec + +jd = juliandate(UTC); + +d = jd-2451543.5; + +% Keplerian Elements for the Sun (geocentric) +w = 282.9404+4.70935e-5*d; % (longitude of perihelion degrees) +%a = 1.000000;% (mean distance, a.u.) +e = 0.016709-1.151e-9.*d;% (eccentricity) +M = mod(356.0470+0.9856002585.*d,360);% (mean anomaly degrees) +L = w + M; %(Sun's mean longitude degrees) +oblecl = 23.4393-3.563e-7.*d; %(Sun's obliquity of the ecliptic) + +%auxiliary angle +E = M+(180/pi).*e.*sin(M.*(pi/180)).*(1+e.*cos(M.*(pi/180))); + +%rectangular coordinates in the plane of the ecliptic (x axis toward +%perhilion) +x = cos(E.*(pi/180))-e; +y = sin(E.*(pi/180)).*sqrt(1-e.^2); + +%find the distance and true anomaly +r = sqrt(x.^2 + y.^2); +v = atan2(y,x).*(180/pi); + +%find the longitude of the sun +lon = v + w; + +%compute the ecliptic rectangular coordinates +xeclip = r.*cos(lon.*(pi/180)); +yeclip = r.*sin(lon.*(pi/180)); +zeclip = 0.0; + +%rotate these coordinates to equitorial rectangular coordinates +xequat = xeclip; +yequat = yeclip.*cos(oblecl.*(pi/180))+zeclip*sin(oblecl.*(pi/180)); +zequat = yeclip.*sin(23.4406.*(pi/180))+zeclip*cos(oblecl.*(pi/180)); + +%convert equatorial rectangular coordinates to RA and Decl: +r = sqrt(xequat.^2 + yequat.^2 + zequat.^2)-(Alt./149598000); %roll up the altitude correction +RA = atan2(yequat,xequat).*(180/pi); +delta = asin(zequat./r).*(180/pi); + +%Following the RA DEC to Az Alt conversion sequence explained here: +%http://www.stargazing.net/kepler/altaz.html + +%Find the J2000 value +%J2000 = jd - 2451545.0; +% hourvec = datevec(UTC); +% UTH = hourvec(:,4) + hourvec(:,5)/60 + hourvec(:,6)/3600; +%also works +UTH = (UTC-floor(UTC))*24; + + +%Calculate local siderial time +GMST0=mod(L+180,360)./15; +SIDTIME = GMST0 + UTH + Lon./15; + +%Replace RA with hour angle HA +HA = (SIDTIME.*15 - RA); + +%convert to rectangular coordinate system +x = cos(HA.*(pi/180)).*cos(delta.*(pi/180)); +y = sin(HA.*(pi/180)).*cos(delta.*(pi/180)); +z = sin(delta.*(pi/180)); + +%rotate this along an axis going east-west. +xhor = x.*cos((90-Lat).*(pi/180))-z.*sin((90-Lat).*(pi/180)); +yhor = y; +zhor = x.*sin((90-Lat).*(pi/180))+z.*cos((90-Lat).*(pi/180)); + +%Find the h and AZ +Az = atan2(yhor,xhor).*(180/pi) + 180; +El = asin(zhor).*(180/pi); +end + +function jd = juliandate(UTC) %#codegen +%should work as well +jd = 1.7210275e+06+31 + UTC ; + +% % This sub function is provided in case juliandate does not come with your +% % distribution of Matlab +% [year month day hour min sec] = datevec(UTC); +% +% idx = (month <= 2); +% year(idx) = year(idx)-1; +% month(idx) = month(idx)+12; +% jd = floor( 365.25*(year + 4716.0)) + floor( 30.6001*( month + 1.0)) + 2.0 - ... +% floor( year/100.0 ) + floor( floor( year/100.0 )/4.0 ) + day - 1524.5 + ... +% (hour + min/60 + sec/3600)/24; +end + + diff --git a/modules/cryoGridLake/cryoGridFLAKE.m b/modules/cryoGridLake/cryoGridFLAKE.m new file mode 100644 index 0000000..fa7b5a1 --- /dev/null +++ b/modules/cryoGridLake/cryoGridFLAKE.m @@ -0,0 +1,144 @@ +function [T, GRID, FLAKE]=cryoGridFLAKE(FLAKE,GRID,SEB,PARA,T,T_old,timestep,k_eff,dE_dt,dE_dt_SEB) + +%update average temperature of the water domain +FLAKE.t_mnw_n_flk = sum(T_old(GRID.lake.water.cT_domain).*GRID.general.K_delta(GRID.lake.water.cT_domain)) ... + ./ sum(GRID.general.K_delta(GRID.lake.water.cT_domain)) + 273.15; + +%update water depth +FLAKE.depth_w = GRID.general.K_grid(GRID.lake.water.cT_domain_lb + 1) - GRID.general.K_grid(GRID.lake.water.cT_domain_ub); + +%update mean temperature according to changes in average temperature with +%water depth. This prevents changes in the bottom temperature due to sudden +%changes in water depth +if GRID.lake.ice.z_ice>0 + FLAKE.t_mnw_p_flk = FLAKE.t_mnw_n_flk; + FLAKE.t_mnw_n_flk = FLAKE.t_wml_n_flk - FLAKE.c_t_n_flk.*(FLAKE.t_wml_n_flk-FLAKE.t_bot_n_flk).*(1.-FLAKE.h_ml_n_flk./FLAKE.depth_w); + GRID.lake.ice.z_ice = GRID.lake.ice.z_ice - (FLAKE.t_mnw_p_flk-FLAKE.t_mnw_n_flk).*4.2e6.*FLAKE.depth_w./(334e3 * 910); +end + +%update ice cover status and thickness +if FLAKE.h_ice_n_flk<1e-3 && GRID.lake.ice.z_ice>=1e-3 + FLAKE.l_ice_create = true; +else + FLAKE.l_ice_create = false; +end +FLAKE.h_ice_n_flk = GRID.lake.ice.z_ice; + +%set boundary conditions +FLAKE.q_snow_flk= 0.; +FLAKE.q_ice_flk = 0.; +FLAKE.q_bot_flk = -(T_old(GRID.lake.water.cT_domain_lb+1)-T_old(GRID.lake.water.cT_domain_lb))... + ./ GRID.general.cT_delta(GRID.lake.water.cT_domain_lb) .* k_eff(GRID.lake.water.cT_domain_lb+1); + +FLAKE.q_w_flk = sum(dE_dt_SEB(GRID.lake.water.cT_domain_ub:GRID.lake.water.cT_domain_lb+1)) ... + - SEB.Sin_water ... + + sum(dE_dt(GRID.lake.water.cT_domain_ub:GRID.lake.water.cT_domain_lb)) ... + - sum(dE_dt_SEB(GRID.lake.water.cT_domain_ub:GRID.lake.water.cT_domain_lb)) ... + + FLAKE.q_bot_flk ... + + GRID.lake.ice.dz_dt_freeze*334e3*910 ... + + GRID.lake.ice.dE_dt_melt_residual; + +if FLAKE.q_w_flk>0 && GRID.lake.ice.z_ice>0 + %correct for positive water heat fluxes under ice cover. This is + %necessary due to the very simple ice cover scheme which needs to + %be improved in fututre model versions. + GRID.lake.ice.z_ice = GRID.lake.ice.z_ice - FLAKE.q_w_flk.*(timestep *24*3600)/(334e3 * 910); + FLAKE.q_w_flk = 0; + FLAKE.h_ice_n_flk = GRID.lake.ice.z_ice; +end + +%transform u_star in atmosphere to u_star in water assuming constant +%desities for air and water. +FLAKE.u_star_w_flk=(SEB.u_star.^2.*(1.293./1e3)).^0.5; + +%calculate FLAKE radiative heat transfer +[FLAKE.i_atm_flk,... + FLAKE.i_w_flk,... + FLAKE.i_ice_flk,... + FLAKE.i_snow_flk,... + FLAKE.i_h_flk,... + FLAKE.i_bot_flk,... + FLAKE.i_intm_0_h_flk,... + FLAKE.i_intm_h_d_flk] = flake_radflux(SEB.Sin_water,FLAKE,PARA); + +% %call FLAKE main function +% if PARA.technical.MEX +% FLAKE = flake_driver_mex(FLAKE, timestep.*24.*3600); +% else + FLAKE = flake_driver(FLAKE, timestep.*24.*3600); +% end + +%map water temperature from shape function on the regular grid +%calculate dimensionless water depth of thermocline +% zeta_cT = (GRID.lake.water.cT_grid(GRID.lake.water.K_grid>=FLAKE.h_ml_n_flk) - FLAKE.h_ml_n_flk) ... +% ./ ((FLAKE.depth_w - FLAKE.h_ml_n_flk)); + +zeta_K = (GRID.lake.water.K_grid(GRID.lake.water.K_grid>=FLAKE.h_ml_n_flk) - FLAKE.h_ml_n_flk) ... + ./ ((FLAKE.depth_w - FLAKE.h_ml_n_flk)); +zeta_K = [zeta_K; 1]; +zeta_delta = diff(zeta_K); + +if ~isempty(zeta_delta) + %The original FLAKE shape function not used due to interpolation + %errors on discrete grid + %phi_theta = (40/3*FLAKE.c_t_n_flk-20/3)*zeta_cT ... + % + (18-30*FLAKE.c_t_n_flk)*zeta_cT.^2 ... + % + (20*FLAKE.c_t_n_flk-12)*zeta_cT.^3 ... + % + (5/3 - 10/3*FLAKE.c_t_n_flk)*zeta_cT.^4; + + %Integrated shape function to calculate averages on discrete grid + int_phi_theta = (40/3*FLAKE.c_t_n_flk-20/3)*1/2*zeta_K.^2 ... + + (18-30*FLAKE.c_t_n_flk)*1/3*zeta_K.^3 ... + + (20*FLAKE.c_t_n_flk-12)*1/4*zeta_K.^4 ... + + (5/3 - 10/3*FLAKE.c_t_n_flk)*1/5*zeta_K.^5; + + %calculate partial intergral for grid cells + int_phi_theta = int_phi_theta(2:end) - int_phi_theta(1:end-1); + phi_theta = int_phi_theta./zeta_delta; + + %change from dimensionless temperature to absolute temperature + Tw_z = -(phi_theta*(FLAKE.t_wml_n_flk-FLAKE.t_bot_n_flk) - FLAKE.t_wml_n_flk)-273.15; +else + Tw_z = []; +end +%map temperature on water grid +Tw = T(GRID.lake.water.cT_domain); + +%note that a step is introduced which introduces small discretization +%errors which need to be corrected later +Tw(GRID.lake.water.K_grid>=FLAKE.h_ml_n_flk) = Tw_z; +Tw(GRID.lake.water.K_grid0 + GRID.lake.ice.z_ice = GRID.lake.ice.z_ice + dE./(334e3 * 910); + %set T water surf to zero + %dE=(0-T(GRID.lake.water.cT_domain_ub))*GRID.general.K_delta(GRID.lake.water.cT_domain_ub)*4.2e6; + %GRID.lake.ice.z_ice = GRID.lake.ice.z_ice + dE./(334e3 * 910); + %T(GRID.lake.water.cT_domain_ub) = 0; +else + ub_h_ml=min([GRID.lake.water.K_grid(GRID.lake.water.K_grid>=FLAKE.h_ml_n_flk); FLAKE.depth_w]); + lb_h_ml=FLAKE.depth_w; + if (lb_h_ml-ub_h_ml)>0 + Tw(GRID.lake.water.K_grid>=FLAKE.h_ml_n_flk) = Tw_z - dE/(4.2e6* (lb_h_ml-ub_h_ml)); + else + Tw = Tw - dE/(4.2e6 * FLAKE.depth_w); + end + T(GRID.lake.water.cT_domain) = Tw; +end + +%update FLAKE average temperature of the water domain +FLAKE.t_mnw_n_flk = sum(T(GRID.lake.water.cT_domain).*GRID.general.K_delta(GRID.lake.water.cT_domain)) ... + ./ sum(GRID.general.K_delta(GRID.lake.water.cT_domain)) + 273.15; + + + diff --git a/modules/cryoGridLake/cryoGridIceCover.m b/modules/cryoGridLake/cryoGridIceCover.m new file mode 100644 index 0000000..3b9a575 --- /dev/null +++ b/modules/cryoGridLake/cryoGridIceCover.m @@ -0,0 +1,95 @@ +function [T,GRID,FLAKE]=cryoGridIceCover(GRID,SEB,FLAKE,T,c_temp,timestep) + +%construct water grids relative to the water surface +if ~isempty(GRID.lake.water.cT_domain_ub) + GRID.lake.water.cT_grid = GRID.general.cT_grid(GRID.lake.water.cT_domain) - GRID.general.K_grid(GRID.lake.water.cT_domain_ub); + GRID.lake.water.K_grid = GRID.general.K_grid(GRID.lake.water.K_domain) - GRID.general.K_grid(GRID.lake.water.cT_domain_ub); +else + GRID.lake.water.cT_grid = []; + GRID.lake.water.K_grid = []; +end + +%initialisize +GRID.lake.ice.dz_dt_freeze=0; +GRID.lake.ice.dz_dt_melt=0; +GRID.lake.ice.dE_dt_melt_residual=0; + +%ice cover build up before +if min(T(GRID.lake.water.cT_domain))<0 + + %distribute heat into the mixed water layer in order to avoid unrealistic freezing of the first water cell + if FLAKE.h_ml_n_flk>0 + Tw=T(GRID.lake.water.cT_domain); + K_delta=GRID.general.K_delta(GRID.lake.water.cT_domain); + + Tw(GRID.lake.water.K_grid0 + dE_dt_ice = (0-T(GRID.lake.ice.cT_domain)) ... + .* c_temp(GRID.lake.ice.cT_domain) ... + .* GRID.general.K_delta(GRID.lake.ice.cT_domain) ... + ./ (timestep.*24.*3600); + + dE_dt_ice = dE_dt_ice .* (T(GRID.lake.ice.cT_domain)>0); + dE_dt_ice = sum(dE_dt_ice); + + %set temperature T to 0°C for cells with melting ice + T((GRID.lake.ice.cT_domain) & T>0) = 0; + GRID.lake.ice.dz_dt_melt = dE_dt_ice./ (334e3 * 910); + GRID.lake.ice.dz_dt_melt(isinf(GRID.lake.ice.dz_dt_melt) | isnan(GRID.lake.ice.dz_dt_melt)) = 0; + + %set melt flag true if the entire ice cover is affected by melting + if sum(T(GRID.lake.ice.cT_domain))==0; + GRID.lake.ice.melt_flag=true; + end +end + +%melt remaining ice cover +GRID.lake.ice.dE_dt_melt_residual = 0; +if isempty(GRID.lake.ice.cT_domain_ub) && GRID.lake.ice.z_ice>0 && T(GRID.lake.water.cT_domain_ub)>0 + GRID.lake.ice.dz_dt_melt = max(-GRID.lake.ice.z_ice/(timestep*24*3600), -SEB.Qg/(334e3 * 910)); + GRID.lake.ice.dE_dt_melt_residual = GRID.lake.ice.dz_dt_melt*(334e3 * 910); +end + +%calculate change of ice cover depth +GRID.lake.ice.dz_dt_ice = GRID.lake.ice.dz_dt_freeze + GRID.lake.ice.dz_dt_melt - GRID.lake.ice.dE_dt_cond_residual./(334e3 * 910); + +%change ice cover thickness +GRID.lake.ice.z_ice = GRID.lake.ice.z_ice + GRID.lake.ice.dz_dt_ice.*(timestep.*24.*3600); +GRID.lake.ice.z_ice = GRID.lake.ice.z_ice*(GRID.lake.ice.z_ice>0); + +%sort water cells under ice cover according to density +if ~isempty(GRID.lake.ice.cT_domain_ub) && ~isempty(GRID.lake.water.cT_domain_ub) + DW = waterDensity(T(GRID.lake.water.cT_domain)); + [~, XI]=sort(DW); + DW=T(GRID.lake.water.cT_domain); + T(GRID.lake.water.cT_domain)=DW(XI); +end + + + + + + diff --git a/modules/cryoGridLake/flake_driver.m b/modules/cryoGridLake/flake_driver.m new file mode 100644 index 0000000..fc027a8 --- /dev/null +++ b/modules/cryoGridLake/flake_driver.m @@ -0,0 +1,1104 @@ +%------------------------------------------------------------------------------ + +function [FLAKE]=flake_driver(FLAKE, del_time) %#codegen + +%------------------------------------------------------------------------------ +% +% Description: +% +% The main driving routine of the lake model FLake +% where computations are performed. +% Advances the surface temperature +% and other FLake variables one time step. +% At the moment, the Euler explicit scheme is used. +% +% Lines embraced with '!_tmp' contain temporary parts of the code. +% Lines embraced/marked with '!_dev' may be replaced +% as improved parameterizations are developed and tested. +% Lines embraced/marked with '!_dm' are DM's comments +% that may be helpful to a user. +% Lines embraced/marked with '!_dbg' are used +% for debugging purposes only. +% +% +% Current Code Owner: DWD, Dmitrii Mironov +% Phone: +49-69-8062 2705 +% Fax: +49-69-8062 3721 +% E-mail: dmitrii.mironov@dwd.de +%------------------------------------------------------------------------------ +% +% Dimensionless constants +% in the equations for the mixed-layer depth +% and for the shape factor with respect to the temperature profile in the thermocline + c_cbl_1 = 0.17 ; % Constant in the CBL entrainment equation + c_cbl_2 = 1. ; % Constant in the CBL entrainment equation + c_sbl_zm_n = 0.5 ; % Constant in the ZM1996 equation for the equilibrium SBL depth + c_sbl_zm_s = 10. ; % Constant in the ZM1996 equation for the equilibrium SBL depth + c_sbl_zm_i = 20. ; % Constant in the ZM1996 equation for the equilibrium SBL depth + c_relax_h = 0.030 ; % Constant in the relaxation equation for the SBL depth + c_relax_c = 0.0030 ; % Constant in the relaxation equation for the shape factor + % with respect to the temperature profile in the thermocline + +% Parameters of the shape functions +% Indices refer to T - thermocline, S - snow, I - ice, +% B1 - upper layer of the bottom sediments, B2 - lower layer of the bottom sediments. +% "pr0" and "pr1" denote zeta derivatives of the corresponding shape function +% at "zeta=0" ad "zeta=1", respectively. + c_t_min = 0.5 ; % Minimum value of the shape factor C_T (thermocline) + c_t_max = 0.8 ; % Maximum value of the shape factor C_T (thermocline) +% phi_t_pr0_1 = 40./3. ; % Constant in the expression for the T shape-function derivative +% phi_t_pr0_2 = 20./3. ; % Constant in the expression for the T shape-function derivative + c_tt_1 = 11./18. ; % Constant in the expression for C_TT (thermocline) + c_tt_2 = 7./45. ; % Constant in the expression for C_TT (thermocline) +% c_b1 = 2./3. ; % Shape factor (upper layer of bottom sediments) +% c_b2 = 3./5. ; % Shape factor (lower layer of bottom sediments) +% phi_b1_pr0 = 2. ; % B1 shape-function derivative +% c_s_lin = 0.5 ; % Shape factor (linear temperature profile in the snow layer) +% phi_s_pr0_lin = 1. ; % S shape-function derivative (linear profile) +% c_i_lin = 0.5 ; % Shape factor (linear temperature profile in the ice layer) +% phi_i_pr0_lin = 1. ; % I shape-function derivative (linear profile) +% phi_i_pr1_lin = 1. ; % I shape-function derivative (linear profile) +% phi_i_ast_mr = 2. ; % Constant in the MR2004 expression for I shape factor +% c_i_mr = 1./12. ; % Constant in the MR2004 expression for I shape factor +% h_ice_max = 3. ; % Maximum ice tickness in + % the Mironov and Ritter (2004, MR2004) ice model [m] + +% Security constants + h_snow_min_flk = 1.0E-5 ; % Minimum snow thickness [m] + h_ice_min_flk = 1.0E-3 ; % Minimum ice thickness [m] + h_ml_min_flk = 1.0E-2 ; % Minimum mixed-layer depth [m] + h_ml_max_flk = 1.0E+3 ; % Maximum mixed-layer depth [m] +% h_b1_min_flk = 1.0E-3 ; % Minimum thickness of the upper layer of bottom sediments [m] + u_star_min_flk = 1.0E-6 ; % Minimum value of the surface friction velocity [m s^{-1}] + +% Security constant(s) + c_small_flk = 1.0E-10 ; % A small number + +% Thermodynamic parameters + tpl_grav = 9.81 ; % Acceleration due to gravity [m s^{-2}] + tpl_t_r = 277.13 ; % Temperature of maximum density of fresh water [K] + tpl_t_f = 273.15 ; % Fresh water freezing point [K] + tpl_a_T = 1.6509E-05 ; % Constant in the fresh-water equation of state [K^{-2}] + tpl_rho_w_r = 1.0E+03 ; % Maximum density of fresh water [kg m^{-3}] +% tpl_rho_i = 9.1E+02 ; % Density of ice [kg m^{-3}] +% tpl_rho_s_min = 1.0E+02 ; % Minimum snow density [kg m^{-3}] +% tpl_rho_s_max = 4.0E+02 ; % Maximum snow density [kg m^{-3}] +% tpl_gamma_rho_s = 2.0E+02 ; % Empirical parameter [kg m^{-4}] + % in the expression for the snow density +% tpl_l_f = 3.34E+05 ; % Latent heat of fusion [J kg^{-1}] + tpl_c_w = 4.2E+03 ; % Specific heat of water [J kg^{-1} K^{-1}] +% tpl_c_i = 1.9E+03 ; % Specific heat of ice [J kg^{-1} K^{-1}] +% tpl_c_s = 2.1E+03 ; % Specific heat of snow [J kg^{-1} K^{-1}] +% tpl_kappa_w = 0.546 ; % Molecular heat conductivity of water [J m^{-1} s^{-1} K^{-1}] +% tpl_kappa_i = 2.29 ; % Molecular heat conductivity of ice [J m^{-1} s^{-1} K^{-1}] + + +%============================================================================== + +%============================================================================== +% +% Declarations + +% Input (procedure arguments) + +% The lake depth [m] +% real( ireals), intent(in) :: ; +% Depth of the thermally active layer of bottom sediments [m] +%depth_bs ,; +% Temperature at the outer edge of +%t_bs ,; +% the thermally active layer of bottom sediments [K] +% The Coriolis parameter [s^{-1}] +%par_coriolis; +% 'Typical' extinction coefficient of the lake water [m^{-1}], +%extincoef_water_typ ,; +% used to compute the equilibrium CBL depth +% The model time step [s] +%del_time ,; +% Surface temperature at the previous time step [K] +%t_sfc_p; +% (equal to either T_ice, T_snow or to T_wML) + +% Output (procedure arguments) + +% Updated surface temperature [K] +% (equal to the updated value of either T_ice, T_snow or T_wML) + +% Local variables of type LOGICAL +% Switch, true = ice does not exist but should be created +l_ice_create=false; +% Switch, true = there is snow above the ice +l_snow_exists=false; +% Switch, true = snow/ice melting from above takes place +l_ice_meltabove=[]; + +% Local variables of type INTEGER +% Loop index +i=0; +% Local variables of type REAL +% Time derivative of T_mnw [K s^{-1}] +d_t_mnw_dt=0; +% Time derivative of T_ice [K s^{-1}] +d_t_ice_dt=0; +% Time derivative of T_bot [K s^{-1}] +d_t_bot_d=0; +% Time derivative of T_B1 [K s^{-1}] +d_t_b1_dt=0; +% Time derivative of h_snow [m s^{-1}] +d_h_snow_dt=0; +% Time derivative of h_ice [m s^{-1}] +d_h_ice_dt=0; +% Time derivative of h_ML [m s^{-1}] +d_h_ml_dt=0; +% Time derivative of H_B1 [m s^{-1}] +d_h_b1_dt=0; +% Time derivative of C_T [s^{-1}] +d_c_t_dt=0; + +% Local variables of type REAL +% The mean buoyancy frequency in the thermocline [s^{-1}] +n_t_mean=0; + +% The ZM96 equilibrium SBL depth scale [m] +zm_h_scale=0; +% The equilibrium CBL depth scale [m] +conv_equil_h_scale=0; + +% Local variables of type REAL +% If h_ice=h_ice_min_flk) +% % Mixed-layer depth is zero, compute flux +% if(h_ml_p_flk<=h_ml_min_flk) +% % Flux with linear T(z) +% q_w_flk = -tpl_kappa_w.*(t_bot_p_flk-t_wml_p_flk)./depth_w; +% % d\Phi(0)/d\zeta (thermocline) +% phi_t_pr0_flk = phi_t_pr0_1.*c_t_p_flk-phi_t_pr0_2; +% % Account for an increased d\Phi(0)/d\zeta +% q_w_flk = q_w_flk.*max(phi_t_pr0_flk, 1.); +% %q_w_flk = FLAKE.q_w_flk;%!!! use external q_w_flk +% else +% % Mixed-layer depth is greater than zero, set flux to zero +% q_w_flk = 0.; +% end +% end; +% % disp([q_w_flk FLAKE.q_ice_flk]) + +% A generalized heat flux scale + q_star_flk = q_w_flk + i_w_flk + i_h_flk - 2..*i_intm_0_h_flk; + +% %Heat flux through the water-bottom sediment interface +% if(lflk_botsed_use) +% q_bot_flk = -tpl_kappa_w.*(t_b1_p_flk-t_bot_p_flk)./max(h_b1_p_flk, h_b1_min_flk).*phi_b1_pr0; +% else +% % The bottom-sediment scheme is not used +% q_bot_flk = 0.; +% end; +% +% couple to CryoGrid soil scheme +% set external t_b1_p_flk = temperature of first soil cell +% set external h_b1_p_flk = delta K grid of first soil cell +% tpl_kappa_w = heat conductivity is assumed to be water +% +%q_bot_flk = - k_b1_p_flk .* (t_b1_p_flk - t_bot_p_flk) ./ h_b1_p_flk; + +%q_bot_flk = 0; +q_bot_flk = FLAKE.q_bot_flk; + + +% %------------------------------------------------------------------------------ +% % Check if ice exists or should be created. +% % If so, compute the thickness and the temperature of ice and snow. +% %------------------------------------------------------------------------------ +% % +% %_dm +% % Notice that a quasi-equilibrium ice-snow model is used +% % to avoid numerical instability when the ice is thin. +% % This is always the case when new ice is created. +% %_dm +% +% %_dev +% % The dependence of snow density and of snow heat conductivity +% % on the snow thickness is accounted for parametrically. +% % That is, the time derivatives of \rho_S and \kappa_S are neglected. +% % The exception is the equation for the snow thickness +% % in case of snow accumulation and no melting, +% % where d\rho_S/dt is incorporated. +% % Furthermore, some (presumably small) correction terms incorporating +% % the snow density and the snow heat conductivity are dropped out. +% % Those terms may be included as better formulations +% % for \rho_S and \kappa_S are available. +% %_dev +% +% % Default values +% l_ice_create = false; +% l_ice_meltabove = false; +% +% +% % Ice does not exist +% if(h_ice_p_flk0) +% h_snow_n_flk = h_snow_p_flk + d_h_snow_dt.*del_time; +% % d\Phi_I(1)/d\zeta_I (ice) +% phi_i_pr1_flk = phi_i_pr1_lin+ phi_i_ast_mr.*min(1., h_ice_n_flk./h_ice_max); +% r_h_icesnow = phi_i_pr1_flk./phi_s_pr0_lin.*tpl_kappa_i./flake_snowheatconduct(h_snow_n_flk).* h_snow_n_flk./max(h_ice_n_flk, h_ice_min_flk); +% % Snow temperature +% t_snow_n_flk = t_ice_n_flk + r_h_icesnow.*(t_ice_n_flk-tpl_t_f); +% else +% h_snow_n_flk=0; +% r_h_icesnow=0; +% t_snow_n_flk=t_ice_n_flk; +% end +% +% end +% +% % Ice exists +% else +% +% % Check if there is snow above the ice +% l_snow_exists = h_snow_p_flk>=h_snow_min_flk; +% +% % T_sfc = T_f, check for melting from above +% if(t_snow_p_flk>=(tpl_t_f-c_small_flk)) +% % T_snow = T_ice if snow is absent +% % There is snow above the ice +% if(l_snow_exists) +% % Atmospheric forcing +% flk_str_1 = q_snow_flk + i_snow_flk - i_ice_flk; +% % Melting of snow and ice from above +% if(flk_str_1>=0.) +% l_ice_meltabove = true; +% d_h_snow_dt =(-flk_str_1./tpl_l_f+dmsnowdt_flk)./flake_snowdensity(h_snow_p_flk); +% d_h_ice_dt = -(i_ice_flk - i_w_flk - q_w_flk)./tpl_l_f./tpl_rho_i; +% end +% % No snow above the ice +% else +% % Atmospheric forcing + heating from the water +% flk_str_1 = q_ice_flk + i_ice_flk - i_w_flk - q_w_flk; +% % Melting of ice from above, snow accumulation may occur +% if(flk_str_1>=0.) +% l_ice_meltabove = true; +% d_h_ice_dt = -flk_str_1./tpl_l_f./tpl_rho_i; +% d_h_snow_dt = dmsnowdt_flk./tpl_rho_s_min; +% end +% end +% % Melting from above takes place +% if(l_ice_meltabove) +% % Advance h_ice +% h_ice_n_flk = h_ice_p_flk + d_h_ice_dt .*del_time; +% % Advance h_snow +% h_snow_n_flk = h_snow_p_flk + d_h_snow_dt.*del_time; +% % Set T_ice to the freezing point +% t_ice_n_flk = tpl_t_f; +% % Set T_snow to the freezing point +% t_snow_n_flk = tpl_t_f; +% end +% +% end +% +% % No melting from above +% if(~l_ice_meltabove) +% +% %d_h_snow_dt = flake_snowdensity(h_snow_p_flk); +% d_h_snow_dt = 0; %!!! avoid snow cover +% % Account for d\rho_S/dt +% if(d_h_snow_dt=h_ice_min_flk) + % Limit the mean temperature under the ice by T_r + t_mnw_n_flk = min(t_mnw_n_flk, tpl_t_r); + % The mixed-layer temperature is equal to the freezing point + t_wml_n_flk = tpl_t_f; + + % Ice has just been created + if(l_ice_create) + % h_ML=D when ice is created +% if (h_ml_p_flk>=depth_w-h_ml_min_flk) %!!! changed since a persistent isothermal layer is not observed + % Set h_ML to zero + h_ml_n_flk = 0.; + % Set C_T to its minimum value + c_t_n_flk = c_t_min; + % h_ML=0) %!!! i_w_flk-q_bot_flk>=0 (warming under ice) + % h_ML > 0 + if(h_ml_p_flk>=c_small_flk) + % C_T (thermocline) remains unchanged + c_t_n_flk = c_t_p_flk; + h_ml_n_flk = depth_w.*(1.-(t_wml_n_flk-t_mnw_n_flk)./(t_wml_n_flk-t_bot_n_flk)./c_t_n_flk); + % Update the mixed-layer depth + h_ml_n_flk = max(h_ml_n_flk, 0.); + % h_ML = 0 + else + % h_ML remains unchanged + h_ml_n_flk = h_ml_p_flk; + c_t_n_flk =(t_wml_n_flk-t_mnw_n_flk)./(t_wml_n_flk-t_bot_n_flk); + % Update the shape factor (thermocline) + c_t_n_flk = min(c_t_max, max(c_t_n_flk, c_t_min)); + end + + else %!!! i_w_flk-q_bot_flk<0 (cooling from the bottom) + + %h_ML remains unchanged + h_ml_n_flk = h_ml_p_flk; + % C_T (thermocline) remains unchanged + c_t_n_flk = c_t_p_flk; + t_bot_n_flk = t_wml_n_flk -(t_wml_n_flk-t_mnw_n_flk)./c_t_n_flk./(1.-h_ml_n_flk./depth_w); + % Update the bottom temperature + + end + + end + + % Security, limit the bottom temperature by T_r + t_bot_n_flk = min(t_bot_n_flk, tpl_t_r); + +% Open water +else + + % Generalised buoyancy flux scale and convective velocity scale + T_water=t_wml_p_flk; + flake_buoypar = tpl_grav*tpl_a_T*(T_water-tpl_t_r); + flk_str_1 = flake_buoypar*q_star_flk/tpl_rho_w_r/tpl_c_w; + if (flk_str_1<0.) + % Convection + w_star_sfc_flk =(-flk_str_1.*h_ml_p_flk).^(1../3.); + else + % Neutral or stable stratification + w_star_sfc_flk = 0.; + end + + %_dm + % The equilibrium depth of the CBL due to surface cooling with the volumetric heating + % is not computed as a solution to the transcendental equation. + % Instead, an algebraic formula is used + % that interpolates between the two asymptotic limits. + %_dm + conv_equil_h_scale = -q_w_flk/max(i_w_flk, c_small_flk); + % The equilibrium CBL depth scale is only used above T_r + if(conv_equil_h_scale>0. && conv_equil_h_scale<1.&& t_wml_p_flk>tpl_t_r) + conv_equil_h_scale = sqrt(6..*conv_equil_h_scale)+ 2..*conv_equil_h_scale./(1.-conv_equil_h_scale); + conv_equil_h_scale = min(depth_w, conv_equil_h_scale./extincoef_water_typ); + else + % Set the equilibrium CBL depth to zero + conv_equil_h_scale = 0.; + end; + + % Mean buoyancy frequency in the thermocline + T_water=0.5.*(t_wml_p_flk+t_bot_p_flk); + flake_buoypar = tpl_grav*tpl_a_T*(T_water-tpl_t_r); + n_t_mean = flake_buoypar.*(t_wml_p_flk-t_bot_p_flk); + if(h_ml_p_flk<=depth_w-h_ml_min_flk) && (n_t_mean>=0) %!!! && (n_t_mean>=0) added to avoid complex numbers + % Compute N + n_t_mean = sqrt(n_t_mean./(depth_w-h_ml_p_flk)); + else + % h_ML=D, set N to zero + n_t_mean = 0.; + end + + % The rate of change of C_T + d_c_t_dt = max([w_star_sfc_flk, u_star_min_flk]).^2; + % Relaxation time scale for C_T + d_c_t_dt = n_t_mean.*(depth_w-h_ml_p_flk).^2./ c_relax_c./d_c_t_dt; + % Rate-of-change of C_T + d_c_t_dt =(c_t_max-c_t_min)./max(d_c_t_dt, c_small_flk); + + % Compute the shape factor and the mixed-layer depth, + % using different formulations for convection and wind mixing + + % C_TT, using C_T at the previous time step + c_tt_flk = c_tt_1.*c_t_p_flk-c_tt_2; + % C_Q using C_T at the previous time step + c_q_flk = 2..*c_tt_flk./c_t_p_flk; + + + % Convective mixing + if(flk_str_1<0.) + + % Update C_T, assuming dh_ML/dt>0 + c_t_n_flk = c_t_p_flk + d_c_t_dt.*del_time; + % Limit C_T + c_t_n_flk = min(c_t_max, max(c_t_n_flk, c_t_min)); + % Re-compute dC_T/dt + d_c_t_dt =(c_t_n_flk-c_t_p_flk)./del_time; + + % Compute dh_ML/dt + if(h_ml_p_flk<=depth_w-h_ml_min_flk) + % Use a reduced entrainment equation (spin-up) + if(h_ml_p_flk<=h_ml_min_flk) + d_h_ml_dt = c_cbl_1./c_cbl_2.*max(w_star_sfc_flk, c_small_flk); + + %_dbg + % WRITE*, ' FLake: reduced entrainment eq. D_time*d_h_ML_dt = ', d_h_ML_dt*del_time + % WRITE*, ' w_* = ', w_star_sfc_flk + % WRITE*, ' \beta*Q_* = ', flk_str_1 + %_dbg + + % Use a complete entrainment equation + else + r_h_icesnow = depth_w./h_ml_p_flk; + r_rho_c_icesnow = r_h_icesnow-1.; + r_ti_icesnow = c_t_p_flk./c_tt_flk; + r_tstar_icesnow =(r_ti_icesnow./2.-1.).*r_rho_c_icesnow + 1.; + d_h_ml_dt = -q_star_flk.*(r_tstar_icesnow.*(1.+c_cbl_1)-1.) - q_bot_flk; + % Q_* and Q_b flux terms + d_h_ml_dt = d_h_ml_dt./tpl_rho_w_r./tpl_c_w; + flk_str_2 =(depth_w-h_ml_p_flk).*(t_wml_p_flk-t_bot_p_flk).*c_tt_2./c_tt_flk.*d_c_t_dt; + % Add dC_T/dt term + d_h_ml_dt = d_h_ml_dt + flk_str_2; + flk_str_2 = i_bot_flk +(r_ti_icesnow-1.).*i_h_flk - r_ti_icesnow.*i_intm_h_d_flk; + flk_str_2 = flk_str_2 +(r_ti_icesnow-2.).*r_rho_c_icesnow.*(i_h_flk-i_intm_0_h_flk); + flk_str_2 = flk_str_2./tpl_rho_w_r./tpl_c_w; + % Add radiation terms + d_h_ml_dt = d_h_ml_dt + flk_str_2; + flk_str_2 = -c_cbl_2.*r_tstar_icesnow.*q_star_flk./tpl_rho_w_r./tpl_c_w./max(w_star_sfc_flk, c_small_flk); + flk_str_2 = flk_str_2 + c_t_p_flk.*(t_wml_p_flk-t_bot_p_flk); + % dh_ML/dt = r.h.s. + d_h_ml_dt = d_h_ml_dt./flk_str_2; + end + %_dm + % Notice that dh_ML/dt may appear to be negative + % (e.g. due to buoyancy loss to bottom sediments and/or + % the effect of volumetric radiation heating), + % although a negative generalized buoyancy flux scale indicates + % that the equilibrium CBL depth has not yet been reached + % and convective deepening of the mixed layer should take place. + % Physically, this situation reflects an approximate character of the lake model. + % Using the self-similar temperature profile in the thermocline, + % there is always communication between the mixed layer, the thermocline + % and the lake bottom. As a result, the rate of change of the CBL depth + % is always dependent on the bottom heat flux and the radiation heating of the thermocline. + % In reality, convective mixed-layer deepening may be completely decoupled + % from the processes underneath. In order to account for this fact, + % the rate of CBL deepening is set to a small value + % if dh_ML/dt proves to be negative. + % This is 'double insurance' however, + % as a negative dh_ML/dt is encountered very rarely. + %_dm + + %_dbg + % IF(d_h_ML_dt.LT.0.) THEN + % WRITE*, 'FLake: negative d_h_ML_dt during convection, = ', d_h_ML_dt + % WRITE*, ' d_h_ML_dt*del_time = ', MAX(d_h_ML_dt, c_small_flk)*del_time + % WRITE*, ' u_* = ', u_star_w_flk + % WRITE*, ' w_* = ', w_star_sfc_flk + % WRITE*, ' h_CBL_eqi = ', conv_equil_h_scale + % WRITE*, ' ZM scale = ', ZM_h_scale + % WRITE*, ' h_ML_p_flk = ', h_ML_p_flk + % endif + % WRITE*, 'FLake: Convection, = ', d_h_ML_dt + % WRITE*, ' Q_* = ', Q_star_flk + % WRITE*, ' \beta*Q_* = ', flk_str_1 + %_dbg + + d_h_ml_dt = max(d_h_ml_dt, c_small_flk); + % Update h_ML + h_ml_n_flk = h_ml_p_flk + d_h_ml_dt.*del_time; + % Security, limit h_ML + h_ml_n_flk = max(h_ml_min_flk, min(h_ml_n_flk, depth_w)); + % Mixing down to the lake bottom + else + h_ml_n_flk = depth_w; + %disp('convec 2 bottom') + end + + % Wind mixing + else + %disp(' wind mixing') + % The surface friction velocity + d_h_ml_dt = max(u_star_w_flk, u_star_min_flk); + zm_h_scale =(abs(par_coriolis)./c_sbl_zm_n + n_t_mean./c_sbl_zm_i).*d_h_ml_dt.^2; + zm_h_scale = zm_h_scale + flk_str_1./c_sbl_zm_s; + zm_h_scale = max(zm_h_scale, c_small_flk); + zm_h_scale = d_h_ml_dt.^3./zm_h_scale; + % The ZM96 SBL depth scale + zm_h_scale = max(h_ml_min_flk, min(zm_h_scale, h_ml_max_flk)); + % Equilibrium mixed-layer depth + zm_h_scale = max(zm_h_scale, conv_equil_h_scale); + + %_dm + % In order to avoid numerical discretization problems, + % an analytical solution to the evolution equation + % for the wind-mixed layer depth is used. + % That is, an exponential relaxation formula is applied + % over the time interval equal to the model time step. + %_dm + + d_h_ml_dt = c_relax_h.*d_h_ml_dt./zm_h_scale.*del_time; + % Update h_ML + h_ml_n_flk = zm_h_scale -(zm_h_scale-h_ml_p_flk).*exp(-d_h_ml_dt); + % Limit h_ML + h_ml_n_flk = max(h_ml_min_flk, min(h_ml_n_flk, depth_w)); + % Re-compute dh_ML/dt + d_h_ml_dt =(h_ml_n_flk-h_ml_p_flk)./del_time; + % Mixed-layer retreat or stationary state, dC_T/dt<0 + if(h_ml_n_flk<=h_ml_p_flk) + d_c_t_dt = -d_c_t_dt; + end + % Update C_T + c_t_n_flk = c_t_p_flk + d_c_t_dt.*del_time; + % Limit C_T + c_t_n_flk = min(c_t_max, max(c_t_n_flk, c_t_min)); + % Re-compute dC_T/dt + d_c_t_dt =(c_t_n_flk-c_t_p_flk)./del_time; + + %_dbg + % WRITE*, 'FLake: wind mixing: d_h_ML_dt*del_time = ', d_h_ML_dt*del_time + % WRITE*, ' h_CBL_eqi = ', conv_equil_h_scale + % WRITE*, ' ZM scale = ', ZM_h_scale + % WRITE*, ' w_* = ', w_star_sfc_flk + % WRITE*, ' u_* = ', u_star_w_flk + % WRITE*, ' h_ML_p_flk = ', h_ML_p_flk + %_dbg + + end + + % Compute the time-rate-of-change of the the bottom temperature, + % depending on the sign of dh_ML/dt + % Update the bottom temperature and the mixed-layer temperature + + % Mixing did not reach the bottom + if(h_ml_n_flk<=depth_w-h_ml_min_flk) + + % Mixed-layer deepening + if(h_ml_n_flk>h_ml_p_flk) + r_h_icesnow = h_ml_p_flk./depth_w; + r_rho_c_icesnow = 1.-r_h_icesnow; + r_ti_icesnow = 0.5.*c_t_p_flk.*r_rho_c_icesnow+c_tt_flk.*(2..*r_h_icesnow-1.); + r_tstar_icesnow =(0.5+c_tt_flk-c_q_flk)./r_ti_icesnow; + r_ti_icesnow =(1.-c_t_p_flk.*r_rho_c_icesnow)./r_ti_icesnow; + + d_t_bot_dt =(q_w_flk-q_bot_flk+i_w_flk-i_bot_flk)./tpl_rho_w_r./tpl_c_w; + d_t_bot_dt = d_t_bot_dt - c_t_p_flk.*(t_wml_p_flk-t_bot_p_flk).*d_h_ml_dt; + % Q+I fluxes and dh_ML/dt term + d_t_bot_dt = d_t_bot_dt.*r_tstar_icesnow./depth_w; + + flk_str_2 = i_intm_h_d_flk -(1.-c_q_flk).*i_h_flk - c_q_flk.*i_bot_flk; + flk_str_2 = flk_str_2.*r_ti_icesnow./(depth_w-h_ml_p_flk)./tpl_rho_w_r./tpl_c_w; + % Add radiation-flux term + d_t_bot_dt = d_t_bot_dt + flk_str_2; + + flk_str_2 =(1.-c_tt_2.*r_ti_icesnow)./c_t_p_flk; + flk_str_2 = flk_str_2.*(t_wml_p_flk-t_bot_p_flk).*d_c_t_dt; + % Add dC_T/dt term + d_t_bot_dt = d_t_bot_dt + flk_str_2; + + % Mixed-layer retreat or stationary state + else + % dT_bot/dt=0 + d_t_bot_dt = 0.; + end + + % Update T_bot + t_bot_n_flk = t_bot_p_flk + d_t_bot_dt.*del_time; + % Security, limit T_bot by the freezing point + t_bot_n_flk = max(t_bot_n_flk, tpl_t_f); + T_water=t_mnw_n_flk; + tpl = tpl_grav*tpl_a_T*(T_water-tpl_t_r); + flk_str_2 =(t_bot_n_flk-tpl_t_r)*flake_buoypar; + % Security, avoid T_r crossover + if(flk_str_2<0.) + t_bot_n_flk = tpl_t_r; + end + t_wml_n_flk = c_t_n_flk.*(1.-h_ml_n_flk./depth_w); + t_wml_n_flk =(t_mnw_n_flk-t_bot_n_flk.*t_wml_n_flk)./(1.-t_wml_n_flk); + % Security, limit T_wML by the freezing point + t_wml_n_flk = max(t_wml_n_flk, tpl_t_f); + + % Mixing down to the lake bottom + else + + h_ml_n_flk = depth_w; + t_wml_n_flk = t_mnw_n_flk; + t_bot_n_flk = t_mnw_n_flk; + c_t_n_flk = c_t_min; + + end + +end + + +%------------------------------------------------------------------------------ +% Compute the depth of the upper layer of bottom sediments +% and the temperature at that depth. +%------------------------------------------------------------------------------ + +% % The bottom-sediment scheme is used +% if(lflk_botsed_use) +% +% % No T(z) maximum (no thermal wave) +% if(h_b1_p_flk>=depth_bs-h_b1_min_flk) +% % Set H_B1_p to zero +% h_b1_p_flk = 0.; +% % Set T_B1_p to the bottom temperature +% t_b1_p_flk = t_bot_p_flk; +% end +% +% flk_str_1 = 2..*phi_b1_pr0./(1.-c_b1).*tpl_kappa_w./tpl_rho_w_r./tpl_c_w.*del_time; +% % Threshold value of H_B1 +% h_ice_threshold = sqrt(flk_str_1); +% % Limit H_B1 +% h_ice_threshold = min(0.9.*depth_bs, h_ice_threshold); +% flk_str_2 = c_b2./(1.-c_b2).*(t_bs-t_b1_p_flk)./(depth_bs-h_b1_p_flk); +% +% % Use a truncated equation for H_B1(t) +% if(h_b1_p_flk=h_snow_min_flk) + % Snow exists, use the snow temperature + t_sfc_n = t_snow_n_flk; +elseif(h_ice_n_flk>=h_ice_min_flk) + % Ice exists but there is no snow, use the ice temperature + t_sfc_n = t_ice_n_flk; +else + % No ice-snow cover, use the mixed-layer temperature + t_sfc_n = t_wml_n_flk; +end + +%------------------------------------------------------------------------------ +% End calculations +%============================================================================== + +FLAKE.t_snow_n_flk = t_snow_n_flk; +FLAKE.t_ice_n_flk = t_ice_n_flk; +FLAKE.t_ice_p_flk = t_ice_p_flk; +FLAKE.t_wml_n_flk = t_wml_n_flk; +FLAKE.t_wml_p_flk = t_wml_p_flk; +FLAKE.t_mnw_n_flk = t_mnw_n_flk; +FLAKE.t_mnw_p_flk = t_mnw_p_flk; +FLAKE.d_t_mnw_dt = d_t_mnw_dt; +FLAKE.t_bot_n_flk = t_bot_n_flk; +FLAKE.t_bot_p_flk = t_bot_p_flk; +FLAKE.h_snow_n_flk = h_snow_n_flk; +FLAKE.h_ice_n_flk = h_ice_n_flk; +FLAKE.h_ml_n_flk = h_ml_n_flk; +FLAKE.c_t_n_flk = c_t_n_flk; +FLAKE.c_i_flk = c_i_flk; +FLAKE.q_bot_flk = q_bot_flk; +FLAKE.d_h_ice_dt = d_h_ice_dt; +FLAKE.q_w_flk = q_w_flk; + + diff --git a/modules/cryoGridLake/flake_radflux.m b/modules/cryoGridLake/flake_radflux.m new file mode 100644 index 0000000..2b3d414 --- /dev/null +++ b/modules/cryoGridLake/flake_radflux.m @@ -0,0 +1,65 @@ + +function [i_atm_flk, i_w_flk, i_ice_flk, i_snow_flk, i_h_flk, i_bot_flk, i_intm_0_h_flk, i_intm_h_d_flk]=flake_radflux(flake_SWnet,FLAKE,PARA) %#codegen + +%FlakeParameters +% Security constants + h_ice_min_flk = 1.0E-3 ; % Minimum ice thickness [m] + h_ml_min_flk = 1.0E-2 ; % Minimum mixed-layer depth [m] + +%-------------------------------------------------------------------------- +blueice_extincoef_optic=PARA.ice.extinction; +blueice_frac_optic=1; + +water_extincoef_optic=PARA.water.extinction; +water_frac_optic=1; + +% transwater_extincoef_optic=0.3; +% transwater_frac_optic=1; +% +% drysnow.extincoef_optic=25; +% drysnow.frac_optic=1; +% +% meltingsnow.extincoef_optic=15; +% meltingsnow.frac_optic=1; +%-------------------------------------------------------------------------- + +i_atm_flk=flake_SWnet; %net sw radiation at the top of the ice cover +h_ice_p_flk=0; %set to zero due to external ice cover scheme +h_ml_p_flk=FLAKE.h_ml_n_flk; + + + +if (h_ice_p_flk >= h_ice_min_flk) % ice exists (snow is excluded) + i_snow_flk = i_atm_flk; + i_ice_flk = i_atm_flk; + i_bot_flk = blueice_frac_optic .* exp(-blueice_extincoef_optic .* h_ice_p_flk); + i_w_flk = i_ice_flk .* i_bot_flk; +else % no ice cover + i_snow_flk = i_atm_flk; + i_ice_flk = i_atm_flk; + i_w_flk = i_atm_flk; +end + + if(h_ml_p_flk >= h_ml_min_flk) %there is a mixed layer + i_bot_flk = water_frac_optic.*exp(-water_extincoef_optic .* h_ml_p_flk); + i_h_flk = i_w_flk*i_bot_flk; + else % mixed layer depth is less then a minimum value + i_h_flk = i_w_flk; + end + + i_bot_flk = water_frac_optic .* exp(-water_extincoef_optic * (FLAKE.depth_w)); + i_bot_flk = i_w_flk.*i_bot_flk; + +if(h_ml_p_flk >= h_ml_min_flk) %integral-mean radiation flux over the mixed layer + i_intm_0_h_flk = water_frac_optic ./ water_extincoef_optic.* (1 - exp(-water_extincoef_optic .* h_ml_p_flk)); + i_intm_0_h_flk = i_w_flk .* i_intm_0_h_flk ./ h_ml_p_flk; +else + i_intm_0_h_flk = i_h_flk; +end + +if(h_ml_p_flk <= FLAKE.depth_w - h_ml_min_flk) % integral-mean radiation flux over the thermocline + i_intm_h_d_flk = water_frac_optic ./ water_extincoef_optic .* ( exp(-water_extincoef_optic .* h_ml_p_flk) - exp(-water_extincoef_optic .* FLAKE.depth_w) ); + i_intm_h_d_flk = i_w_flk .* i_intm_h_d_flk ./ (FLAKE.depth_w-h_ml_p_flk); +else + i_intm_h_d_flk = i_h_flk; +end \ No newline at end of file diff --git a/modules/cryoGridLake/flake_roughnessLength.m b/modules/cryoGridLake/flake_roughnessLength.m new file mode 100644 index 0000000..fdaf4c0 --- /dev/null +++ b/modules/cryoGridLake/flake_roughnessLength.m @@ -0,0 +1,174 @@ +function [z0u, z0t, z0q]=flake_roughnessLength(fetch, u_a, u_star, h_ice_p_flk) + +%------------------------------------------------------------------------------ +% +% Description: +% +% Computes the water-surface or the ice-surface roughness lengths +% with respect to wind velocity, potential temperature and specific humidity. +% +% The water-surface roughness lengths with respect to wind velocity is computed +% from the Charnock formula when the surface is aerodynamically rough. +% A simple empirical formulation is used to account for the dependence +% of the Charnock parameter on the wind fetch. +% When the flow is aerodynamically smooth, the roughness length with respect to +% wind velocity is proportional to the depth of the viscous sub-layer. +% The water-surface roughness lengths for scalars are computed using the power-law +% formulations in terms of the roughness Reynolds number (Zilitinkevich et al. 2001). +% The ice-surface aerodynamic roughness is taken to be constant. +% The ice-surface roughness lengths for scalars +% are computed through the power-law formulations +% in terms of the roughness Reynolds number (Andreas 2002). +% +% +% Current Code Owner: DWD, Dmitrii Mironov +% Phone: +49-69-8062 2705 +% Fax: +49-69-8062 3721 +% E-mail: dmitrii.mironov@dwd.de +% +% History: +% Version Date Name +% ---------- ---------- ---- +% 1.00 2005/11/17 Dmitrii Mironov +% Initial release +% 1.01 2014/08/09 Moritz Langer +% - modifited to work with CryoGrid3 +% +% Code Description: +% Language: Fortran 90. +% Software Standards: 'European Standards for Writing and +% Documenting Exchangeable Fortran 90 Code'. +%============================================================================== + +% fetch: Typical wind fetch [m] +% u_a: Wind speed [m s^{-1}] +% u_star: Friction velocity in the surface air layer [m s^{-1}] +% h_ice: Ice thickness [m] + +%Flake constants +%============================================================================== + + c_Karman = 0.40 ; % The von Karman constant + Pr_neutral = 1.0 ; % Turbulent Prandtl number at neutral static stability + Sc_neutral = 1.0 ; % Turbulent Schmidt number at neutral static stability + + + z0u_ice_rough = 1.0E-03 ; % Aerodynamic roughness of the ice surface [m] (rough flow) + c_z0u_smooth = 0.1 ; % Constant in the expression for z0u (smooth flow) + c_z0u_rough = 1.23E-02 ; % The Charnock constant in the expression for z0u (rough flow) + c_z0u_rough_L = 1.00E-01 ; % An increased Charnock constant (used as the upper limit) + c_z0u_ftch_f = 0.70 ; % Factor in the expression for fetch-dependent Charnock parameter + c_z0u_ftch_ex = 0.3333333 ; % Exponent in the expression for fetch-dependent Charnock parameter + c_z0t_rough_1 = 4.0 ; % Constant in the expression for z0t (factor) + c_z0t_rough_2 = 3.2 ; % Constant in the expression for z0t (factor) + c_z0t_rough_3 = 0.5 ; % Constant in the expression for z0t (exponent) + c_z0q_rough_1 = 4.0 ; % Constant in the expression for z0q (factor) + c_z0q_rough_2 = 4.2 ; % Constant in the expression for z0q (factor) + c_z0q_rough_3 = 0.5 ; % Constant in the expression for z0q (exponent) + c_z0t_ice_b0s = 1.250 ; % Constant in the expression for z0t over ice + c_z0t_ice_b0t = 0.149 ; % Constant in the expression for z0t over ice + c_z0t_ice_b1t = -0.550 ; % Constant in the expression for z0t over ice + c_z0t_ice_b0r = 0.317 ; % Constant in the expression for z0t over ice + c_z0t_ice_b1r = -0.565 ; % Constant in the expression for z0t over ice + c_z0t_ice_b2r = -0.183 ; % Constant in the expression for z0t over ice + c_z0q_ice_b0s = 1.610 ; % Constant in the expression for z0q over ice + c_z0q_ice_b0t = 0.351 ; % Constant in the expression for z0q over ice + c_z0q_ice_b1t = -0.628 ; % Constant in the expression for z0q over ice + c_z0q_ice_b0r = 0.396 ; % Constant in the expression for z0q over ice + c_z0q_ice_b1r = -0.512 ; % Constant in the expression for z0q over ice + c_z0q_ice_b2r = -0.180 ; % Constant in the expression for z0q over ice + + re_z0s_ice_t = 2.5 ; % Threshold value of the surface Reynolds number + % used to compute z0t and z0q over ice (Andreas 2002) + re_z0u_thresh = 0.1; % Threshold value of the roughness Reynolds number + % [value from Zilitinkevich, Grachev, and Fairall (200), + % currently not used] + + +% Thermodynamic parameters + tpl_grav = 9.81 ; % Acceleration due to gravity [m s^{-2}] + tpsf_R_dryair = 2.8705E+02 ; % Gas constant for dry air [J kg^{-1} K^{-1}] + tpsf_R_watvap = 4.6151E+02 ; % Gas constant for water vapour [J kg^{-1} K^{-1}] + + + tpsf_nu_u_a = 1.50E-05 ; % Kinematic molecular viscosity of air [m^{2} s^{-1}] + + +% Security constants + u_wind_min_sf = 1.0E-02 ; % Minimum wind speed [m s^{-1}] + h_Ice_min_flk = 1.0E-3 ; % Minimum ice thickness [m] + +% Useful constants +% num_1o3_sf = 1./3.; % 1/3 + +%Flake parameters +%============================================================================== + + +%============================================================================== +% Start calculations +%------------------------------------------------------------------------------ + +% Water surface +if(h_ice_p_flk < h_Ice_min_flk) + + % The Charnock parameter as dependent on dimensionless fetch + % Inverse dimensionless fetch + c_z0u_fetch = max(u_a, u_wind_min_sf).^2./tpl_grav./fetch; + c_z0u_fetch = c_z0u_rough + c_z0u_ftch_f.*c_z0u_fetch.^c_z0u_ftch_ex; + % Limit Charnock parameter + c_z0u_fetch = min(c_z0u_fetch, c_z0u_rough_L); + + % Threshold value of friction velocity + %u_star_thresh =(c_z0u_smooth./c_z0u_fetch.*tpl_grav.*tpsf_nu_u_a).^num_1o3_sf; + + % Surface Reynolds number and its threshold value + re_s = u_star.^3./tpsf_nu_u_a./tpl_grav; + re_s_thresh = c_z0u_smooth./c_z0u_fetch; + + % Aerodynamic roughness + if(re_s <= re_s_thresh) + % Smooth flow + z0u = c_z0u_smooth.*tpsf_nu_u_a./u_star; + else + % Rough flow + z0u = c_z0u_fetch.*u_star.*u_star./tpl_grav; + end + % Roughness for scalars + z0q = c_z0u_fetch.*max(re_s, re_s_thresh); + z0t = c_z0t_rough_1.*z0q.^c_z0t_rough_3 - c_z0t_rough_2; + z0q = c_z0q_rough_1.*z0q.^c_z0q_rough_3 - c_z0q_rough_2; + z0t = z0u.*exp(-c_Karman./Pr_neutral.*z0t); + z0q = z0u.*exp(-c_Karman./Sc_neutral.*z0q); + +% Ice surface +else + + % Threshold value of friction velocity + %u_star_thresh = c_z0u_smooth.*tpsf_nu_u_a./z0u_ice_rough; + + % Aerodynamic roughness + z0u = max(z0u_ice_rough, c_z0u_smooth.*tpsf_nu_u_a./u_star); + + % Roughness Reynolds number + re_s = max(u_star.*z0u./tpsf_nu_u_a, 1.0E-07 ); + + % Roughness for scalars + if(re_s<=re_z0s_ice_t) + z0t = c_z0t_ice_b0t + c_z0t_ice_b1t.*log(re_s); + z0t = min(z0t, c_z0t_ice_b0s); + z0q = c_z0q_ice_b0t + c_z0q_ice_b1t.*log(re_s); + z0q = min(z0q, c_z0q_ice_b0s); + else + z0t = c_z0t_ice_b0r + c_z0t_ice_b1r.*log(re_s) + c_z0t_ice_b2r.*log(re_s).^2; + z0q = c_z0q_ice_b0r + c_z0q_ice_b1r.*log(re_s) + c_z0q_ice_b2r.*log(re_s).^2; + end + z0t = z0u.*exp(z0t); + z0q = z0u.*exp(z0q); + +end + + +%------------------------------------------------------------------------------ +% End calculations +%============================================================================== diff --git a/modules/cryoGridLake/heatConductionIceCover.m b/modules/cryoGridLake/heatConductionIceCover.m new file mode 100644 index 0000000..68d216c --- /dev/null +++ b/modules/cryoGridLake/heatConductionIceCover.m @@ -0,0 +1,28 @@ +function [dE_dt_cond GRID]= heatConductionIceCover(dE_dt_cond, T, k_eff, k_temp, GRID) + +GRID.lake.ice.dE_dt_cond_residual=0; +if ~isempty(GRID.lake.ice.cT_domain_ub) && ~isempty(GRID.lake.water.cT_domain_ub) + dE_dt_cond_save = dE_dt_cond(GRID.lake.ice.cT_domain_lb) + dE_dt_cond(GRID.lake.ice.cT_domain_lb+1); + + if sum(GRID.lake.ice.cT_domain)<2 + + dE_dt_cond(GRID.lake.ice.cT_domain_lb) = k_temp(GRID.lake.ice.K_domain_lb) .* (0-T(GRID.lake.ice.cT_domain_lb)) ... + ./ GRID.general.K_delta(GRID.lake.ice.K_domain_lb)./2; + else + + dE_dt_cond(GRID.lake.ice.cT_domain_lb) = k_temp(GRID.lake.ice.K_domain_lb) .* (0-T(GRID.lake.ice.cT_domain_lb)) ... + ./ GRID.general.K_delta(GRID.lake.ice.K_domain_lb)./2 ... + - ... + k_eff(GRID.lake.ice.cT_domain_ub-1) .* (T(GRID.lake.ice.cT_domain_lb)-T(GRID.lake.ice.cT_domain_ub-1)) ... + ./ GRID.general.cT_delta(GRID.lake.ice.cT_domain_ub-1); + end + + dE_dt_cond(GRID.lake.ice.cT_domain_lb+1) = k_eff(GRID.lake.ice.cT_domain_lb+2).*(T(GRID.lake.ice.cT_domain_lb+2)-T(GRID.lake.ice.cT_domain_lb+1)) ... + ./ GRID.general.cT_delta(GRID.lake.ice.cT_domain_lb+2) ... + - ... + k_temp(GRID.lake.ice.K_domain_lb+1) .* (T(GRID.lake.ice.cT_domain_lb+1)-0) ... + ./ GRID.general.K_delta(GRID.lake.ice.K_domain_lb+1)./2; + + %residual heat flux used for ice cover + GRID.lake.ice.dE_dt_cond_residual = dE_dt_cond_save - (dE_dt_cond(GRID.lake.ice.cT_domain_lb) + dE_dt_cond(GRID.lake.ice.cT_domain_lb+1)); +end diff --git a/modules/cryoGridLake/heatConductionIceCover.m~ b/modules/cryoGridLake/heatConductionIceCover.m~ new file mode 100644 index 0000000..2b91c6a --- /dev/null +++ b/modules/cryoGridLake/heatConductionIceCover.m~ @@ -0,0 +1,28 @@ +function [dE_dt_cond GRID]= heatConductionIceCover(dE_dt_cond, T, k_eff, k_temp, GRID) + +GRID.ice.dE_dt_cond_residual=0; +if ~isempty(GRID.ice.cT_domain_ub) && ~isempty(GRID.water.cT_domain_ub) + dE_dt_cond_save = dE_dt_cond(GRID.ice.cT_domain_lb) + dE_dt_cond(GRID.ice.cT_domain_lb+1); + + if sum(GRID.ice.cT_domain)<2 + + dE_dt_cond(GRID.ice.cT_domain_lb) = k_temp(GRID.ice.K_domain_lb) .* (0-T(GRID.ice.cT_domain_lb)) ... + ./ GRID.general.K_delta(GRID.ice.K_domain_lb)./2; + else + + dE_dt_cond(GRID.ice.cT_domain_lb) = k_temp(GRID.ice.K_domain_lb) .* (0-T(GRID.ice.cT_domain_lb)) ... + ./ GRID.general.K_delta(GRID.ice.K_domain_lb)./2 ... + - ... + k_eff(GRID.ice.cT_domain_ub-1) .* (T(GRID.ice.cT_domain_lb)-T(GRID.ice.cT_domain_ub-1)) ... + ./ GRID.general.cT_delta(GRID.ice.cT_domain_ub-1); + end + + dE_dt_cond(GRID.ice.cT_domain_lb+1) = k_eff(GRID.ice.cT_domain_lb+2).*(T(GRID.ice.cT_domain_lb+2)-T(GRID.ice.cT_domain_lb+1)) ... + ./ GRID.general.cT_delta(GRID.ice.cT_domain_lb+2) ... + - ... + k_temp(GRID.ice.K_domain_lb+1) .* (T(GRID.ice.cT_domain_lb+1)-0) ... + ./ GRID.general.K_delta(GRID.ice.K_domain_lb+1)./2; + + %residual heat flux used for ice cover + GRID.ice.dE_dt_cond_residual = dE_dt_cond_save - (dE_dt_cond(GRID.ice.cT_domain_lb) + dE_dt_cond(GRID.ice.cT_domain_lb+1)); +end \ No newline at end of file diff --git a/modules/cryoGridLake/heatConductionLateral.m~ b/modules/cryoGridLake/heatConductionLateral.m~ new file mode 100644 index 0000000..c905833 --- /dev/null +++ b/modules/cryoGridLake/heatConductionLateral.m~ @@ -0,0 +1,16 @@ +function [dE_dt_cond dE_dt_lateral T_lateral]=heatConductionLateral(dE_dt_cond,T,k_temp,GRID,REF,FLAKE,t) + +[~, idx]=min(abs(REF.load.OUT.timestamp-t)); +T_lateral=REF.load.OUT.cryoGrid3(:,idx); + +%Estimated lateral heat flux. This procedure assumes that the thermal regime +%of the ground is not affected by the lake in a distance of about 2 twice +%the diameter of the lake. Assuming a circular lake shape, the lake has +%an effetive cross section with the soil domain of pi. Thus, the lateral +%heat flux per m^2 scales with (3.14.*FLAKE.fetch.*GRID.general.K_delta)./(3.14./4.*FLAKE.fetch.^2) + +dE_dt_lateral = k_temp.*(T_lateral-T)./(2.*FLAKE.fetch) .* 4.*GRID.general.K_delta./FLAKE.fetch; +dE_dt_lateral(~GRID.soil.cT_domain | T>0)=0; %exclude talik from lateral heat flux + +dE_dt_cond=dE_dt_cond+dE_dt_lateral; + diff --git a/modules/cryoGridLake/initializeLAKE.m b/modules/cryoGridLake/initializeLAKE.m new file mode 100644 index 0000000..5a4a5b7 --- /dev/null +++ b/modules/cryoGridLake/initializeLAKE.m @@ -0,0 +1,30 @@ +function [FLAKE GRID] = initializeLAKE(GRID, PARA); + +% GRID.lake.unfrozenWaterSurface = false; %tsvd not needed anymore +GRID.lake.residualWater = 0; % the water content stored "mixed" cells of air and water if a water body is present + +%---- flake initialization ------------------------------------------------ +FLAKE.t_snow_n_flk=0+273.15; +FLAKE.t_ice_n_flk=0+273.15; +FLAKE.t_wml_n_flk=6+273.15; +FLAKE.t_mnw_n_flk=5+273.15; +FLAKE.t_bot_n_flk=4+273.15; +FLAKE.t_b1_n_flk=7+273.15; +FLAKE.h_snow_n_flk=0; +FLAKE.h_ice_n_flk=0; +FLAKE.h_ml_n_flk=3; +FLAKE.h_b1_n_flk=10; +FLAKE.c_t_n_flk=0; +FLAKE.h_ice_n_flk=0; + +FLAKE.i_w_flk=0; +FLAKE.i_bot_flk=0; +FLAKE.q_w_flk=0; +FLAKE.q_bot_flk=0; + +%tsvd FLAKE.fetch=100; not used anymore +FLAKE.depth_w=PARA.water.depth; +FLAKE.d_h_ice_dt = 0; +FLAKE.q_ice_water = 0; +FLAKE.extincoef_water_typ=PARA.water.extinction; +FLAKE.latitude=PARA.location.latitude; \ No newline at end of file diff --git a/modules/cryoGridLake/updateGRID_flake.m b/modules/cryoGridLake/updateGRID_flake.m new file mode 100644 index 0000000..0a7df70 --- /dev/null +++ b/modules/cryoGridLake/updateGRID_flake.m @@ -0,0 +1,16 @@ +function GRID = updateGRID_flake(GRID) + + +%update ice cover and water grid +GRID.lake.ice.cT_domain = (GRID.general.K_grid(2:end) - GRID.general.K_grid(min([GRID.lake.water.cT_domain_ub GRID.lake.ice.cT_domain_ub GRID.lake.ice.cT_domain_ub])))<=GRID.lake.ice.z_ice & ... + (GRID.general.K_grid(2:end) - GRID.general.K_grid(min([GRID.lake.water.cT_domain_ub GRID.lake.ice.cT_domain_ub])))> 0; + +GRID.lake.ice.K_domain = GRID.lake.ice.cT_domain; +[GRID.lake.ice.cT_domain_lb GRID.lake.ice.cT_domain_ub] = LayerIndex(GRID.lake.ice.cT_domain); +[GRID.lake.ice.K_domain_lb GRID.lake.ice.K_domain_ub] = LayerIndex(GRID.lake.ice.K_domain); + +%update water body grid +GRID.lake.water.cT_domain = (~GRID.air.cT_domain & ~GRID.snow.cT_domain & ~GRID.lake.ice.cT_domain & ~GRID.soil.cT_domain); +GRID.lake.water.K_domain = GRID.lake.water.cT_domain; +[GRID.lake.water.cT_domain_lb GRID.lake.water.cT_domain_ub] = LayerIndex(GRID.lake.water.cT_domain); +[GRID.lake.water.K_domain_lb GRID.lake.water.K_domain_ub] = LayerIndex(GRID.lake.water.K_domain); diff --git a/modules/cryoGridLake/waterAlbedo.m b/modules/cryoGridLake/waterAlbedo.m new file mode 100644 index 0000000..090b0b4 --- /dev/null +++ b/modules/cryoGridLake/waterAlbedo.m @@ -0,0 +1,33 @@ +function water_albedo = waterAlbedo(sun_elevation,windspeed) +%water albedo parameterization after Wayne and Burt (1954) +%assuming a fixed atmospheric turbitiy T'=3. +%This is a very simple parameterization and might be replaced by a more +%sophisticated model in the future. + +u_a=windspeed; + +%translate wind speeds [m/s] into wave slopes following suggestions of +%Wayne and Burt (1954) +h2 = (0.0<=u_a & u_a<0.2) .* 100; +h2 = h2 + (0.2<=u_a & u_a<5.5) .* 30; +h2 = h2 + (5.5<=u_a & u_a<13.9).* 20; +h2 = h2 + (13.9<=u_a).*10; + +%tabulation of wave slopes after Wayne and Burt (1954) +H2 = [100; 30; 20; 10]; +[~, i_h]=min(abs(H2-h2)); + +p=sun_elevation; +%tabulation of sun elevation angles after Wayne and Burt (1954) +P=[90 50 30 10]; +[~, i_p]=min(abs(P-p)); + +water_albedo_tab = [0.044 0.053 0.089 0.168; + 0.045 0.050 0.086 0.202; + 0.045 0.050 0.084 0.219; + 0.046 0.050 0.080 0.281]; + +water_albedo = water_albedo_tab(i_h,i_p); + + + diff --git a/modules/cryoGridLake/waterDensity.m b/modules/cryoGridLake/waterDensity.m new file mode 100644 index 0000000..9d7fd34 --- /dev/null +++ b/modules/cryoGridLake/waterDensity.m @@ -0,0 +1,10 @@ +function DW = waterDensity(T) + +%calulated following the CIPM formula +a1=-3.983035; +a2=301.797; +a3=522528.9; +a4=69.34881; +a5=999.974950; + +DW=a5*(1- ((T+a1).^2.*(T+a2))./(a3.*(T+a4))); %[kg/m³] \ No newline at end of file diff --git a/modules/cryoGridLateral/applyLateralSnowFluxes.m b/modules/cryoGridLateral/applyLateralSnowFluxes.m new file mode 100644 index 0000000..f9e7957 --- /dev/null +++ b/modules/cryoGridLateral/applyLateralSnowFluxes.m @@ -0,0 +1,78 @@ +function [T, GRID] = applyLateralSnowFluxes( T, PARA, GRID, FORCING, my_snow_change ) + + if ~isempty(GRID.snow.cT_domain_ub) %snow cover already exitis + if my_snow_change>0 + disp( 'depositing lateral snow' ); + while my_snow_change > 0 + temp_snow_flux = min( [ my_snow_change, PARA.technical.SWEperCell-GRID.snow.Snow_i(GRID.snow.cT_domain_ub) ]); + + GRID.snow.Snow_i(GRID.snow.cT_domain_ub) = GRID.snow.Snow_i(GRID.snow.cT_domain_ub) ... + + temp_snow_flux; + GRID.snow.Snow_a(GRID.snow.cT_domain_ub) = GRID.snow.Snow_a(GRID.snow.cT_domain_ub) ... + + (temp_snow_flux./(PARA.snow.rho_snow./PARA.constants.rho_w) - temp_snow_flux); % could replace this to assure correct fresh snow density + + + % add an empty snow cell + GRID.snow.cT_domain(GRID.air.cT_domain_lb)=1; + GRID.snow.K_domain(GRID.air.K_domain_lb)=1; + [GRID.snow.cT_domain_lb, GRID.snow.cT_domain_ub] = LayerIndex(GRID.snow.cT_domain); + [GRID.snow.K_domain_lb, GRID.snow.K_domain_ub] = LayerIndex(GRID.snow.K_domain); + + GRID.air.cT_domain(GRID.air.cT_domain_lb)=0; + GRID.air.K_domain(GRID.air.K_domain_lb)=0; + [GRID.air.cT_domain_lb, GRID.air.cT_domain_ub] = LayerIndex(GRID.air.cT_domain); + [GRID.air.K_domain_lb, GRID.air.K_domain_ub] = LayerIndex(GRID.air.K_domain); + + GRID.snow.Snow_i(GRID.snow.cT_domain_ub)=0; + GRID.snow.Snow_w(GRID.snow.cT_domain_ub)=0; + GRID.snow.Snow_a(GRID.snow.cT_domain_ub)=0; + T(GRID.snow.cT_domain_ub)=T(GRID.snow.cT_domain_ub+1); + + my_snow_change = my_snow_change - temp_snow_flux; + + end + elseif my_snow_change<0 + disp( 'removing lateral snow' ); + while -my_snow_change >= GRID.snow.Snow_i(GRID.snow.cT_domain_ub) + + my_snow_change = my_snow_change + GRID.snow.Snow_i(GRID.snow.cT_domain_ub); + % route down water + GRID.snow.Snow_w(GRID.snow.cT_domain_ub+1) = GRID.snow.Snow_w(GRID.snow.cT_domain_ub+1) + GRID.snow.Snow_w(GRID.snow.cT_domain_ub); % this will not work if only one snow cell left + % clean upper cell + GRID.snow.Snow_i(GRID.snow.cT_domain_ub)=0; + GRID.snow.Snow_w(GRID.snow.cT_domain_ub)=0; + GRID.snow.Snow_a(GRID.snow.cT_domain_ub)=0; + T(GRID.snow.cT_domain_ub)=FORCING.i.Tair; + + % remove upper cell + GRID.snow.cT_domain(GRID.snow.cT_domain_ub)=0; + GRID.snow.K_domain(GRID.snow.cT_domain_ub)=0; + [GRID.snow.cT_domain_lb, GRID.snow.cT_domain_ub] = LayerIndex(GRID.snow.cT_domain); + [GRID.snow.K_domain_lb, GRID.snow.K_domain_ub] = LayerIndex(GRID.snow.K_domain); + + GRID.air.cT_domain(GRID.air.cT_domain_lb+1)=1; + GRID.air.K_domain(GRID.air.K_domain_lb+1)=1; + [GRID.air.cT_domain_lb, GRID.air.cT_domain_ub] = LayerIndex(GRID.air.cT_domain); + [GRID.air.K_domain_lb, GRID.air.K_domain_ub] = LayerIndex(GRID.air.K_domain); + + end + + % remove remaining snow_flux from upper cell + GRID.snow.Snow_i(GRID.snow.cT_domain_ub) = GRID.snow.Snow_i(GRID.snow.cT_domain_ub) + my_snow_change; + %GRID.snow.Snow_a(GRID.snow.cT_domain_ub) = GRID.snow.Snow_a(GRID.snow.cT_domain_ub) + (my_snow_change./(PARA.snow.rho_snow./PARA.constants.rho_w) - my_snow_change); + GRID.snow.Snow_a(GRID.snow.cT_domain_ub) = GRID.snow.Snow_i(GRID.snow.cT_domain_ub) .* ( (PARA.constants.rho_w ./ PARA.snow.rho_snow ) - 1 ); % this assures fresh snow density for upper cell + else % lateral_snow_flux==0 + %do nothing + end + + elseif my_snow_change>0 %no snow cover and positive lateral input + %---------- add the new snow into initial SWE variable in case of no snow cover------------------ + GRID.snow.SWEinitial = GRID.snow.SWEinitial + my_snow_change; + elseif my_snow_change<0 % no snow cover, but negative lateral flow calculated + warning('snow exchange - negative lateral snow flux without existing snow cover'); + end + + + + +end \ No newline at end of file diff --git a/modules/cryoGridLateral/calculateLateralHeatFluxes.m b/modules/cryoGridLateral/calculateLateralHeatFluxes.m new file mode 100644 index 0000000..24105f1 --- /dev/null +++ b/modules/cryoGridLateral/calculateLateralHeatFluxes.m @@ -0,0 +1,62 @@ +function [dE_dt, BALANCE] = calculateLateralHeatFluxes(T_index, k_index, PACKAGE_heatExchange_j, GRID, PARA, BALANCE, j) + index = labindex; + dE_dt = zeros( length(GRID.general.cT_grid), 1); + if PARA.ensemble.thermal_contact_length(index,j)>0 % calculate lateral heat flux only for laterally connected workers + + % change to absolute altitude grid + altitude_cTgrid_index = -GRID.general.cT_grid + PARA.ensemble.initial_altitude(index); + + distance_index_j = PARA.ensemble.distanceBetweenPoints(j, index); + weight_index = PARA.ensemble.weight(index); + weight_j = PARA.ensemble.weight(j); + contact_length_index_j = PARA.ensemble.thermal_contact_length(j, index); +%tsvd allow for heat exchange over full vertical profile contact_altitude = min( [ PARA.ensemble.altitude(j), PARA.ensemble.altitude(index) ] ) - distance_index_j; %below this depth, grid cells will exchange heat + % determine the contact domain +% contact_altitude = min( [ PARA.ensemble.altitude(j), PARA.ensemble.altitude(index) ] ); %below this depth, grid cells will exchange heat + contact_altitude = 0.; % ttt temporal fix zzz + contact_domain = altitude_cTgrid_index <= contact_altitude; %all cells in the current ensemble member + +% save uuu.mat contact_domain +% assert( 1==0, 'stop here') + + + % interpolate j-values to index-grid + altitude_cTgrid_j = -PACKAGE_heatExchange_j.cT_grid + PARA.ensemble.initial_altitude(j); + min_contact_altitude = min( [altitude_cTgrid_index(end), altitude_cTgrid_j(end)] ); %zzz max instead of min def. also max_contact_alt ? + altitude_cTgrid_index(end) = min_contact_altitude; + altitude_cTgrid_j(end) = min_contact_altitude; + T_j = PACKAGE_heatExchange_j.T; + k_j = PACKAGE_heatExchange_j.k_cTgrid; + if ~isreal(altitude_cTgrid_index); disp('altitude_cTgrid_index contains complex values'); end %likley not relevant anymore + if ~isreal(altitude_cTgrid_j); disp('altitude_cTgrid_j contains complex values'); end + if ~isreal(T_j); disp('T_j contains complex values'); end + if ~isreal(k_j); disp('k_j contains complex values'); end + + T_interp_j = interp1( altitude_cTgrid_j, T_j, altitude_cTgrid_index, 'linear'); +% try +% assert( sum( isnan( T_interp_j ) )==0, 'calc lat heat fluxes - error in T interpolation') %ttt +% catch +% save Data_Tinterp T_interp_j altitude_cTgrid_j T_j altitude_cTgrid_index index +% % error('interpolation NAN T error') +% end + k_interp_j = interp1( altitude_cTgrid_j, k_j, altitude_cTgrid_index, 'linear'); +% try +% assert( sum( isnan( k_interp_j ) )==0, 'calc lat heat fluxes - error in k interpolation') %ttt +% catch +% save Data_kinterp k_interp_j altitude_cTgrid_j k_j altitude_cTgrid_index index +% error('interpolation NAN K error') +% end + % determine effectice thermal conductivities + k_eff = (weight_index+weight_j) ./ ( weight_index./k_index + weight_j./k_interp_j ); + + % energy flux from worker j to index in [J / m^3 / s] + dE_dt(contact_domain) = k_eff(contact_domain) .* (T_interp_j(contact_domain)-T_index(contact_domain)) ./ distance_index_j .* contact_length_index_j ./ PARA.ensemble.area(index) ; % in [ J / m^3 / s ] + +% T_index(contact_domain) = T_index(contact_domain) + dE_dt_j ./ c_index(contact_domain) .* PARA.technical.syncTimeStep .* 24 .* 3600; + + % balance is not correct + %tsvd BALANCE.energy.Q_lateral(contact_domain) = BALANCE.energy.Q_lateral(contact_domain) + dE_dt(contact_domain) .* PARA.technical.syncTimeStep .* 24 .* 3600 .* GRID.general.K_delta(contact_domain); % in [ J / m^2 ] + BALANCE.energy.Q_lateral(contact_domain) = k_eff(contact_domain) .* (T_interp_j(contact_domain)-T_index(contact_domain)) ./ distance_index_j; % in W/m2 + + end +end diff --git a/modules/cryoGridLateral/calculateLateralSnowFluxes.m b/modules/cryoGridLateral/calculateLateralSnowFluxes.m new file mode 100644 index 0000000..a67aaaa --- /dev/null +++ b/modules/cryoGridLateral/calculateLateralSnowFluxes.m @@ -0,0 +1,49 @@ +function snow_flux_j = calculateLateralSnowFluxes( mobile_snow_index, PACKAGE_snowExchange_j, GRID, PARA, index, j) + + snow_flux_j = 0; + snow_diffusivity = PARA.ensemble.snow_diffusivity; % in [m^2/s] constant of proportionality + + + distance_index_j = PARA.ensemble.distanceBetweenPoints(index,j); + surface_altitude_index = PARA.ensemble.surface_altitude(index); + surface_altitude_j = PARA.ensemble.surface_altitude(j); + snow_w_j = PACKAGE_snowExchange_j.snow.Snow_w; + mobile_snow_j = PACKAGE_snowExchange_j.mobile_snow; + + % preconditions: points connected and minimum surface elevation difference in order to prevent oscillations between two workers + if distance_index_j~=0 && abs(surface_altitude_index-surface_altitude_j) > GRID.snow.snowCellSize + + + hasMobileSnow_index = surface_altitude_index > PARA.ensemble.altitude(index)+PARA.ensemble.immobile_snow_height(index); + hasMobileSnow_j = surface_altitude_j > PARA.ensemble.altitude(j)+PARA.ensemble.immobile_snow_height(j); + + isMelting_index = sum(GRID.snow.Snow_w)>0; + isMelting_j = sum(snow_w_j) > 0; + + if isMelting_index || isMelting_j + disp('melting conditions'); + end + + % this does not account for wind speed so far + maxSnowFlux = snow_diffusivity .* abs(surface_altitude_j - surface_altitude_index) ./ distance_index_j .* PARA.ensemble.snow_contact_length(index,j) ./ PARA.ensemble.area(index); % in [m SWE / s] + + + + + if surface_altitude_j > surface_altitude_index && hasMobileSnow_j && ~isMelting_j % worker index receiving drift snow from j + + snow_flux_j = min( [maxSnowFlux, mobile_snow_j * PARA.ensemble.weight(j) / PARA.ensemble.weight(index) ]); + + elseif surface_altitude_index > surface_altitude_j && hasMobileSnow_index && ~isMelting_index % worker index is depositing drift snow to j + + snow_flux_j = -min( [ maxSnowFlux, mobile_snow_index ] ) ; + + else % no drift snow exchange + + snow_flux_j = 0; + + end + + end + +end \ No newline at end of file diff --git a/modules/cryoGridLateral/calculateLateralSnowFluxes2.m b/modules/cryoGridLateral/calculateLateralSnowFluxes2.m new file mode 100644 index 0000000..dc75c35 --- /dev/null +++ b/modules/cryoGridLateral/calculateLateralSnowFluxes2.m @@ -0,0 +1,15 @@ +function snow_change = calculateLateralSnowFluxes2( mobile_snow, PARA ) + + index = labindex; + % calculate mobile snow volume + mobile_snow_volume = sum ( mobile_snow .* PARA.ensemble.weight .* (PARA.ensemble.terrain_index_snow>0) ); + % calculate individual change (deposit or removal) dependent on terrain index + if PARA.ensemble.terrain_index_snow(index)>0 %removal + snow_change = -mobile_snow(index); + elseif PARA.ensemble.terrain_index_snow(index)<0 % deposit + snow_change = -mobile_snow_volume .* PARA.ensemble.terrain_index_snow(index); + else % terrain index = 0 or NaN + snow_change = 0; + end + +end diff --git a/modules/cryoGridLateral/calculateLateralWaterFluxes.m b/modules/cryoGridLateral/calculateLateralWaterFluxes.m new file mode 100644 index 0000000..10516e5 --- /dev/null +++ b/modules/cryoGridLateral/calculateLateralWaterFluxes.m @@ -0,0 +1,145 @@ +function water_flux_j = calculateLateralWaterFluxes(T, PACKAGE_waterExchange_j, GRID, PARA, j) +% Function that calculate the water fluxes between the 2 workers. +% PARA.ensemble should contain the elevations, the water table values, the +% bucketbottom the weight and conductivities of each worcker. +% GRID will contain the soil water content. +% T is the temperature vector of the current worker. +% packageWorkerj has to be a bundle as defined considering necessary inputs + +index = labindex; + +water_flux_j = NaN; + + +% Check whether the considered workers (index, j) are +% hydrologically connceted + +if PARA.ensemble.hydraulic_contact_length(index,j)>0 + % conditions for water infiltration / exchanges + infiltration_condition_index = isempty(GRID.snow.cT_domain_ub) && T(GRID.soil.cT_domain_ub)>0; + infiltration_condition_j = PACKAGE_waterExchange_j.infiltration_condition; + + + if infiltration_condition_index && infiltration_condition_j + + wt_index = PARA.ensemble.water_table_altitude(index); + wt_j = PACKAGE_waterExchange_j.water_table_altitude; + ald_index = PARA.ensemble.active_layer_depth_altitude(index); + ald_j = PACKAGE_waterExchange_j.active_layer_depth_altitude; + + + % Decipher between cases + [waterpotWindex, hasWater_index] = nanmax([wt_index, ald_index ] ); %jjj + [waterpotWj, hasWater_j] = nanmax([wt_j, ald_j ] ); + + + if (waterpotWj > waterpotWindex && hasWater_j==1)% Current worker is gaining water + + % Calculate the maximum exchanged water volume + DeltaH = waterpotWj - waterpotWindex; + contact_height = waterpotWj - nanmax( [ waterpotWindex, ald_j ]); + Distance=sqrt(DeltaH^2 + PARA.ensemble.hydraulicDistance(j,index)^2); + %section=(PARA.ensemble.waterTable(j)-soilGrid_workerj(PARA.ensemble.bottomBucketSoilcTIndex(j)+1)) * PARA.ensemble.contactLength(j,index); + section=contact_height .* PARA.ensemble.hydraulic_contact_length(j,index); + DarcyFlux=PARA.ensemble.hydraulic_conductivity(j,index) * (DeltaH/Distance) * section / PARA.ensemble.area(index); + %MaxWaterHeight=DarcyFlux * PARA.technical.syncTimeStep ; % Jan : weight here corresponds to an actual area in m^2 + +% % Calculate available water volume that can be lost +% availableWater_li=Wc_workerj > PARA.soil.fieldCapacity; % Find where there is water above field capacity +% cellresidual_water=0; +% +% if (altitude_j-soilGrid_workerj(PARA.ensemble.bottomBucketSoilcTIndex(j)+1))> waterpotWindex; % Bucket bottom of the worker loosing water is higher than water table of the worker gaining water +% +% availableWater_li(PARA.ensemble.bottomBucketSoilcTIndex(j)+1:end)=0; % Stop accounting for water after the bottom of the bucket +% +% else % Bucket bottom of the worker loosing water is below the water table of the worker gaining water +% +% [ ind_cT, missingHeightInCell ] = getWTsoilcTindex(soilGrid_workerj,altitude_j,waterpotWindex );% find the index of the cell of worker loosing water matching with the waterTable elevation of worker gaining water +% [ ind_wT, ~ ] = getWTsoilcTindex(soilGrid_workerj,altitude_j,PARA.ensemble.waterTable(j) ); +% if ind_cT==ind_wT; +% fprintf('WARNING from getWaterFluxes2Workers : waterTable of workers 1 and 2 in the same gridcell\n') +% PARA.ensemble.lateralFlux_inWaterHeight(j,index)=0; +% PARA.ensemble.lateralFlux_inWaterHeight(index,j)=0; +% return +% end +% cellresidual_water=Wc_workerj(ind_cT)*missingHeightInCell; %err here +% availableWater_li(ind_cT:end)=0; % Stop accounting for water after the watertable of the worker gaining water +% +% end +% +% availableWater=sum((Wc_workerj(availableWater_li)-PARA.soil.fieldCapacity).*K_deltaSoil_workerj(availableWater_li))+cellresidual_water; +% +% lateralFlux_inWaterHeight=DarcyFlux;% min(MaxWaterHeight,availableWater); +% PARA.ensemble.lateralFlux_inWaterHeight(j,index)=-lateralFlux_inWaterHeight; % lateralFlux_inWaterHeight is a non symetric matrix where mat(worker1,worker2) givesthe water height between both but scale to worker 1 +% PARA.ensemble.lateralFlux_inWaterHeight(index,j)=(PARA.ensemble.weight(j)/PARA.ensemble.weight(index)).*lateralFlux_inWaterHeight; +% + water_flux_j=DarcyFlux; + elseif (waterpotWindex > waterpotWj && hasWater_index==1) % Current worker is loosing water + + % Calculate maximum of the exchange water volume + DeltaH= waterpotWindex - waterpotWj; + contact_height =waterpotWindex - nanmax( [ waterpotWj, ald_index ] ); + Distance=sqrt(DeltaH^2 + PARA.ensemble.hydraulicDistance(j,index)^2); + %section=(PARA.ensemble.waterTable(index)-GRID.soil.soilGrid(PARA.ensemble.bottomBucketSoilcTIndex(index)+1)) * PARA.ensemble.contactLength(j,index); + %Jan: see question above + section=contact_height .* PARA.ensemble.hydraulic_contact_length(j,index); + DarcyFlux=PARA.ensemble.hydraulic_conductivity(j,index) * (DeltaH/Distance) * section / PARA.ensemble.area(index); + %MaxWaterHeight=DarcyFlux * PARA.technical.syncTimeStep; + + + +% % Calculate available water volume that can be lost +% availableWater_li=wc > PARA.soil.fieldCapacity; % Find where there is water above field capacity +% cellresidual_water=0; +% +% if (altitude_index-GRID.soil.soilGrid(PARA.ensemble.bottomBucketSoilcTIndex(index)+1))> waterpotWj; % Bucket bottom of the worker loosing water is higher than water table of the worker gaining water +% +% availableWater_li(PARA.ensemble.bottomBucketSoilcTIndex(index)+1:end)=0; % Stop accounting for water after the bottom of the bucket +% +% else % Bucket bottom of the worker loosing water is below the water table of the worker gaining water +% +% [ ind_cT, missingHeightInCell ] = getWTsoilcTindex(GRID.soil.soilGrid,altitude_index,waterpotWj ); % find the index of the cell of worker loosing water matching with the waterTable elevation of worker gaining water +% [ ind_wT, ~ ] = getWTsoilcTindex(GRID.soil.soilGrid,altitude_index,PARA.ensemble.waterTable(index) ); +% if ind_cT==ind_wT; +% fprintf('WARNING from getWaterFluxes2Workers : waterTable of workers 1 and 2 in the same gridcell\n') +% PARA.ensemble.lateralFlux_inWaterHeight(j,index)=0; +% PARA.ensemble.lateralFlux_inWaterHeight(index,j)=0; +% return +% end +% cellresidual_water=missingHeightInCell*wc(ind_cT); % err here +% availableWater_li(ind_cT:end)=0; % Stop accounting for water after the watertable of the worker gaining water +% +% end +% +% K_deltaSoil=GRID.general.K_delta(GRID.soil.cT_domain); +% availableWater=sum((wc(availableWater_li)-PARA.soil.fieldCapacity).*K_deltaSoil(availableWater_li))+cellresidual_water; +% lateralFlux_inWaterHeight=min(MaxWaterHeight,availableWater); + % Jan: for now: take maximum flux + + water_flux_j=-DarcyFlux; + +% lateralFlux_inWaterHeight=DarcyFlux; +% PARA.ensemble.lateralFlux_inWaterHeight(j,index)=(PARA.ensemble.weight(index)/PARA.ensemble.weight(j)).*lateralFlux_inWaterHeight; +% PARA.ensemble.lateralFlux_inWaterHeight(index,j)=-lateralFlux_inWaterHeight; + else % same water table, no lateralFlux + water_flux_j=0; +% PARA.ensemble.lateralFlux_inWaterHeight(j,index)=0; +% PARA.ensemble.lateralFlux_inWaterHeight(index,j)=0; + end + + else % No possible movement of water, because of snow or suficial freezing +% PARA.ensemble.lateralFlux_inWaterHeight(j,index)=0; +% PARA.ensemble.lateralFlux_inWaterHeight(index,j)=0; + water_flux_j=0; + end +else +% % No connection between workers +% PARA.ensemble.lateralFlux_inWaterHeight(j,index)=NaN; +% PARA.ensemble.lateralFlux_inWaterHeight(index,j)=NaN; + water_flux_j = NaN; +end + +%water_fluxes = PARA.ensemble.lateralFlux_inWaterHeight(index); + + +end diff --git a/modules/cryoGridLateral/calculateTerrainIndexSnow.m b/modules/cryoGridLateral/calculateTerrainIndexSnow.m new file mode 100644 index 0000000..f1a4407 --- /dev/null +++ b/modules/cryoGridLateral/calculateTerrainIndexSnow.m @@ -0,0 +1,25 @@ +function terrain_index_snow_final = calculateTerrainIndexSnow(altitudes, weight) +%tsvd +weight_fix = [1, 1]; % temporal fix zzz ttt +weight=weight_fix; + +snowCellSize = 0.025; +allAltitudes=[]; +for i=1:size(weight,2) + %allAltitudes=[allAltitudes repmat( altitudes(1,i) ,1, weight(1,i))]; + allAltitudes=[allAltitudes repmat( round(altitudes(1,i)./snowCellSize).*snowCellSize ,1, weight(1,i))]; + +end + +terrain_index_snow=(allAltitudes-mean(allAltitudes))./std(allAltitudes); +terrain_index_snow=terrain_index_snow./sum(terrain_index_snow(terrain_index_snow>0)); %this must be normalized to conserve energy, check later with PARA.location.aerial_fraction +terrain_index_snow(isnan(terrain_index_snow))=0; + +terrain_index_snow_final=[]; +j=1; +for i=1:size(weight,2) + terrain_index_snow_final=[terrain_index_snow_final terrain_index_snow(1,j)]; + j=j+weight(1,i); +end + +%terrain_index_snow_final = terrain_index_snow_final./sum(terrain_index_snow_final(terrain_index_snow_final>0)); \ No newline at end of file diff --git a/modules/cryoGridLateral/checkPreconditionSnowExchange.m b/modules/cryoGridLateral/checkPreconditionSnowExchange.m new file mode 100644 index 0000000..2daa206 --- /dev/null +++ b/modules/cryoGridLateral/checkPreconditionSnowExchange.m @@ -0,0 +1,16 @@ +function [precond_snow] = checkPreconditionSnowExchange( GRID, PARA ) + + precondition_snowExchange = double( ~isempty(GRID.snow.cT_domain_ub) && ( PARA.ensemble.surface_altitude(labindex) > PARA.ensemble.altitude(labindex)+PARA.ensemble.immobile_snow_height(labindex) ) ); + + for j=1:numlabs + if j~=labindex + labSend( precondition_snowExchange, j, 3); + end + end + for j=1:numlabs + if j~=labindex + precondition_snowExchange = precondition_snowExchange + labReceive( j, 3); + end + end + + precond_snow = precondition_snowExchange > 0; % sufficient if one realization has mobile snow diff --git a/modules/cryoGridLateral/checkPreconditionWaterExchange.m b/modules/cryoGridLateral/checkPreconditionWaterExchange.m new file mode 100644 index 0000000..5186ddb --- /dev/null +++ b/modules/cryoGridLateral/checkPreconditionWaterExchange.m @@ -0,0 +1,17 @@ +function [precond_water] = checkPreconditionWaterExchange( T, GRID ) + + precondition_waterExchange = double( T(GRID.soil.cT_domain_ub)>0 && isempty(GRID.snow.cT_domain_ub) ); % matches with conditions of infiltration + + for j=1:numlabs + if j~=labindex + labSend( precondition_waterExchange, j, 2); + end + end + + for j=1:numlabs + if j~=labindex + precondition_waterExchange = precondition_waterExchange + labReceive( j, 2); + end + end + + precond_water = precondition_waterExchange > 1 % at least two workers need matching conditions diff --git a/modules/cryoGridLateral/getMaxSnowAltitude.m b/modules/cryoGridLateral/getMaxSnowAltitude.m new file mode 100644 index 0000000..2638f98 --- /dev/null +++ b/modules/cryoGridLateral/getMaxSnowAltitude.m @@ -0,0 +1,3 @@ +function max_snow = getMaxSnowAltitude(PARA) + max_snow = max( PARA.ensemble.altitude ) + PARA.snow.relative_maxSnow; +end diff --git a/modules/cryoGridLateral/getMaxWaterAltitude.m b/modules/cryoGridLateral/getMaxWaterAltitude.m new file mode 100644 index 0000000..69e129d --- /dev/null +++ b/modules/cryoGridLateral/getMaxWaterAltitude.m @@ -0,0 +1,3 @@ +function max_water = getMaxWaterAltitude(PARA) + max_water = max( PARA.ensemble.soil_altitude ) + PARA.soil.relative_maxWater; %zzz jjj +end diff --git a/modules/cryoGridLateral/get_parallel_variables.m b/modules/cryoGridLateral/get_parallel_variables.m new file mode 100644 index 0000000..bb36a74 --- /dev/null +++ b/modules/cryoGridLateral/get_parallel_variables.m @@ -0,0 +1,194 @@ +function PARA = get_parallel_variables(PARA) + + index = labindex; + + % auxiliary calculations for circular geometry + F_L = 0.25; % landscape Lake Fraction + radius = 10; % in [m] + perimeter = 2*pi.*radius; % in [m] + distance = sqrt(pi./(4*F_L)) * radius; % in [m] + %distance = 2.*radius; + % geometric relations +% PARA.ensemble.distanceBetweenPoints = diameter .* ( ones(numlabs) - eye(numlabs) );% [0, 4, 4; 4, 0, 4 ; 4, 4, 0]; %in m; put 0 for all non-connected ensemble members + PARA.ensemble.distanceBetweenPoints = distance .* ( ones(numlabs) - eye(numlabs) ); %in m; put 0 for all non-connected ensemble members; %zzz needed? + %%%PARA.ensemble.distanceBetweenPoints = distance ; %in m; put 0 for all non-connected ensemble members; %zzz needed? + % PARA.ensemble.weight = [1, 1];%[2, 1, 1]; + %%%PARA.ensemble.weight = [radius/(PARA.ensemble.distanceBetweenPoints+radius),PARA.ensemble.distanceBetweenPoints/(PARA.ensemble.distanceBetweenPoints+radius)]; % weight keff by length radius and distance + PARA.ensemble.weight = [radius/(distance+radius),distance/(distance+radius)]; % weight keff by length radius and distance + + %area = pi.*radius^2; % in [m^2] + %area = [pi.*radius^2 , pi.*(PARA.ensemble.distanceBetweenPoints^2-radius^2); % in [m^2] + %PARA.ensemble.area = PARA.ensemble.weight.*area; % in m^2 zzz jjj ??? + + PARA.ensemble.area = [pi.*radius^2 , pi.*(distance^2-radius^2)]; % in [m^2] + + % topographical relations + %tsvd PARA.ensemble.initial_altitude = [20.0, 20.5]; %[20.0, 21.0, 20.5]; %in m a.s.l., this is the reference for the "zero" position of the grids +% PARA.ensemble.initial_altitude = [20.0, 20.0]; %[20.0, 21.0, 20.5]; %in m a.s.l., this is the reference for the "zero" position of the grids + PARA.ensemble.initial_altitude = [0., 0.]; %[20.0, 21.0, 20.5]; %in m a.s.l., this is the reference for the "zero" position of the grids + + PARA.ensemble.altitude = PARA.ensemble.initial_altitude; + PARA.ensemble.surface_altitude = PARA.ensemble.initial_altitude; + + % parameters related to heat exchange + PARA.ensemble.thermal_contact_length = perimeter .* ( ones(numlabs) - eye(numlabs ) ); % [ 0, 1, 0 ; 1, 0, 1 ; 0, 1, 0 ]; % + + % parameters related to water exchange + PARA.ensemble.external_water_flux=[0, 0 ] ; % 0]; %in m/day + PARA.ensemble.hydraulic_conductivity= PARA.soil.hydraulic_conductivity * ( ones(numlabs) - eye(numlabs ) );%[ 0, 1, 0 ; 1, 0, 1 ; 0, 1, 0 ]; %in m/sec % [Roth: 1e-5 for saturated silt, 2.2e-5 for saturated sand] + PARA.ensemble.water_table_altitude = PARA.ensemble.altitude; %initialize somehow; + + %PARA.ensemble.max_water_flux= [0 0]; %in m water equivalent + PARA.ensemble.hydraulic_contact_length = perimeter .* ( ones(numlabs) - eye(numlabs ) );%[ 0, 1, 0 ; 1, 0, 1 ; 0, 1, 0 ]; + PARA.ensemble.active_layer_depth_altitude = [NaN NaN NaN]; + PARA.ensemble.hydraulicDistance = PARA.ensemble.distanceBetweenPoints; + + % parameters related to snow exchange + %PARA.ensemble.snow_diffusivity = PARA.snow.diffusivity; + %PARA.ensemble.relative_max_snow_height = 0.2; + PARA.ensemble.immobile_snow_height = [0.1, 0.1 ]; %, 0.2 ]; %in m %this replaces PARA.snow.maxSnow ? zzz + PARA.ensemble.terrain_index_snow = calculateTerrainIndexSnow(PARA.ensemble.altitude, PARA.ensemble.weight); + PARA.ensemble.snow_contact_length = perimeter .* ( ones(numlabs) - eye(numlabs ) ); + + % location-specific static + PARA.location.initial_altitude = PARA.ensemble.initial_altitude(index); + PARA.soil.externalWaterFlux = PARA.ensemble.external_water_flux(index); + % location-specific dynamic auxiliary variables + PARA.location.area = PARA.ensemble.area(index); + PARA.location.altitude = PARA.ensemble.altitude(index); + PARA.location.surface_altitude = PARA.ensemble.surface_altitude(index); + PARA.location.water_table_altitude = PARA.ensemble.water_table_altitude(index); + PARA.location.active_layer_depth_altitude = PARA.ensemble.active_layer_depth_altitude(index); + % location-specific dynamic common thresholds + PARA.location.absolute_maxWater_altitude = [max( PARA.ensemble.altitude ) + PARA.soil.relative_maxWater]; + PARA.location.absolute_maxSnow_altitude = [max( PARA.ensemble.altitude ) + PARA.snow.relative_maxSnow]; + + % soil stratigraphy + % column 1: start depth of layer (first layer must start with 0) - each layer extends until the beginning of the next layer, the last layer extends until the end of the model domain + % column 2: volumetric water+ice content; column 3: volumetric mineral content; column 4: volumetric organic content; + % column 5: code for soil type: 1: sand, 2: silt + % column 6: natural porosity - should be the same as 1-mineral-organic if no ground subsidence/thermokarst occurs + + + % default stratigraphy used in publication: + PARA.soil.layer_properties={[ 0.0 0.60 0.10 0.15 1 0.75;... % Non-Lake + 0.15 0.65 0.3 0.05 2 0.65;... + 0.9 0.65 0.3 0.05 1 0.65;... + 9.0 0.30 0.70 0.00 1 0.30 ], ... + % + [ 0.0 0.65 0.3 0.05 2 0.65;... % Lake + 0.9 0.65 0.3 0.05 1 0.65;... + 9.0 0.30 0.70 0.00 1 0.30 ]}; + +% PARA.soil.layer_properties = {[0.0 0.5 0.5 0.00 1 0.50 ;... % center stratigraphy without excess ice +% 1.0 0.5 0.5 0.00 1 0.50 ;... +% 10.0 0.25 0.75 0.00 1 0.25 ] , ... +% [0.0 0.5 0.5 0.00 1 0.50;... % rim stratigraphy with excess ice +% 0.7 0.8 0.2 0.00 1 0.50;... +% 10.0 0.25 0.75 0.00 1 0.25 ], ... +% [0.0 0.5 0.5 0.00 1 0.50;... % trough stratigraphy with excess ice +% 0.1 0.8 0.2 0.00 1 0.50;... +% 10.0 0.25 0.75 0.00 1 0.25 ]}; + + +% PARA.soil.layer_properties = {[0.0 0.5 0.5 0.00 1 0.50 ;... % non-lake stratigraphy (no excess ice) +% 1.0 0.5 0.5 0.00 1 0.50 ;... +% 10.0 0.25 0.75 0.00 1 0.25 ], ... +% +% [0.0 0.5 0.5 0.00 1 0.50 ;... % lake stratigraphy +% 1.0 0.5 0.5 0.00 1 0.50 ;... +% 10.0 0.25 0.75 0.00 1 0.25 ]}; + + +% PARA.soil.layer_properties = {[0.0 0.4 0.1 0.15 1 0.75 ;... % non-lake stratigraphy (~Langer JGR15) +% % 0.2 0.65 0.3 0.05 2 0.65 ;... +% 1.0 0.65 0.3 0.05 1 0.65 ;... +% 10.0 0.3 0.7 0.00 1 0.3 ], ... +% % +% [0.0 0.4 0.1 0.15 1 0.75 ;... % lake stratigraphy +% 1.0 0.65 0.3 0.05 1 0.65 ;... +% 10.0 0.3 0.7 0.00 1 0.3 ]}; + +%todotodo read parameters from file! +% PARA.soil.layer_properties = {[0.0 0.5 0.5 0.25 1 0.50 ;... % reference case - Langer 2015 +% 0.2 0.5 0.5 0.00 1 0.50 ;... +% +% ... update +% 0.9 0.25 0.75 0.00 1 0.25 ] , ... +% 9.0 0.25 0.75 0.00 1 0.25 ] , ... +% 1000.0 0.25 0.75 0.00 1 0.25 ] , ... + + + % PARA.soil.layer_properties = {[0.0 0.5 0.5 0.00 1 0.50 ;... % lake stratigraphy (no excess ice) +% 0.02 0.5 0.5 0.00 1 0.50 ;... +% 0.04 0.5 0.5 0.00 1 0.50 ;... +% 0.06 0.5 0.5 0.00 1 0.50 ;... +% 0.08 0.5 0.5 0.00 1 0.50 ;... +% 0.10 0.5 0.5 0.00 1 0.50 ;... +% 0.12 0.5 0.5 0.00 1 0.50 ;... +% 0.14 0.5 0.5 0.00 1 0.50 ;... +% 0.16 0.5 0.5 0.00 1 0.50 ;... +% 0.18 0.5 0.5 0.00 1 0.50 ;... +% 0.20 0.5 0.5 0.00 1 0.50 ] , ... +% +% [0.0 0.5 0.5 0.00 1 0.50 ;... % lab 2 +% 0.02 0.5 0.5 0.00 1 0.50 ;... +% 0.04 0.5 0.5 0.00 1 0.50 ;... +% 0.06 0.5 0.5 0.00 1 0.50 ;... +% 0.08 0.5 0.5 0.00 1 0.50 ;... +% 0.10 0.5 0.5 0.00 1 0.50 ;... +% 0.12 0.5 0.5 0.00 1 0.50 ;... +% 0.14 0.5 0.5 0.00 1 0.50 ;... +% 0.16 0.5 0.5 0.00 1 0.50 ;... +% 0.18 0.5 0.5 0.00 1 0.50 ;... +% 0.20 0.5 0.5 0.00 1 0.50 ] }; + PARA.soil.layer_properties = PARA.soil.layer_properties{index}; + + % different initial conditions +% PARA.Tinitial = [-5 5 5;... +% 0 -5 -2;... +% 1 -5 -1;... +% 10 -8 0;... +% 20 -10 0;... +% 100 -10 1;... +% 2000 10 10]; + +% PARA.Tinitial = [-5 5 5;... +% 0 -5 4;... +% 1 -5 1;... +% 10 -8 0;... +% 20 -10 0;... +% 100 -10 1;... +% 2000 10 10]; + + PARA.Tinitial = [ -5 10 10;... % SSW setting + 0 0 6;... + 0.9 -3 6;... + 2 -4 0;... + 10 -7 -5;... + 20 -10 -10;... + 100 -10 -10;... + 2000 10 10]; + +% PARA.Tinitial = [ -5 10 10;... % MSW setting +% 0 0 5;... +% 6 -3 4;... +% 10 -7 0;... +% 20 -10 -9;... +% 100 -10 -10;... +% 2000 10 10]; + +% PARA.Tinitial = [-5 10 10;... this profile can cause model crashes in mpi... +% 0 0 0;... +% 5 -5 -5;... +% 20 -10 -10;... +% 100 -10 -10;... +% 2000 10 10]; + + PARA.Tinitial=[PARA.Tinitial(:,1) PARA.Tinitial(:, 1+index)]; + + PARA.water.depth = [0.,5.]; % non-lake and small-sized water body (SSW) + %PARA.water.depth = [0.,6.]; % non-lake and medium-sized water body (MSW) + + PARA.water.depth = PARA.water.depth(index); +end diff --git a/modules/cryoGridLateral/get_parallel_variables_batch.m b/modules/cryoGridLateral/get_parallel_variables_batch.m new file mode 100644 index 0000000..5cd8786 --- /dev/null +++ b/modules/cryoGridLateral/get_parallel_variables_batch.m @@ -0,0 +1,91 @@ +function PARA = get_parallel_variables(PARA,SETUP) + + index = labindex; + + % auxiliary calculations for circular geometry + L_F = SETUP.LF; % landscape Lake Fraction + radius = SETUP.LR; % Lake radius in [m] + perimeter = 2*pi.*radius; % in [m] + distance = sqrt(pi./(4*L_F)) * radius; % in [m] + %distance = 2.*radius; + % geometric relations +% PARA.ensemble.distanceBetweenPoints = diameter .* ( ones(numlabs) - eye(numlabs) );% [0, 4, 4; 4, 0, 4 ; 4, 4, 0]; %in m; put 0 for all non-connected ensemble members + PARA.ensemble.distanceBetweenPoints = distance .* ( ones(numlabs) - eye(numlabs) ); %in m; put 0 for all non-connected ensemble members; %zzz needed? + %%%PARA.ensemble.distanceBetweenPoints = distance ; %in m; put 0 for all non-connected ensemble members; %zzz needed? + % PARA.ensemble.weight = [1, 1];%[2, 1, 1]; + %%%PARA.ensemble.weight = [radius/(PARA.ensemble.distanceBetweenPoints+radius),PARA.ensemble.distanceBetweenPoints/(PARA.ensemble.distanceBetweenPoints+radius)]; % weight keff by length radius and distance + PARA.ensemble.weight = [radius/(distance+radius),distance/(distance+radius)]; % weight keff by length radius and distance + + %area = pi.*radius^2; % in [m^2] + %area = [pi.*radius^2 , pi.*(PARA.ensemble.distanceBetweenPoints^2-radius^2); % in [m^2] + %PARA.ensemble.area = PARA.ensemble.weight.*area; % in m^2 zzz jjj ??? + + PARA.ensemble.area = [pi.*radius^2 , pi.*(distance^2-radius^2)]; % in [m^2] + + % topographical relations + %tsvd PARA.ensemble.initial_altitude = [20.0, 20.5]; %[20.0, 21.0, 20.5]; %in m a.s.l., this is the reference for the "zero" position of the grids +% PARA.ensemble.initial_altitude = [20.0, 20.0]; %[20.0, 21.0, 20.5]; %in m a.s.l., this is the reference for the "zero" position of the grids + PARA.ensemble.initial_altitude = [0., 0.]; %[20.0, 21.0, 20.5]; %in m a.s.l., this is the reference for the "zero" position of the grids + + PARA.ensemble.altitude = PARA.ensemble.initial_altitude; + PARA.ensemble.surface_altitude = PARA.ensemble.initial_altitude; + + % parameters related to heat exchange + PARA.ensemble.thermal_contact_length = perimeter .* ( ones(numlabs) - eye(numlabs ) ); % [ 0, 1, 0 ; 1, 0, 1 ; 0, 1, 0 ]; % + + % parameters related to water exchange + PARA.ensemble.external_water_flux=[0, 0 ] ; % 0]; %in m/day + PARA.ensemble.hydraulic_conductivity= PARA.soil.hydraulic_conductivity * ( ones(numlabs) - eye(numlabs ) );%[ 0, 1, 0 ; 1, 0, 1 ; 0, 1, 0 ]; %in m/sec % [Roth: 1e-5 for saturated silt, 2.2e-5 for saturated sand] + PARA.ensemble.water_table_altitude = PARA.ensemble.altitude; %initialize somehow; + + %PARA.ensemble.max_water_flux= [0 0]; %in m water equivalent + PARA.ensemble.hydraulic_contact_length = perimeter .* ( ones(numlabs) - eye(numlabs ) );%[ 0, 1, 0 ; 1, 0, 1 ; 0, 1, 0 ]; + PARA.ensemble.active_layer_depth_altitude = [NaN NaN NaN]; + PARA.ensemble.hydraulicDistance = PARA.ensemble.distanceBetweenPoints; + + % parameters related to snow exchange + %PARA.ensemble.snow_diffusivity = PARA.snow.diffusivity; + %PARA.ensemble.relative_max_snow_height = 0.2; + PARA.ensemble.immobile_snow_height = [0.1, 0.1 ]; %, 0.2 ]; %in m %this replaces PARA.snow.maxSnow ? zzz + PARA.ensemble.terrain_index_snow = calculateTerrainIndexSnow(PARA.ensemble.altitude, PARA.ensemble.weight); + PARA.ensemble.snow_contact_length = perimeter .* ( ones(numlabs) - eye(numlabs ) ); + + % location-specific static + PARA.location.initial_altitude = PARA.ensemble.initial_altitude(index); + PARA.soil.externalWaterFlux = PARA.ensemble.external_water_flux(index); + % location-specific dynamic auxiliary variables + PARA.location.area = PARA.ensemble.area(index); + PARA.location.altitude = PARA.ensemble.altitude(index); + PARA.location.surface_altitude = PARA.ensemble.surface_altitude(index); + PARA.location.water_table_altitude = PARA.ensemble.water_table_altitude(index); + PARA.location.active_layer_depth_altitude = PARA.ensemble.active_layer_depth_altitude(index); + % location-specific dynamic common thresholds + PARA.location.absolute_maxWater_altitude = [max( PARA.ensemble.altitude ) + PARA.soil.relative_maxWater]; + PARA.location.absolute_maxSnow_altitude = [max( PARA.ensemble.altitude ) + PARA.snow.relative_maxSnow]; + + % soil stratigraphy + % column 1: start depth of layer (first layer must start with 0) - each layer extends until the beginning of the next layer, the last layer extends until the end of the model domain + % column 2: volumetric water+ice content; column 3: volumetric mineral content; column 4: volumetric organic content; + % column 5: code for soil type: 1: sand, 2: silt + % column 6: natural porosity - should be the same as 1-mineral-organic if no ground subsidence/thermokarst occurs + + + % default stratigraphy used in publication: +PARA.soil.layer_properties={[ 0.0 0.60 0.10 0.15 1 0.75;... % Non-Lake + 0.15 0.65 0.3 0.05 2 0.65;... + 0.9 0.65 0.3 0.05 1 0.65;... + 9.0 0.30 0.70 0.00 1 0.30 ], ... + % + [ 0.0 0.65 0.3 0.05 2 0.65;... % Lake + 0.9 0.65 0.3 0.05 1 0.65;... + 9.0 0.30 0.70 0.00 1 0.30 ]}; + + PARA.soil.layer_properties = PARA.soil.layer_properties{index}; + + % different initial conditions + + PARA.Tinitial=[SETUP.Tini(:,1) SETUP.Tini(:,1+index)]; + + PARA.water.depth = [0.,SETUP.LD]; + PARA.water.depth = PARA.water.depth(index); +end diff --git a/modules/cryoGridLateral/updateAuxiliaryVariablesAndCommonThresholds.m b/modules/cryoGridLateral/updateAuxiliaryVariablesAndCommonThresholds.m new file mode 100644 index 0000000..f2a2175 --- /dev/null +++ b/modules/cryoGridLateral/updateAuxiliaryVariablesAndCommonThresholds.m @@ -0,0 +1,39 @@ +function [PARA] = updateAuxiliaryVariablesAndCommonThresholds( T, wc, GRID, PARA) + + PARA.ensemble.surface_altitude(labindex) = getSurfaceAltitude( PARA, GRID ); + PARA.ensemble.altitude(labindex) = getAltitude( PARA, GRID ); + PARA.ensemble.soil_altitude(labindex) = getSoilAltitude( PARA, GRID ); + PARA.ensemble.water_table_altitude(labindex) = getWaterTableAltitude(T, wc, GRID, PARA ); %JAN: Leo uses getWaterTabelFC to account for non-saturated cells above fieldCapacity + PARA.ensemble.active_layer_depth_altitude(labindex) = getActiveLayerDepthAltitude(PARA, GRID, T); + % sending information from "labindex" to all "j" + for j=1:numlabs + if j~=labindex + labSend(PARA.ensemble.surface_altitude(labindex), j, 1); + labSend(PARA.ensemble.altitude(labindex), j, 2); + labSend(PARA.ensemble.water_table_altitude(labindex), j, 3); + labSend(PARA.ensemble.active_layer_depth_altitude(labindex), j, 4); + labSend(PARA.ensemble.soil_altitude(labindex), j, 5); + end + end + % receiving --------------------------------------------------- + % receive information from all other realizations + for j=1:numlabs %update surface altitudes to recalculate terrain index + if j~=labindex + PARA.ensemble.surface_altitude(j)=labReceive(j, 1); + PARA.ensemble.altitude(j)=labReceive(j, 2); + PARA.ensemble.water_table_altitude(j)=labReceive(j, 3); + PARA.ensemble.active_layer_depth_altitude(j)=labReceive(j, 4); + PARA.ensemble.soil_altitude(j)=labReceive(j, 5); + end + end + + % update common thresholds for water and snow exchagne + PARA.location.absolute_maxSnow_altitude = getMaxSnowAltitude(PARA); + PARA.location.absolute_maxWater_altitude = getMaxWaterAltitude(PARA); + + % update auxiliary variables in location struct // MORE? + PARA.location.altitude = PARA.ensemble.altitude(labindex); + PARA.location.surface_altitude = PARA.ensemble.surface_altitude(labindex); + PARA.location.soil_altitude = PARA.ensemble.soil_altitude(labindex); + PARA.location.water_table_altitude = PARA.ensemble.water_table_altitude(labindex); + PARA.location.active_layer_depth_altitude = PARA.ensemble.active_layer_depth_altitude(labindex); diff --git a/modules/cryoGridSEB/L_star.m b/modules/cryoGridSEB/L_star.m index 029ef93..ddd9725 100644 --- a/modules/cryoGridSEB/L_star.m +++ b/modules/cryoGridSEB/L_star.m @@ -19,20 +19,14 @@ % potential error here, pressure set to 1005 hPa, must be changed? -%Tz=Tz+273.15; - - u_star = real(uz.*kappa./(log(z./z0)- psi_M(z./Lstar, z0./Lstar))); L_star = real(-rho.*cp.*Tz./kappa./g.*u_star.^3./(Qh + 0.61.*cp./L.*Tz.*Qe)); -L_star=(abs(L_star)<1e-7).*L_star./abs(L_star).*1e-7 + (abs(L_star)>=1e-7).*L_star; %changed to 1e-5, as 1e-7 before - - -SEB.ustar = u_star; -SEB.L_star=[SEB.L_star(2:end) L_star]; - - -assert(~isnan(L_star), 'Lstar is nan'); - - - - +% L_star=(abs(L_star)<1e-6).*L_star./abs(L_star).*1e-6 + (abs(L_star)>=1e-6).*L_star; % lower limit for Lstar (before 1e-7 and 1e-5) +% L_star=(abs(L_star)>1e6).*L_star./abs(L_star).*1e6 + (abs(L_star)<=1e6).*L_star; % introduced upper limit for Lstar +%tsvd new limits for Lstar to prevent NAN in Lstar +L_star=(abs(L_star)<1e-4).*L_star./abs(L_star).*1e-4 + (abs(L_star)>=1e-4).*L_star; % lower limit for Lstar (before 1e-7 and 1e-4) +L_star=(abs(L_star)>1e4).*L_star./abs(L_star).*1e4 + (abs(L_star)<=1e4).*L_star; % introduced upper limit for Lstar +assert(~isnan(L_star),'L_star is NAN!') + +SEB.u_star = u_star; +SEB.L_star=[SEB.L_star(2:end) L_star]; \ No newline at end of file diff --git a/modules/cryoGridSEB/Q_eq.m b/modules/cryoGridSEB/Q_eq.m index ba160a3..97a1181 100644 --- a/modules/cryoGridSEB/Q_eq.m +++ b/modules/cryoGridSEB/Q_eq.m @@ -15,6 +15,7 @@ +assert(~isnan(Lstar),'Lstar is NAN!') if T_surf<=273.15 Q_e = -rho.*L_i.*kappa.*uz.*kappa./(log(z./z0)- psi_M(z./Lstar, z0./Lstar)).*(q-satPresIce(T_surf)./p)./(log(z./z0)- psi_H(z./Lstar, z0./Lstar)); @@ -23,4 +24,6 @@ + rs.*uz.*kappa.^2./(log(z./z0)- psi_M(z./Lstar, z0./Lstar))); end +assert(~isnan(Q_e),'Q_e is NAN!') + %Q_e = -rho.*L.*kappa.*uz.*kappa./(log(z./z0)).*(RH.*satPresIce(Tz)-satPresIce(T_surf))./p./(log(z./z0)); \ No newline at end of file diff --git a/modules/cryoGridSEB/surfaceCondition.m b/modules/cryoGridSEB/surfaceCondition.m index 8569c2e..3076519 100644 --- a/modules/cryoGridSEB/surfaceCondition.m +++ b/modules/cryoGridSEB/surfaceCondition.m @@ -1,9 +1,9 @@ -function [PARA, GRID] = surfaceCondition(GRID, PARA, T) +%tsvd function [PARA, GRID] = surfaceCondition(GRID, PARA, T) % old implementation +function [PARA, GRID] = surfaceCondition(GRID, PARA, T, t, FORCING, SEB) -% set surface parameters (albedo, emissivity, roughnesslength, resistance -% to evaporation) according to the actual surface conditions +% set surface parameters (albedo, emissivity, roughnesslength, resistance to evaporation) according to the actual surface conditions -GRID.lake.unfrozenWaterSurface=false; +% GRID.lake.unfrozenWaterSurface=false; %tsvd not needed anymore %default soil surface PARA.surf.albedo = PARA.soil.albedo; @@ -18,23 +18,45 @@ PARA.surf.z0 = PARA.snow.z0; PARA.surf.rs = PARA.snow.rs; -% check if water surface exists and whether it is frozen +% lll comment out this block +% % check if water surface exists and whether it is frozen +% +% elseif GRID.soil.cT_domain(GRID.air.cT_domain_lb+1)==1 ... +% && GRID.soil.cT_organic(1)+GRID.soil.cT_mineral(1)<=1e-6 +% +% % upper soil cell is pure water +% if T(GRID.soil.cT_domain_ub)>0 % unfrozen +% GRID.lake.unfrozenWaterSurface = true; +% PARA.surf.albedo = PARA.water.albedo; +% PARA.surf.epsilon = PARA.water.epsilon; +% PARA.surf.z0 = PARA.water.z0; +% PARA.surf.rs = PARA.water.rs; +% else %frozen +% PARA.surf.albedo = PARA.ice.albedo; +% PARA.surf.epsilon = PARA.ice.epsilon; +% PARA.surf.z0 = PARA.ice.z0; +% PARA.surf.rs = PARA.ice.rs; +% end +% end -elseif GRID.soil.cT_domain(GRID.air.cT_domain_lb+1)==1 ... - && GRID.soil.cT_organic(1)+GRID.soil.cT_mineral(1)<=1e-6 - - % upper soil cell is pure water - if T(GRID.soil.cT_domain_ub)>0 % unfrozen - GRID.lake.unfrozenWaterSurface = true; - PARA.surf.albedo = PARA.water.albedo; - PARA.surf.epsilon = PARA.water.epsilon; - PARA.surf.z0 = PARA.water.z0; - PARA.surf.rs = PARA.water.rs; - else %frozen - PARA.surf.albedo = PARA.ice.albedo; - PARA.surf.epsilon = PARA.ice.epsilon; - PARA.surf.z0 = PARA.ice.z0; - PARA.surf.rs = PARA.ice.rs; - end +%tsvd check if lake exists +elseif GRID.lake.water.cT_domain(GRID.air.cT_domain_lb+1)==1 % water surface + % GRID.lake.unfrozenWaterSurface = true; %tsvd not needed any more + %note SolarAzEl.m delivers only an approximation of sun position / t must be in UTC + [~, sun_elevation] = SolarAzEl(t,PARA.location.latitude,PARA.location.longitude,PARA.location.altitude); + PARA.water.albedo = waterAlbedo(sun_elevation, FORCING.i.wind); + PARA.surf.albedo = PARA.water.albedo; + PARA.surf.epsilon = PARA.water.epsilon; + [PARA.surf.z0, z0t, z0q] = flake_roughnessLength(PARA.water.fetch, FORCING.i.wind, SEB.u_star, 0); + PARA.surf.z0=real(PARA.surf.z0); + PARA.surf.rs = PARA.water.rs; + +elseif GRID.lake.ice.cT_domain(GRID.air.cT_domain_lb+1)==1 % ice surface + PARA.surf.albedo = PARA.ice.albedo; + PARA.surf.epsilon = PARA.ice.epsilon; + [PARA.surf.z0, z0t, z0q] = flake_roughnessLength(PARA.water.fetch, FORCING.i.wind, SEB.u_star, 1); + PARA.surf.z0=real(PARA.surf.z0); + PARA.surf.rs = PARA.ice.rs; +end + end - \ No newline at end of file diff --git a/modules/cryoGridSEB/surfaceEnergyBalance.m b/modules/cryoGridSEB/surfaceEnergyBalance.m deleted file mode 100644 index d083466..0000000 --- a/modules/cryoGridSEB/surfaceEnergyBalance.m +++ /dev/null @@ -1,63 +0,0 @@ -function SEB=surfaceEnergyBalance(T, wc, FORCING, GRID, PARA, SEB); - - -Lstar=mean(SEB.L_star); - -sigma=5.67e-8; %Stefan-Boltzmann const. -z=PARA.technical.z; - -Qh=real(Q_h(FORCING.i.wind, z, PARA.surf.z0, FORCING.i.Tair, T(GRID.air.cT_domain_lb+1), Lstar, FORCING.i.p, FORCING.i.q)); - - - -%______here SW radiation is calculated_____________________________________ -dE_dt=GRID.general.cT_grid.*0; -Qsolar=GRID.general.cT_grid.*0; - -dE_dt(GRID.air.cT_domain_lb+1,1)=(1-PARA.surf.albedo).*FORCING.i.Sin; -%------ snow surface (solid state green house effect) --------------------- -if ~isempty(GRID.snow.cT_domain_ub) - beta=PARA.snow.extinction; - Qsolar(GRID.snow.cT_domain_ub:GRID.snow.cT_domain_lb+1) = dE_dt(GRID.snow.cT_domain_ub) .* exp(-beta.*(GRID.general.K_grid(GRID.snow.cT_domain_ub:GRID.snow.cT_domain_lb+1)-GRID.general.K_grid(GRID.snow.cT_domain_ub))); - dE_dt(GRID.snow.cT_domain_ub:GRID.snow.cT_domain_lb) = -Qsolar(GRID.snow.cT_domain_ub+1:GRID.snow.cT_domain_lb+1) + Qsolar(GRID.snow.cT_domain_ub:GRID.snow.cT_domain_lb); - %put the rest to cell below snow - dE_dt(GRID.snow.cT_domain_lb+1) = Qsolar(GRID.snow.cT_domain_lb+1); -end - - -%__________________________________________________________________________ -Sout = PARA.surf.albedo*FORCING.i.Sin; -Lout = PARA.surf.epsilon.*sigma.*(T(GRID.air.cT_domain_lb+1)+273.15).^4+(1-PARA.surf.epsilon).*FORCING.i.Lin; -Qnet = FORCING.i.Sin-Sout + FORCING.i.Lin - Lout ; -Qg = Qnet-Qh-Qe; - - -%calculate ET - -if ~isempty(GRID.snow.cT_domain_ub) || T(GRID.soil.cT_domain_ub)<=0 %snow cover or uppermost grid cell frozen - Qe=real(Q_eq(FORCING.i.wind, z, PARA.surf.z0, FORCING.i.q, FORCING.i.Tair, T(GRID.air.cT_domain_lb+1), Lstar, PARA.surf.rs, FORCING.i.p)); -else - Qe_pot=real(Q_eq(FORCING.i.wind, z, PARA.surf.z0, FORCING.i.q, FORCING.i.Tair, T(GRID.air.cT_domain_lb+1), Lstar, 0, FORCING.i.p)); - fraction - -end - - -dE_dt(GRID.air.cT_domain_lb+1) = dE_dt(GRID.air.cT_domain_lb+1) ... - + PARA.surf.epsilon.*FORCING.i.Lin ... - - PARA.surf.epsilon.*sigma.*(T(GRID.air.cT_domain_lb+1)+273.15).^4 ... - - Qh - Qe; %Qe positive: cooling of soil =>epaporation/subl. => loss of SWE - - - - - -SEB.dE_dt_SEB = dE_dt; -SEB.Qnet = Qnet; -SEB.Qh = Qh; -SEB.Qe = Qe; -SEB.Qg = Qg; -SEB.Sout = Sout; -SEB.Lout = Lout; - - \ No newline at end of file diff --git a/modules/cryoGridSnow/CryoGridSnow.m b/modules/cryoGridSnow/CryoGridSnow.m index 1a4aca0..431a664 100644 --- a/modules/cryoGridSnow/CryoGridSnow.m +++ b/modules/cryoGridSnow/CryoGridSnow.m @@ -1,12 +1,17 @@ function [T, GRID, PARA, SEB, BALANCE] = CryoGridSnow(T, GRID, FORCING, SEB, PARA, c_temp, timestep, BALANCE) - +% zzz line 4 commented out now because of problem 7.7.2014!!! + if(~isempty(GRID.lake.water.cT_domain_ub)) +% assert(GRID.lake.water.cT_domain(GRID.lake.water.cT_domain_ub)+GRID.snow.cT_domain(GRID.lake.water.cT_domain_ub-1)<2,'snow on lake!'); + % disp('snow on lake!') +%%% assert(GRID.lake.water.cT_domain_ub-GRID.snow.cT_domain_lb>1,'snow on lake!') + end if ~isempty(GRID.snow.cT_domain_ub) %snow cover already exitis %----------calculate snow surface albedo ------------------------------ if max(T(GRID.snow.cT_domain))>=0 % melting conditions PARA.snow.albedo = PARA.snow.min_albedo + (PARA.snow.albedo - PARA.snow.min_albedo) ... - .* exp(-PARA.snow.tau_f .* timestep.*24.*3600 ./ PARA.snow.tau_1); + .* exp(-PARA.snow.tau_f .* timestep.*24.*3600 ./ PARA.snow.tau_1); else PARA.snow.albedo=max(PARA.snow.albedo-PARA.snow.tau_a.*timestep.*24.*3600./PARA.snow.tau_1, PARA.snow.min_albedo); end @@ -14,15 +19,14 @@ %--------SEB.sublimation----------------------------------------------- GRID.snow.Snow_i(GRID.snow.cT_domain_ub) = GRID.snow.Snow_i(GRID.snow.cT_domain_ub) ... - - SEB.Qe.*timestep.*24.*3600./PARA.constants.L_sg./PARA.constants.rho_i;%- SEB.Qe.*timestep.*24.*3600./(L+L_lv)./1000; + - SEB.Qe.*timestep.*24.*3600./PARA.constants.L_sg./PARA.constants.rho_i;%- SEB.Qe.*timestep.*24.*3600./(L+L_lv)./1000; nonAirFractionUppermostGridCell = (GRID.snow.Snow_i(GRID.snow.cT_domain_ub)+GRID.snow.Snow_w(GRID.snow.cT_domain_ub))./... (GRID.snow.Snow_i(GRID.snow.cT_domain_ub)+GRID.snow.Snow_w(GRID.snow.cT_domain_ub)+GRID.snow.Snow_a(GRID.snow.cT_domain_ub)); - GRID.snow.Snow_a(GRID.snow.cT_domain_ub) = GRID.snow.Snow_a(GRID.snow.cT_domain_ub) ... - - ( SEB.Qe.*timestep.*24.*3600./PARA.constants.L_sg./PARA.constants.rho_i./nonAirFractionUppermostGridCell ...% - ( SEB.Qe.*timestep.*24.*3600./(L+L_lv)./1000./nonAirFractionUppermostGridCell ... - - SEB.Qe.*timestep.*24.*3600./PARA.constants.L_sg./PARA.constants.rho_i); %- SEB.Qe.*timestep.*24.*3600./(L+L_lv)./1000); - + GRID.snow.Snow_a(GRID.snow.cT_domain_ub) = GRID.snow.Snow_a(GRID.snow.cT_domain_ub) ... + - ( SEB.Qe.*timestep.*24.*3600./PARA.constants.L_sg./PARA.constants.rho_i./nonAirFractionUppermostGridCell ...% - ( SEB.Qe.*timestep.*24.*3600./(L+L_lv)./1000./nonAirFractionUppermostGridCell ... + - SEB.Qe.*timestep.*24.*3600./PARA.constants.L_sg./PARA.constants.rho_i); %- SEB.Qe.*timestep.*24.*3600./(L+L_lv)./1000); BALANCE.water.ds = BALANCE.water.ds - SEB.Qe.*timestep.*24.*3600./PARA.constants.L_sg./PARA.constants.rho_i*1000; % sublimation in [mm] @@ -30,65 +34,71 @@ if max(T(GRID.snow.cT_domain))>0 || FORCING.i.rainfall>0 || sum(GRID.snow.Snow_w)>0 %cases when melt or infiltration occur [T(GRID.snow.cT_domain),... - GRID.snow.Snow_i(GRID.snow.cT_domain),... - GRID.snow.Snow_w(GRID.snow.cT_domain),... - GRID.snow.Snow_a(GRID.snow.cT_domain),... - newMelt] = ... + GRID.snow.Snow_i(GRID.snow.cT_domain),... + GRID.snow.Snow_w(GRID.snow.cT_domain),... + GRID.snow.Snow_a(GRID.snow.cT_domain),... + newMelt] = ... snowMelt(T(GRID.snow.cT_domain),... - GRID.snow.Snow_i(GRID.snow.cT_domain),... - GRID.snow.Snow_w(GRID.snow.cT_domain),... - GRID.snow.Snow_a(GRID.snow.cT_domain),... - FORCING.i.rainfall.*timestep./1000,... - c_temp(GRID.snow.cT_domain),... - PARA); - - %account for meltwater in water balance + GRID.snow.Snow_i(GRID.snow.cT_domain),... + GRID.snow.Snow_w(GRID.snow.cT_domain),... + GRID.snow.Snow_a(GRID.snow.cT_domain),... + FORCING.i.rainfall.*timestep./1000,... + c_temp(GRID.snow.cT_domain),... + PARA); + + GRID.lake.residualWater = GRID.lake.residualWater + newMelt; % zzz BALANCE.water.dr_snowmelt = BALANCE.water.dr_snowmelt + (-newMelt.*1000); % in [mm] end - %-------- add the new snow to the upper most snow cell in the case of a exisiting snow cover ------------------- - if isempty(PARA.snow.maxSnow) + %-------- add the new snow to the upper most snow cell in the case of a exisiting snow cover ------------------- + if isempty( PARA.location.absolute_maxSnow_altitude ) deltaSnow_i = max(0, FORCING.i.snowfall.*timestep./1000); else snowHeight = abs( GRID.general.K_grid(GRID.snow.cT_domain_ub) - GRID.general.K_grid(GRID.snow.cT_domain_lb+1) ); - if snowHeight>PARA.snow.maxSnow - warning(' excess snow occurs '); - end + maxSnowHeight = PARA.location.absolute_maxSnow_altitude - getAltitude( PARA, GRID ); + + % assert(maxSnowHeight - snowHeight >=0,'neg snow height!') %ttt + deltaSnow_i = max( [ 0, ... - min( [ FORCING.i.snowfall.*timestep./1000, ... - (PARA.snow.maxSnow - snowHeight ) .* PARA.snow.rho_snow ./ PARA.constants.rho_w ] ) ] ); %ensures that no more than maxSnow can accumulate + min( [ FORCING.i.snowfall.*timestep./1000, ... + (maxSnowHeight - snowHeight ) .* PARA.snow.rho_snow ./ PARA.constants.rho_w ] ) ] ); %ensures that no more than maxSnow can accumulate %account for excess snow in water balance - BALANCE.water.dr_excessSnow = BALANCE.water.dr_excessSnow -( FORCING.i.snowfall.*timestep - deltaSnow_i*1000 ); %defined as negative when snow is removed; in [mm] + BALANCE.water.dr_excessSnow = BALANCE.water.dr_excessSnow -( FORCING.i.snowfall.*timestep - deltaSnow_i*1000 ); %defined as negative when snow is removed end GRID.snow.Snow_i(GRID.snow.cT_domain_ub) = GRID.snow.Snow_i(GRID.snow.cT_domain_ub) ... - + deltaSnow_i; - - GRID.snow.Snow_a(GRID.snow.cT_domain_ub) = GRID.snow.Snow_a(GRID.snow.cT_domain_ub) ... - + (deltaSnow_i./(PARA.snow.rho_snow./1000) ... - - deltaSnow_i); + + deltaSnow_i; + GRID.snow.Snow_a(GRID.snow.cT_domain_ub) = GRID.snow.Snow_a(GRID.snow.cT_domain_ub) ... + + ( deltaSnow_i./(PARA.snow.rho_snow./ PARA.constants.rho_w) - deltaSnow_i); else %no snow cover - %---------- add the new snow into initial SWE variable in case of no snow cover------------------ - - GRID.snow.SWEinitial = GRID.snow.SWEinitial + FORCING.i.snowfall.*timestep./1000 - GRID.snow.SWEinitial.*0.1.*timestep; - % account for decrease in SWEinitial in water balance + if(isempty(GRID.lake.water.cT_domain_ub) || ~isempty(GRID.lake.ice.cT_domain_ub) ) %tsvd only allow for snow buildup if no lake, or if lake ice exists + GRID.snow.SWEinitial = GRID.snow.SWEinitial + FORCING.i.snowfall.*timestep./1000 - GRID.snow.SWEinitial.*0.1.*timestep; + end + GRID.lake.residualWater = GRID.lake.residualWater + GRID.snow.SWEinitial.*0.1.*timestep; % zzz check with Jan: what if rain on lake ice...(collect in residualWater?), where collect rain on lake water? what if SWEinitial exists, then lake ice melts away...? BALANCE.water.dr_snowmelt = BALANCE.water.dr_snowmelt - GRID.snow.SWEinitial.*0.1.*timestep*1000; %SWEinitial decreasing counted as surface runoff %----- add the rainfall as runoff in case of no infiltration into frozen ground - if ~PARA.modules.infiltration || (PARA.modules.infiltration && T(GRID.soil.cT_domain_ub)<=0 )%no infiltration scheme or uppermost soil cell frozen + %ppp + if ~PARA.modules.infiltration || (PARA.modules.infiltration && T(GRID.soil.cT_domain_ub)<=0 )%no infiltration scheme or uppermost soil cell frozen zzz + GRID.lake.residualWater = GRID.lake.residualWater + FORCING.i.rainfall.*timestep./1000; BALANCE.water.dr_rain = BALANCE.water.dr_rain - FORCING.i.rainfall.*timestep; end - end %--------- update albedo after fresh fallen snow -------------------------- % determine time of last snowfall for albedo calculation - SEB.newSnow = SEB.newSnow-SEB.newSnow.*0.1.*timestep + FORCING.i.snowfall.*timestep./1000; + SEB.newSnow = SEB.newSnow-SEB.newSnow.*0.1.*timestep + FORCING.i.snowfall.*timestep./1000; if SEB.newSnow>= PARA.technical.SWEperCell/2 PARA.snow.albedo=PARA.snow.max_albedo; SEB.newSnow=0; end + + if(~isempty(GRID.lake.water.cT_domain_ub)) +% zzz commented out now for fix... assert(GRID.lake.water.cT_domain(GRID.lake.water.cT_domain_ub)+GRID.snow.cT_domain(GRID.lake.water.cT_domain_ub-1)<2,'snow on lake (2)!'); +% assert(GRID.lake.water.cT_domain_ub-GRID.snow.cT_domain_lb>1,'snow on lake (2)!') +% disp('snow on lake 2') + end end \ No newline at end of file diff --git a/modules/cryoGridSnow/maxLiqWater.m b/modules/cryoGridSnow/maxLiqWater.m index 639c04c..3a9f31b 100644 --- a/modules/cryoGridSnow/maxLiqWater.m +++ b/modules/cryoGridSnow/maxLiqWater.m @@ -1,11 +1,10 @@ -function maxLiqWater=maxLiqWater(T, snow_i, snow_w, snow_a, poreSpace, c_temp, PARA) +function maxLiqWater=maxLiqWater(T, snow_i, snow_w, snow_a, poreSpace, c_temp) - L=PARA.constants.L_sl.*PARA.constants.rho_w; %3.34e8; +L=3.34e8; - waterHoldingPot=0.05.* (poreSpace.*snow_i)./(1-poreSpace); %in m; 5% of the pore space can be filled by water +waterHoldingPot=0.05.* (poreSpace.*snow_i)./(1-poreSpace); %in m; 5% of the pore space can be filled by water + +maxLiqWater = (waterHoldingPot - snow_w - T.*c_temp.*(snow_i+snow_w+snow_a)./L); %in m +maxLiqWater = min([snow_a maxLiqWater]'); +maxLiqWater = maxLiqWater'; % negative if snow_w>waterHoldingPot - maxLiqWater = (waterHoldingPot - snow_w - T.*c_temp.*(snow_i+snow_w+snow_a)./L); %in m - maxLiqWater = min([snow_a maxLiqWater]'); - maxLiqWater = maxLiqWater'; % negative if snow_w>waterHoldingPot - -end \ No newline at end of file diff --git a/modules/cryoGridSnow/melt.m b/modules/cryoGridSnow/melt.m index 7b893ba..563d797 100644 --- a/modules/cryoGridSnow/melt.m +++ b/modules/cryoGridSnow/melt.m @@ -1,37 +1,36 @@ -function [T, snow_i, snow_w, snow_a]=melt(T, snow_i, snow_w, snow_a, poreSpace, c_temp, PARA) +function [T, snow_i, snow_w, snow_a]=melt(T, snow_i, snow_w, snow_a, poreSpace, c_temp) - L=PARA.constants.L_sl.*PARA.constants.rho_w; %3.34e8; +L=3.34e8; - pot_SWE=double(T>0).*T.*c_temp.*(snow_i+snow_w+snow_a)./L; +pot_SWE=double(T>0).*T.*c_temp.*(snow_i+snow_w+snow_a)./L; - T=double(T<=0).*T; - delta_SWE=pot_SWE; +T=double(T<=0).*T; +delta_SWE=pot_SWE; - if sum(pot_SWE>snow_i)~=0% in one cell more energy than needed to melt entire cell - for i=1:size(T,1)-1 - delta_SWE(i)=min([snow_i(i) pot_SWE(i)]); - SWEres=pot_SWE(i) - delta_SWE(i); - if (snow_i(i+1)+snow_w(i+1)+snow_a(i+1))~=0 - T(i+1)=T(i+1) + SWEres.*L./(c_temp(i+1).*(snow_i(i+1)+snow_w(i+1)+snow_a(i+1))); - end - pot_SWE(i+1)=pot_SWE(i+1) + double(T(i+1)>0).*T(i+1).*c_temp(i+1).*(snow_i(i+1)+snow_w(i+1)+snow_a(i+1))./L; - T(i+1)=double(T(i+1)<=0).*T(i+1); - end - i=size(T,1); +if sum(pot_SWE>snow_i)~=0% in one cell more energy than needed to melt entire cell + for i=1:size(T,1)-1 delta_SWE(i)=min([snow_i(i) pot_SWE(i)]); + SWEres=pot_SWE(i) - delta_SWE(i); + if (snow_i(i+1)+snow_w(i+1)+snow_a(i+1))~=0 + T(i+1)=T(i+1) + SWEres.*L./(c_temp(i+1).*(snow_i(i+1)+snow_w(i+1)+snow_a(i+1))); + end + pot_SWE(i+1)=pot_SWE(i+1) + double(T(i+1)>0).*T(i+1).*c_temp(i+1).*(snow_i(i+1)+snow_w(i+1)+snow_a(i+1))./L; + T(i+1)=double(T(i+1)<=0).*T(i+1); end - %T(find(isnan(T(:,1))==1),1)=0; - + i=size(T,1); + delta_SWE(i)=min([snow_i(i) pot_SWE(i)]); +end +%T(find(isnan(T(:,1))==1),1)=0; - %delta_SWE - % delta_SWE = min([snow_i double(T>0).*T.*c_temp.*(snow_i+snow_w+snow_a)./L]'); %Energy conserving since sensible heat term is only excess energy from SEB+conduction - % delta_SWE=delta_SWE'; + +%delta_SWE +% delta_SWE = min([snow_i double(T>0).*T.*c_temp.*(snow_i+snow_w+snow_a)./L]'); %Energy conserving since sensible heat term is only excess energy from SEB+conduction +% delta_SWE=delta_SWE'; - snow_i=snow_i - delta_SWE; %melting only changes SWE, not density theta_s +snow_i=snow_i - delta_SWE; %melting only changes SWE, not density theta_s - snow_w=snow_w + delta_SWE; - %snow_a=(poreSpace.*snow_i + poreSpace.*snow_w - snow_w)./(1-poreSpace); %pore space stays stays constant +snow_w=snow_w + delta_SWE; +%snow_a=(poreSpace.*snow_i + poreSpace.*snow_w - snow_w)./(1-poreSpace); %pore space stays stays constant -end \ No newline at end of file diff --git a/modules/cryoGridSnow/refreeze.m b/modules/cryoGridSnow/refreeze.m index 279c8ce..566bb58 100644 --- a/modules/cryoGridSnow/refreeze.m +++ b/modules/cryoGridSnow/refreeze.m @@ -1,6 +1,6 @@ function [T, snow_i, snow_w]=refreeze(T, snow_i, snow_w, snow_a, c_temp, PARA) - L=PARA.constants.L_sl.*PARA.constants.rho_w;%3.34e8; + L=PARA.constants.L_sl.*PARA.constants.rho_w; %3.34e8; delta_SWE = min([snow_w, -1.*double(T<=0).*T.*c_temp.*(snow_i+snow_w+snow_a)./L]'); delta_SWE=delta_SWE'; diff --git a/modules/cryoGridSnow/snowMelt.m b/modules/cryoGridSnow/snowMelt.m index 6b4c4f4..d319297 100644 --- a/modules/cryoGridSnow/snowMelt.m +++ b/modules/cryoGridSnow/snowMelt.m @@ -1,31 +1,65 @@ function [T, snow_i, snow_w, snow_a, runoff] = snowMelt(T, snow_i, snow_w, snow_a, water_flux, c_temp, PARA) +%------------melt the snow for cells with T>0------------ runoff=0; - - %------------melt the snow for cells with T>0------------ + +%energyC=sum(T.*c_temp.*(snow_i+snow_w+snow_a))+(sum(snow_w)+water_flux).*3.34e8; + poreSpace=(snow_w+snow_a)./(snow_i+snow_w+snow_a); - poreSpace(isnan(poreSpace(:,1)),1)=300/1000; + poreSpace(isnan(poreSpace(:,1)),1)=300/1000; %JAN: what does this number mean?? - [T, snow_i, snow_w, snow_a]=melt(T, snow_i, snow_w, snow_a, poreSpace, c_temp, PARA); + [T, snow_i, snow_w, snow_a]=melt(T, snow_i, snow_w, snow_a, poreSpace, c_temp); + % energyC2=sum(T.*c_temp.*(snow_i+snow_w+snow_a))+(sum(snow_w)+water_flux).*3.34e8; pS=(snow_w+snow_a)./(snow_i+snow_w+snow_a); pS(isnan(pS(:,1)),1)=300/1000; - %-----------calculate how much water (in m) a snow cell can hold----- - maxWater=maxLiqWater(T(pS>0), snow_i(pS>0), snow_w(pS>0), snow_a(pS>0), poreSpace(pS>0), c_temp(pS>0), PARA); + + + + + +%-----------calculate how much water (in m) a snow cell can hold----- + + maxWater=maxLiqWater(T(pS>0), snow_i(pS>0), snow_w(pS>0), snow_a(pS>0), poreSpace(pS>0), c_temp(pS>0)); + + if water_flux>0 || (~isempty(maxWater) && min(maxWater)<0) %infiltration occurs - %------------infiltrate from top to bottom-------------------- +%------------infiltrate from top to bottom-------------------- + + [snow_w(pS>0), snow_a(pS>0), water_flux] = infiltrateTop2Bottom(snow_i(pS>0), snow_w(pS>0), snow_a(pS>0), poreSpace(pS>0), maxWater, water_flux); + + + +%------------infiltrate bottom to top---------------------- + + % energyC3=sum(T.*c_temp.*(snow_i+snow_w+snow_a))+(sum(snow_w)+water_flux).*3.34e8; + + + - %------------infiltrate bottom to top---------------------- if water_flux>0 [snow_w(pS>0), snow_a(pS>0), runoff] = infiltrateBottom2Top(snow_i(pS>0), snow_w(pS>0), snow_a(pS>0), water_flux); end + + % energyC4=sum(T.*c_temp.*(snow_i+snow_w+snow_a))+(sum(snow_w)+runoff).*3.34e8; + + +%----------shift energy from water to T due to refreezing--------- end - %----------shift energy from water to T due to refreezing--------- + [T, snow_i, snow_w] = refreeze(T, snow_i, snow_w, snow_a, c_temp, PARA); + + % energyC5=sum(T.*c_temp.*(snow_i+snow_w+snow_a))+(sum(snow_w)+runoff).*3.34e8; + + % if abs((energyC5-energyC)./energyC)>0.01 + % energyC + % energyC2 + + % energyC5 end diff --git a/modules/cryoGridSnow/updateGRID_snow.m b/modules/cryoGridSnow/updateGRID_snow.m index 3467dc3..1c0a2c3 100644 --- a/modules/cryoGridSnow/updateGRID_snow.m +++ b/modules/cryoGridSnow/updateGRID_snow.m @@ -1,157 +1,215 @@ function [GRID, T, BALANCE] = updateGRID_snow(T, GRID, PARA, BALANCE) -snowCellSize=GRID.snow.snowCellSize; - -if isempty(GRID.snow.cT_domain_lb)==1 %no snow exists - - if GRID.snow.SWEinitial>=PARA.technical.SWEperCell/2 %create and initialize first cell of snow - - %------ modify snow and air grid ----------------------------- - GRID.snow.cT_domain(GRID.air.cT_domain_lb)=1; - GRID.snow.K_domain(GRID.air.K_domain_lb)=1; - [GRID.snow.cT_domain_lb, GRID.snow.cT_domain_ub] = LayerIndex(GRID.snow.cT_domain); - [GRID.snow.K_domain_lb, GRID.snow.K_domain_ub] = LayerIndex(GRID.snow.K_domain); - - GRID.air.cT_domain(GRID.air.cT_domain_lb)=0; - GRID.air.K_domain(GRID.air.K_domain_lb)=0; - [GRID.air.cT_domain_lb, GRID.air.cT_domain_ub] = LayerIndex(GRID.air.cT_domain); - [GRID.air.K_domain_lb, GRID.air.K_domain_ub] = LayerIndex(GRID.air.K_domain); - - % ------- update SWE grid ------------------------------------- - GRID.snow.Snow_i(GRID.snow.cT_domain_ub) = GRID.snow.SWEinitial; - GRID.snow.Snow_w(GRID.snow.cT_domain_ub) = 0; - GRID.snow.Snow_a(GRID.snow.cT_domain_ub) = (GRID.snow.SWEinitial./(PARA.snow.rho_snow./1000) - GRID.snow.SWEinitial); - GRID.snow.SWEinitial=0; - - % -------- update K grid ------------------------------------- - GRID.general.K_grid(GRID.snow.K_domain_ub)=-1.*( GRID.snow.Snow_i(GRID.snow.cT_domain_ub) + GRID.snow.Snow_a(GRID.snow.cT_domain_ub) + GRID.snow.Snow_w(GRID.snow.cT_domain_ub) ); - T(GRID.snow.cT_domain_ub)=T(GRID.air.cT_domain_lb); - - end - -else %snow exists + snowCellSize=GRID.snow.snowCellSize; - check_change=false; - GRID.general.K_grid(GRID.snow.cT_domain_ub) = GRID.general.K_grid(GRID.snow.cT_domain_ub+1) -... - ( GRID.snow.Snow_i(GRID.snow.cT_domain_ub) + GRID.snow.Snow_w(GRID.snow.cT_domain_ub) + GRID.snow.Snow_a(GRID.snow.cT_domain_ub)); %updates the position of the uppermost snow grid cell + if isempty(GRID.snow.cT_domain_lb)==1 %no snow exists - if GRID.snow.Snow_i(GRID.snow.cT_domain_ub)>=1.5.*PARA.technical.SWEperCell %create new grid cell % JAN: why 1.5 ??? + if GRID.snow.SWEinitial>=PARA.technical.SWEperCell/2 %create and initialize first cell of snow + + %------ modify snow and air grid ----------------------------- + GRID.snow.cT_domain(GRID.air.cT_domain_lb)=1; + GRID.snow.K_domain(GRID.air.K_domain_lb)=1; + [GRID.snow.cT_domain_lb, GRID.snow.cT_domain_ub] = LayerIndex(GRID.snow.cT_domain); + [GRID.snow.K_domain_lb, GRID.snow.K_domain_ub] = LayerIndex(GRID.snow.K_domain); + + GRID.air.cT_domain(GRID.air.cT_domain_lb)=0; + GRID.air.K_domain(GRID.air.K_domain_lb)=0; + [GRID.air.cT_domain_lb, GRID.air.cT_domain_ub] = LayerIndex(GRID.air.cT_domain); + [GRID.air.K_domain_lb, GRID.air.K_domain_ub] = LayerIndex(GRID.air.K_domain); + + % ------- update SWE grid ------------------------------------- + GRID.snow.Snow_i(GRID.snow.cT_domain_ub) = GRID.snow.SWEinitial; + GRID.snow.Snow_w(GRID.snow.cT_domain_ub) = 0; + GRID.snow.Snow_a(GRID.snow.cT_domain_ub) = (GRID.snow.SWEinitial./(PARA.snow.rho_snow./PARA.constants.rho_w) - GRID.snow.SWEinitial); + GRID.snow.SWEinitial=0; + + % -------- update K grid ------------------------------------- + assert(~isnan(GRID.general.K_grid(GRID.snow.cT_domain_ub+1)),' error in uppermost lake cell K_grid'); %tsvd + + GRID.general.K_grid(GRID.snow.cT_domain_ub) = GRID.general.K_grid(GRID.snow.cT_domain_ub+1) - ( GRID.snow.Snow_i(GRID.snow.cT_domain_ub) + GRID.snow.Snow_a(GRID.snow.cT_domain_ub) + GRID.snow.Snow_w(GRID.snow.cT_domain_ub) ); + %tsvd GRID.general.K_grid(GRID.snow.K_domain_ub)=-1.*( GRID.snow.Snow_i(GRID.snow.cT_domain_ub) + GRID.snow.Snow_a(GRID.snow.cT_domain_ub) + GRID.snow.Snow_w(GRID.snow.cT_domain_ub) ); + T(GRID.snow.cT_domain_ub)=T(GRID.air.cT_domain_lb); + assert( sum(isnan( GRID.general.K_grid ) )==0, 'updateGRID_snow - error xxx') + assert( sum(isnan(GRID.snow.Snow_i) )==0,' GRID.snow.snow_i NAN 1') + end + + else %snow exists + + check_change=false; + assert( sum(isnan(GRID.snow.Snow_i) )==0,' GRID.snow.snow_i NAN 1') - %------ modify snow and air grid ----------------------------- - GRID.snow.cT_domain(GRID.air.cT_domain_lb)=1; - GRID.snow.K_domain(GRID.air.K_domain_lb)=1; - [GRID.snow.cT_domain_lb, GRID.snow.cT_domain_ub] = LayerIndex(GRID.snow.cT_domain); - [GRID.snow.K_domain_lb, GRID.snow.K_domain_ub] = LayerIndex(GRID.snow.K_domain); - - GRID.air.cT_domain(GRID.air.cT_domain_lb)=0; - GRID.air.K_domain(GRID.air.K_domain_lb)=0; - [GRID.air.cT_domain_lb, GRID.air.cT_domain_ub] = LayerIndex(GRID.air.cT_domain); - [GRID.air.K_domain_lb, GRID.air.K_domain_ub] = LayerIndex(GRID.air.K_domain); + GRID.general.K_grid(GRID.snow.cT_domain_ub) = GRID.general.K_grid(GRID.snow.cT_domain_ub+1) -... + ( GRID.snow.Snow_i(GRID.snow.cT_domain_ub) + GRID.snow.Snow_w(GRID.snow.cT_domain_ub) + GRID.snow.Snow_a(GRID.snow.cT_domain_ub)); %updates the position of the uppermost snow grid cell + assert( ~isnan( GRID.general.K_grid(GRID.snow.cT_domain_ub) ), 'updateGRID_snow - error in uppermost snow cell position' ); + if(~isempty(GRID.lake.water.cT_domain_ub)) + assert(GRID.lake.water.cT_domain(GRID.lake.water.cT_domain_ub)+GRID.snow.cT_domain(GRID.lake.water.cT_domain_ub-1)<2,'snow on lake!'); + % assert(GRID.lake.water.cT_domain_ub-GRID.snow.cT_domain_lb>1,'snow on lake in updateGRID_snow.m!') + end - % ------- update SWE grid - GRID.snow.Snow_i(GRID.snow.cT_domain_ub)=1./3.*GRID.snow.Snow_i(GRID.snow.cT_domain_ub+1); - GRID.snow.Snow_w(GRID.snow.cT_domain_ub)=1./3.*GRID.snow.Snow_w(GRID.snow.cT_domain_ub+1); - GRID.snow.Snow_a(GRID.snow.cT_domain_ub)=1./3.*GRID.snow.Snow_a(GRID.snow.cT_domain_ub+1); - GRID.snow.Snow_i(GRID.snow.cT_domain_ub+1)=GRID.snow.Snow_i(GRID.snow.cT_domain_ub+1) - GRID.snow.Snow_i(GRID.snow.cT_domain_ub); - GRID.snow.Snow_w(GRID.snow.cT_domain_ub+1)=GRID.snow.Snow_w(GRID.snow.cT_domain_ub+1) - GRID.snow.Snow_w(GRID.snow.cT_domain_ub); - GRID.snow.Snow_a(GRID.snow.cT_domain_ub+1)=GRID.snow.Snow_a(GRID.snow.cT_domain_ub+1) - GRID.snow.Snow_a(GRID.snow.cT_domain_ub); - T(GRID.snow.cT_domain_ub)=T(GRID.snow.cT_domain_ub+1); - check_change=true; - end - - if min(GRID.snow.Snow_i(GRID.snow.cT_domain))<=0.5.*PARA.technical.SWEperCell %avoid looping when unnecessary - for i=GRID.snow.cT_domain_ub:GRID.snow.cT_domain_lb-1 %check all snow cells except for the lowermost one for too small ice and water contents - merge with lower cell - - if GRID.snow.Snow_i(i)<=0.5.*PARA.technical.SWEperCell - - %------ modify snow and air grid ----------------------------- - GRID.snow.cT_domain(GRID.snow.cT_domain_ub)=0; - GRID.snow.K_domain(GRID.snow.K_domain_ub)=0; - [GRID.snow.cT_domain_lb, GRID.snow.cT_domain_ub] = LayerIndex(GRID.snow.cT_domain); - [GRID.snow.K_domain_lb, GRID.snow.K_domain_ub] = LayerIndex(GRID.snow.K_domain); - - GRID.air.cT_domain(GRID.air.cT_domain_lb+1)=1; - GRID.air.K_domain(GRID.air.K_domain_lb+1)=1; - [GRID.air.cT_domain_lb, GRID.air.cT_domain_ub] = LayerIndex(GRID.air.cT_domain); - [GRID.air.K_domain_lb, GRID.air.K_domain_ub] = LayerIndex(GRID.air.K_domain); - - %-------- rearrange SWE and T grids -------------------------- - GRID.snow.Snow_i(i+1)=GRID.snow.Snow_i(i+1)+GRID.snow.Snow_i(i); - GRID.snow.Snow_w(i+1)=GRID.snow.Snow_w(i+1)+GRID.snow.Snow_w(i); - GRID.snow.Snow_a(i+1)=GRID.snow.Snow_a(i+1)+GRID.snow.Snow_a(i); - GRID.snow.Snow_i(2:i)=GRID.snow.Snow_i(1:i-1); - GRID.snow.Snow_w(2:i)=GRID.snow.Snow_w(1:i-1); - GRID.snow.Snow_a(2:i)=GRID.snow.Snow_a(1:i-1); - T(i+1)=(T(i+1)+T(i))/2; - T(2:i)=T(1:i-1); - end + if GRID.snow.Snow_i(GRID.snow.cT_domain_ub)>=1.5.*PARA.technical.SWEperCell %create new grid cell + + %------ modify snow and air grid ----------------------------- + GRID.snow.cT_domain(GRID.air.cT_domain_lb)=1; + GRID.snow.K_domain(GRID.air.K_domain_lb)=1; + [GRID.snow.cT_domain_lb, GRID.snow.cT_domain_ub] = LayerIndex(GRID.snow.cT_domain); + [GRID.snow.K_domain_lb, GRID.snow.K_domain_ub] = LayerIndex(GRID.snow.K_domain); + + GRID.air.cT_domain(GRID.air.cT_domain_lb)=0; + GRID.air.K_domain(GRID.air.K_domain_lb)=0; + [GRID.air.cT_domain_lb, GRID.air.cT_domain_ub] = LayerIndex(GRID.air.cT_domain); + [GRID.air.K_domain_lb, GRID.air.K_domain_ub] = LayerIndex(GRID.air.K_domain); + + % ------- update SWE grid + GRID.snow.Snow_i(GRID.snow.cT_domain_ub)=1./3.*GRID.snow.Snow_i(GRID.snow.cT_domain_ub+1); + GRID.snow.Snow_w(GRID.snow.cT_domain_ub)=1./3.*GRID.snow.Snow_w(GRID.snow.cT_domain_ub+1); + GRID.snow.Snow_a(GRID.snow.cT_domain_ub)=1./3.*GRID.snow.Snow_a(GRID.snow.cT_domain_ub+1); + GRID.snow.Snow_i(GRID.snow.cT_domain_ub+1)=GRID.snow.Snow_i(GRID.snow.cT_domain_ub+1) - GRID.snow.Snow_i(GRID.snow.cT_domain_ub); + GRID.snow.Snow_w(GRID.snow.cT_domain_ub+1)=GRID.snow.Snow_w(GRID.snow.cT_domain_ub+1) - GRID.snow.Snow_w(GRID.snow.cT_domain_ub); + GRID.snow.Snow_a(GRID.snow.cT_domain_ub+1)=GRID.snow.Snow_a(GRID.snow.cT_domain_ub+1) - GRID.snow.Snow_a(GRID.snow.cT_domain_ub); + T(GRID.snow.cT_domain_ub)=T(GRID.snow.cT_domain_ub+1); + check_change=true; end - check_change=true; - end - - if GRID.snow.Snow_i(GRID.snow.cT_domain_lb)<=0.5.*PARA.technical.SWEperCell && sum(GRID.snow.cT_domain)>=2 %lowermost grid cell has too little snow, but there still is 2 or more snow cells - merge with upper cell - - %------ modify snow and air grid ---------------------------------- - GRID.snow.cT_domain(GRID.snow.cT_domain_ub)=0; - GRID.snow.K_domain(GRID.snow.cT_domain_ub)=0; - [GRID.snow.cT_domain_lb, GRID.snow.cT_domain_ub] = LayerIndex(GRID.snow.cT_domain); - [GRID.snow.K_domain_lb, GRID.snow.K_domain_ub] = LayerIndex(GRID.snow.K_domain); - - GRID.air.cT_domain(GRID.air.cT_domain_lb+1)=1; - GRID.air.K_domain(GRID.air.K_domain_lb+1)=1; - [GRID.air.cT_domain_lb, GRID.air.cT_domain_ub] = LayerIndex(GRID.air.cT_domain); - [GRID.air.K_domain_lb, GRID.air.K_domain_ub] = LayerIndex(GRID.air.K_domain); - - %-------- rearrange SWE and T grids -------------------------- - GRID.snow.Snow_i(GRID.snow.cT_domain_lb)=GRID.snow.Snow_i(GRID.snow.cT_domain_lb)+GRID.snow.Snow_i(GRID.snow.cT_domain_lb-1); - GRID.snow.Snow_w(GRID.snow.cT_domain_lb)=GRID.snow.Snow_w(GRID.snow.cT_domain_lb)+GRID.snow.Snow_w(GRID.snow.cT_domain_lb-1); - GRID.snow.Snow_a(GRID.snow.cT_domain_lb)=GRID.snow.Snow_a(GRID.snow.cT_domain_lb)+GRID.snow.Snow_a(GRID.snow.cT_domain_lb-1); - GRID.snow.Snow_i(2:GRID.snow.cT_domain_lb-1)=GRID.snow.Snow_i(1:GRID.snow.cT_domain_lb-2); - GRID.snow.Snow_w(2:GRID.snow.cT_domain_lb-1)=GRID.snow.Snow_w(1:GRID.snow.cT_domain_lb-2); - GRID.snow.Snow_a(2:GRID.snow.cT_domain_lb-1)=GRID.snow.Snow_a(1:GRID.snow.cT_domain_lb-2); - - T(GRID.snow.cT_domain_lb)=(T(GRID.snow.cT_domain_lb)+T(GRID.snow.cT_domain_lb-1))/2; - T(2:GRID.snow.cT_domain_lb-1)=T(1:GRID.snow.cT_domain_lb-2); - - check_change=true; + + if min(GRID.snow.Snow_i(GRID.snow.cT_domain))<=0.5.*PARA.technical.SWEperCell %avoid looping when unnecessary + for i=GRID.snow.cT_domain_ub:GRID.snow.cT_domain_lb-1 %check all snow cells except for the lowermost one for too small ice and water contents - merge with lower cell + + if GRID.snow.Snow_i(i)<=0.5.*PARA.technical.SWEperCell + + %------ modify snow and air grid ----------------------------- + GRID.snow.cT_domain(GRID.snow.cT_domain_ub)=0; + GRID.snow.K_domain(GRID.snow.K_domain_ub)=0; + [GRID.snow.cT_domain_lb, GRID.snow.cT_domain_ub] = LayerIndex(GRID.snow.cT_domain); + [GRID.snow.K_domain_lb, GRID.snow.K_domain_ub] = LayerIndex(GRID.snow.K_domain); + + GRID.air.cT_domain(GRID.air.cT_domain_lb+1)=1; + GRID.air.K_domain(GRID.air.K_domain_lb+1)=1; + [GRID.air.cT_domain_lb, GRID.air.cT_domain_ub] = LayerIndex(GRID.air.cT_domain); + [GRID.air.K_domain_lb, GRID.air.K_domain_ub] = LayerIndex(GRID.air.K_domain); + + %-------- rearrange SWE and T grids -------------------------- + GRID.snow.Snow_i(i+1)=GRID.snow.Snow_i(i+1)+GRID.snow.Snow_i(i); + GRID.snow.Snow_w(i+1)=GRID.snow.Snow_w(i+1)+GRID.snow.Snow_w(i); + GRID.snow.Snow_a(i+1)=GRID.snow.Snow_a(i+1)+GRID.snow.Snow_a(i); + GRID.snow.Snow_i(2:i)=GRID.snow.Snow_i(1:i-1); + GRID.snow.Snow_w(2:i)=GRID.snow.Snow_w(1:i-1); + GRID.snow.Snow_a(2:i)=GRID.snow.Snow_a(1:i-1); + T(i+1)=(T(i+1)+T(i))/2; + T(2:i)=T(1:i-1); + end + end + check_change=true; + end + + if GRID.snow.Snow_i(GRID.snow.cT_domain_lb)<=0.5.*PARA.technical.SWEperCell && sum(GRID.snow.cT_domain)>=2 %lowermost grid cell has too little snow, but there still is 2 or more snow cells - merge with upper cell + + %------ modify snow and air grid ---------------------------------- + GRID.snow.cT_domain(GRID.snow.cT_domain_ub)=0; + GRID.snow.K_domain(GRID.snow.cT_domain_ub)=0; + [GRID.snow.cT_domain_lb, GRID.snow.cT_domain_ub] = LayerIndex(GRID.snow.cT_domain); + [GRID.snow.K_domain_lb, GRID.snow.K_domain_ub] = LayerIndex(GRID.snow.K_domain); + + GRID.air.cT_domain(GRID.air.cT_domain_lb+1)=1; + GRID.air.K_domain(GRID.air.K_domain_lb+1)=1; + [GRID.air.cT_domain_lb, GRID.air.cT_domain_ub] = LayerIndex(GRID.air.cT_domain); + [GRID.air.K_domain_lb, GRID.air.K_domain_ub] = LayerIndex(GRID.air.K_domain); + + %-------- rearrange SWE and T grids -------------------------- + GRID.snow.Snow_i(GRID.snow.cT_domain_lb)=GRID.snow.Snow_i(GRID.snow.cT_domain_lb)+GRID.snow.Snow_i(GRID.snow.cT_domain_lb-1); + GRID.snow.Snow_w(GRID.snow.cT_domain_lb)=GRID.snow.Snow_w(GRID.snow.cT_domain_lb)+GRID.snow.Snow_w(GRID.snow.cT_domain_lb-1); + GRID.snow.Snow_a(GRID.snow.cT_domain_lb)=GRID.snow.Snow_a(GRID.snow.cT_domain_lb)+GRID.snow.Snow_a(GRID.snow.cT_domain_lb-1); + GRID.snow.Snow_i(2:GRID.snow.cT_domain_lb-1)=GRID.snow.Snow_i(1:GRID.snow.cT_domain_lb-2); + GRID.snow.Snow_w(2:GRID.snow.cT_domain_lb-1)=GRID.snow.Snow_w(1:GRID.snow.cT_domain_lb-2); + GRID.snow.Snow_a(2:GRID.snow.cT_domain_lb-1)=GRID.snow.Snow_a(1:GRID.snow.cT_domain_lb-2); + + T(GRID.snow.cT_domain_lb)=(T(GRID.snow.cT_domain_lb)+T(GRID.snow.cT_domain_lb-1))/2; + T(2:GRID.snow.cT_domain_lb-1)=T(1:GRID.snow.cT_domain_lb-2); + + check_change=true; + end + + + if check_change==true %update grid spacings + + assert( sum( sum( isnan(GRID.snow.Snow_i(GRID.snow.cT_domain) + GRID.snow.Snow_w(GRID.snow.cT_domain) + GRID.snow.Snow_a(GRID.snow.cT_domain)) ) ) == 0 , 'updateGRID_snow - error in Snow_i/a/w grid' ); + + % snow grid + soilTop = GRID.general.K_grid(GRID.snow.cT_domain_lb+1); %tsvd in case of a lake, soilTop corresponds to lakeTop + GRID.general.K_grid(GRID.snow.cT_domain)= soilTop - flipud(cumsum(flipud(GRID.snow.Snow_i(GRID.snow.cT_domain) + GRID.snow.Snow_w(GRID.snow.cT_domain) + GRID.snow.Snow_a(GRID.snow.cT_domain)))); + GRID.general.K_grid(GRID.air.cT_domain)=[GRID.general.K_grid(GRID.air.cT_domain_lb)+(-snowCellSize)*(GRID.air.cT_domain_lb-1):snowCellSize:GRID.general.K_grid(GRID.air.cT_domain_lb)]'; + + assert( sum( isnan( GRID.general.K_grid ) )==0, 'updateGRID_snow - error in K grid after snow grid update') + + % air grid zzz this is not in FLAKE version... jjj + snowTop = GRID.general.K_grid(GRID.snow.cT_domain_ub); + numAirCells = sum( GRID.air.cT_domain ); + GRID.general.K_grid(GRID.air.cT_domain) = flip( snowTop-snowCellSize:-snowCellSize:snowTop-snowCellSize*numAirCells ); + + assert( sum( isnan( GRID.general.K_grid ) )==0, 'updateGRID_snow - error in K grid after air grid update') + + %GRID.general.K_grid(GRID.air.cT_domain) = [GRID.general.K_grid(GRID.air.cT_domain_lb)+(-2*snowCellSize)*(GRID.air.cT_domain_lb-1):2*snowCellSize:GRID.general.K_grid(GRID.air.cT_domain_lb)]'; + end + + if (GRID.snow.Snow_i(GRID.snow.cT_domain_ub)<=0.5.*PARA.technical.SWEperCell && sum(GRID.snow.cT_domain)<2) %remove last grid cell if snow threshold is reached + + % for water balance: add snow of last grid cell to runoff + BALANCE.water.dr_snowmelt = BALANCE.water.dr_snowmelt - ( GRID.snow.Snow_i(GRID.snow.cT_domain_ub) + GRID.snow.Snow_w(GRID.snow.cT_domain_ub) ).*1000; + + %------ modify snow and air grid ---------------------------------- + GRID.snow.cT_domain(GRID.snow.cT_domain_ub)=0; + GRID.snow.K_domain(GRID.snow.cT_domain_ub)=0; + [GRID.snow.cT_domain_lb, GRID.snow.cT_domain_ub] = LayerIndex(GRID.snow.cT_domain); + [GRID.snow.K_domain_lb, GRID.snow.K_domain_ub] = LayerIndex(GRID.snow.K_domain); + + GRID.air.cT_domain(GRID.air.cT_domain_lb+1)=1; + GRID.air.K_domain(GRID.air.K_domain_lb+1)=1; + [GRID.air.cT_domain_lb, GRID.air.cT_domain_ub] = LayerIndex(GRID.air.cT_domain); + [GRID.air.K_domain_lb, GRID.air.K_domain_ub] = LayerIndex(GRID.air.K_domain); + + + GRID.snow.Snow_i(GRID.air.cT_domain_lb)=0; + GRID.snow.Snow_w(GRID.air.cT_domain_lb)=0; + GRID.snow.Snow_a(GRID.air.cT_domain_lb)=0; + T(GRID.air.cT_domain_lb)=0; + + end + + end - - - if check_change==true %update grid spacings - GRID.general.K_grid(GRID.snow.cT_domain)=GRID.general.K_grid(GRID.snow.cT_domain_lb+1) - flipud(cumsum(flipud(GRID.snow.Snow_i(GRID.snow.cT_domain) + GRID.snow.Snow_w(GRID.snow.cT_domain) + GRID.snow.Snow_a(GRID.snow.cT_domain)))); - GRID.general.K_grid(GRID.air.cT_domain)=[GRID.general.K_grid(GRID.air.cT_domain_lb)+(-snowCellSize)*(GRID.air.cT_domain_lb-1):snowCellSize:GRID.general.K_grid(GRID.air.cT_domain_lb)]'; - end - - if (GRID.snow.Snow_i(GRID.snow.cT_domain_ub)<=0.5.*PARA.technical.SWEperCell && sum(GRID.snow.cT_domain)<2) %remove last grid cell if snow threshold is reached - - % for water balance: add snow of last grid cell to runoff - BALANCE.water.dr_snowmelt = BALANCE.water.dr_snowmelt - ( GRID.snow.Snow_i(GRID.snow.cT_domain_ub) + GRID.snow.Snow_w(GRID.snow.cT_domain_ub) ).*1000; - - %------ modify snow and air grid ---------------------------------- - GRID.snow.cT_domain(GRID.snow.cT_domain_ub)=0; - GRID.snow.K_domain(GRID.snow.cT_domain_ub)=0; - [GRID.snow.cT_domain_lb, GRID.snow.cT_domain_ub] = LayerIndex(GRID.snow.cT_domain); - [GRID.snow.K_domain_lb, GRID.snow.K_domain_ub] = LayerIndex(GRID.snow.K_domain); - - GRID.air.cT_domain(GRID.air.cT_domain_lb+1)=1; - GRID.air.K_domain(GRID.air.K_domain_lb+1)=1; - [GRID.air.cT_domain_lb, GRID.air.cT_domain_ub] = LayerIndex(GRID.air.cT_domain); - [GRID.air.K_domain_lb, GRID.air.K_domain_ub] = LayerIndex(GRID.air.K_domain); - - - GRID.snow.Snow_i(GRID.air.cT_domain_lb)=0; - GRID.snow.Snow_w(GRID.air.cT_domain_lb)=0; - GRID.snow.Snow_a(GRID.air.cT_domain_lb)=0; - T(GRID.air.cT_domain_lb)=0; - end + GRID.general.K_grid(GRID.air.cT_domain_lb)= GRID.general.K_grid(GRID.air.cT_domain_lb+1)-snowCellSize; + GRID.general.K_grid(GRID.air.cT_domain_lb-1)= GRID.general.K_grid(GRID.air.cT_domain_lb+1)-2.*snowCellSize; + + GRID.general.cT_grid=( GRID.general.K_grid(1:end-1)+ GRID.general.K_grid(2:end))./2; %grid on which capacity and temperature information lives (midpoints of grid cells) + GRID.general.cT_delta=(- GRID.general.cT_grid(1:end-1,1)+ GRID.general.cT_grid(2:end,1)); + GRID.general.K_delta=(- GRID.general.K_grid(1:end-1,1)+ GRID.general.K_grid(2:end,1)); + + %bugfix as still situations may occur where K_delta<0 ... zzz also if lake level changes !? + if ( sum( GRID.general.K_delta < 0 ) > 0 ) + disp('updateGRID_snow - bugfix K grid'); + %update grid spacings + % snow grid + if ~isempty(GRID.snow.cT_domain_ub) % snow cover + GRID.general.K_grid(GRID.snow.cT_domain) = GRID.general.K_grid(GRID.snow.cT_domain_lb+1) - flipud(cumsum(flipud(GRID.snow.Snow_i(GRID.snow.cT_domain) + GRID.snow.Snow_w(GRID.snow.cT_domain) + GRID.snow.Snow_a(GRID.snow.cT_domain)))); + surfaceTop = GRID.general.K_grid(GRID.snow.cT_domain_ub); + else %lll no snow cover +%tsvd LAKE cases added surfaceTop = GRID.general.K_grid(GRID.soil.cT_domain_ub); + if(~isempty(GRID.lake.ice.cT_domain_ub) ) % lake ice + surfaceTop = GRID.general.K_grid(GRID.ice.water.cT_domain_ub); + elseif(~isempty(GRID.lake.water.cT_domain_ub) ) % open lake + surfaceTop = GRID.general.K_grid(GRID.lake.water.cT_domain_ub); + else + surfaceTop = GRID.general.K_grid(GRID.soil.cT_domain_ub); % soil + end + end + % air grid + numAirCells = sum( GRID.air.cT_domain ); + GRID.general.K_grid(GRID.air.cT_domain) = flip( surfaceTop-snowCellSize:-snowCellSize:surfaceTop-snowCellSize*numAirCells ); + GRID.general.cT_grid = ( GRID.general.K_grid(1:end-1) + GRID.general.K_grid(2:end) ) ./ 2; + GRID.general.cT_delta = ( -GRID.general.cT_grid(1:end-1,1) + GRID.general.cT_grid(2:end,1) ); + GRID.general.K_delta = ( -GRID.general.K_grid(1:end-1,1) + GRID.general.K_grid(2:end,1) ); + end + assert( sum( isnan(T(GRID.snow.cT_domain)))==0, 'updateGRID_snow - error in T after grid update' ); + assert( sum( isnan(GRID.general.K_grid))==0, 'update_GRID_snow - error in Kgrid after grid update'); end - -% update grid spacings -GRID.general.K_grid(GRID.air.cT_domain_lb)= GRID.general.K_grid(GRID.air.cT_domain_lb+1)-snowCellSize; -GRID.general.K_grid(GRID.air.cT_domain_lb-1)= GRID.general.K_grid(GRID.air.cT_domain_lb+1)-2.*snowCellSize; -GRID.general.cT_grid=( GRID.general.K_grid(1:end-1)+ GRID.general.K_grid(2:end))./2; %grid on which capacity and temperature information lives (midpoints of grid cells) -GRID.general.cT_delta=(- GRID.general.cT_grid(1:end-1,1)+ GRID.general.cT_grid(2:end,1)); -GRID.general.K_delta=(- GRID.general.K_grid(1:end-1,1)+ GRID.general.K_grid(2:end,1)); \ No newline at end of file diff --git a/modules/cryoGridSoil/createStratigraphy.m b/modules/cryoGridSoil/createStratigraphy.m index 185f2ce..4ffede1 100644 --- a/modules/cryoGridSoil/createStratigraphy.m +++ b/modules/cryoGridSoil/createStratigraphy.m @@ -18,9 +18,4 @@ GRID.soil.cT_organic = interp1(soilParam(:,1),soilParam(:,4),GRID.general.cT_grid(GRID.soil.cT_domain),'linear'); GRID.soil.cT_soilType = interp1(soilParam(:,1),soilParam(:,5),GRID.general.cT_grid(GRID.soil.cT_domain),'nearest'); GRID.soil.cT_natPor = interp1(soilParam(:,1),soilParam(:,6),GRID.general.cT_grid(GRID.soil.cT_domain),'linear'); - -GRID.soil.K_water = interp1(soilParam(:,1),soilParam(:,2),GRID.general.K_grid(GRID.soil.K_domain),'linear'); -GRID.soil.K_mineral = interp1(soilParam(:,1),soilParam(:,3),GRID.general.K_grid(GRID.soil.K_domain),'linear'); -GRID.soil.K_organic = interp1(soilParam(:,1),soilParam(:,4),GRID.general.K_grid(GRID.soil.K_domain),'linear'); -GRID.soil.K_soilType = interp1(soilParam(:,1),soilParam(:,5),GRID.general.K_grid(GRID.soil.K_domain),'nearest'); - +GRID.soil.cT_actPor = 1. - GRID.soil.cT_mineral - GRID.soil.cT_organic; \ No newline at end of file diff --git a/modules/cryoGridSoil/heatConduction.m b/modules/cryoGridSoil/heatConduction.m index b7546a6..1bb7c07 100644 --- a/modules/cryoGridSoil/heatConduction.m +++ b/modules/cryoGridSoil/heatConduction.m @@ -4,7 +4,6 @@ cT_delta = GRID.general.cT_delta; cT_cellAboveSurface = GRID.air.cT_domain_lb; - dE_dt=T.*0; dE_dt(cT_cellAboveSurface+1)= k_eff(cT_cellAboveSurface+2).*(T(cT_cellAboveSurface+2)-T(cT_cellAboveSurface+1))./cT_delta(cT_cellAboveSurface+1) ; diff --git a/modules/cryoGridSoil/initializeSoilThermalProperties.m b/modules/cryoGridSoil/initializeSoilThermalProperties.m index 19d7f50..fd85e98 100644 --- a/modules/cryoGridSoil/initializeSoilThermalProperties.m +++ b/modules/cryoGridSoil/initializeSoilThermalProperties.m @@ -8,32 +8,42 @@ cT_mineral = GRID.soil.cT_mineral; cT_organic = GRID.soil.cT_organic; cT_soilType = GRID.soil.cT_soilType; -K_water = GRID.soil.K_water; -K_mineral = GRID.soil.K_mineral; -K_organic = GRID.soil.K_organic; -K_soilType = GRID.soil.K_soilType; + arraySize = PARA.technical.arraySizeT; cT_grid = GRID.general.cT_grid(GRID.soil.cT_domain); kh_bedrock = PARA.soil.kh_bedrock; -c_w = PARA.constants.c_w; %4.2*10^6; %[J/m�K] -c_o = PARA.constants.c_o; %2.5*10^6; %[J/m�K] -c_m = PARA.constants.c_m; %2*10^6; %[J/m�K] -c_a = PARA.constants.c_a; %0.00125*10^6;%[J/m�K] -c_i = PARA.constants.c_i; %1.9*10^6;%[J/m�K] +c_w = PARA.constants.c_w; %4.2*10^6; %[J/m???K] +c_o = PARA.constants.c_o; %2.5*10^6; %[J/m???K] +c_m = PARA.constants.c_m; %2*10^6; %[J/m???K] +c_a = PARA.constants.c_a; %0.00125*10^6;%[J/m???K] +c_i = PARA.constants.c_i; %1.9*10^6;%[J/m???K] %density of water -rho_w = PARA.constants.rho_w; %1000; %[kg/m�] +rho_w = PARA.constants.rho_w; %1000; %[kg/m???] %rho_i=900; %latent heat of freezing L_si = PARA.constants.L_sl; %334000; % [J/kg] deltaT=0.001*ones(size(cT_grid,1),1); -% JAN: modification to assume pure water for mixed air/water cells -cT_water(cT_mineral+cT_organic<=1e-6)=1.; +% temporary upscaling of min+org fractions in cells with too low min+org fraction in order to have realistic thermal properties +cT_natPor = GRID.soil.cT_natPor; +cT_actPor = GRID.soil.cT_actPor; + +lowMinOrg_domain = cT_mineral+cT_organic>=1e-6 & ~GRID.soil.excessGroundIce & (cT_actPor > cT_natPor+1e-6 ); % cells with lower soil matrix material than 1-natPor +if sum(lowMinOrg_domain)>0 + disp('initializeSoilThermalProperties - cells with low matrix material exist --> upscaling of matrix fraction to ensure realistic thermal properties'); + cT_matrix = cT_mineral + cT_organic; + cT_mineral(lowMinOrg_domain) = cT_mineral(lowMinOrg_domain) ./ cT_matrix(lowMinOrg_domain) .* (1 - cT_natPor(lowMinOrg_domain)) ; + cT_organic(lowMinOrg_domain) = cT_organic(lowMinOrg_domain) ./ cT_matrix(lowMinOrg_domain) .* (1 - cT_natPor(lowMinOrg_domain)) ; + cT_water(lowMinOrg_domain) = min( cT_water(lowMinOrg_domain), cT_natPor(lowMinOrg_domain) ) ; +end +% temporary upscaling of pure water for mixed air/water cells +freeWater_domain = cT_mineral+cT_organic<1e-6; % cells without soil matrix material +cT_water(freeWater_domain)=1.; %------- capacity part ---------------------------------------------------- waterMin=0; @@ -42,25 +52,19 @@ organic=cT_organic; a=cT_soilType; - +% set cT_thawed cT_thawed=zeros(size(a,1),1); -%cT_frozen=-15*ones(size(a,1),1); - +% determine cT_frozen ch=mineral*c_m+organic*c_o+waterMin.*c_w+(water-waterMin)*c_i; - -% JAN: Why the additional loop from -30 to -1??? --> determine cT_frozen %preallocate variable c_h2o=ones(length(a),length(-30:0.01:-1)); - j=1; for i= -30:0.01:-1 c_h2o(:,j)=L_si*rho_w*(freezeC(water, 1-mineral-organic, a, i+deltaT/2, PARA)-freezeC(water, 1-mineral-organic, a, i-deltaT/2, PARA))/deltaT(1,1) < 0.05.*ch; j=j+1; end - %preallocate variables cT_frozen=-30+((sum(c_h2o')')-1).*0.01; - c_h2o=ones(length(a),length(1:arraySize-2)+1); water_c=c_h2o; ch=c_h2o; @@ -71,7 +75,8 @@ c_h2o(:,1) = L_si*rho_w* (freezeC(water, 1-mineral-organic, a, cT_frozen+deltaT/2, PARA)-freezeC(water, 1-mineral-organic, a, cT_frozen-deltaT/2, PARA))/deltaT(1,1); for i=1:arraySize-2 water_c(:,i+1) = freezeC(water, 1-mineral-organic, a, cT_thawed+(cT_frozen-cT_thawed)*(arraySize-2-i)/(arraySize-2), PARA); - ch(:,i+1) = mineral*c_m+organic*c_o+freezeC(water, 1-mineral-organic, a, cT_thawed+(cT_frozen-cT_thawed)*(arraySize-2-i)/(arraySize-2), PARA)*(c_w-c_i)+water*c_i; + ch(:,i+1) = mineral*c_m+organic*c_o+water_c(:,i+1)*(c_w-c_i)+water*c_i; + %ch(:,i+1) = mineral*c_m+organic*c_o+freezeC(water, 1-mineral-organic, a, cT_thawed+(cT_frozen-cT_thawed)*(arraySize-2-i)/(arraySize-2), PARA)*(c_w-c_i)+water*c_i; c_h2o(:,i+1) = L_si*rho_w*(freezeC(water, 1-mineral-organic, a, cT_thawed+(cT_frozen-cT_thawed)*(arraySize-2-i)/(arraySize-2)+deltaT/2, PARA)-freezeC(water, 1-mineral-organic, a, cT_thawed+(cT_frozen-cT_thawed)*(arraySize-2-i)/(arraySize-2)-deltaT/2, PARA))/deltaT(1,1); end @@ -81,55 +86,36 @@ liquidWaterContent = [water_c water]; % water content - -% lastSnowCell=find(cT_grid(:,1)<0); -% lastSnowCell=lastSnowCell(end); -% -% capacity(1:lastSnowCell,:) = repmat(rho_snow/rho_i*c_i, lastSnowCell, size(capacity,2)); - - %---------- conductivity part --------------------------------------------- -% water=K_water; -% mineral=K_mineral; -% organic=K_organic; -% a=K_soilType; -%%K_frozen=-15*ones(size(a,1),1); -%K_frozen=[cT_frozen(1,1); 0.5.*(cT_frozen(1:end-1,1)+cT_frozen(2:end,1)) ; cT_frozen(end,1)]; -%K_thawed=zeros(size(a,1),1); - % changed to cT-grid since K- interpolation is done external now -water=cT_water; -mineral=cT_mineral; -organic=cT_organic; -a=cT_soilType; +% water=cT_water; +% mineral=cT_mineral; +% organic=cT_organic; +% a=cT_soilType; +% -K_frozen=cT_frozen; -K_thawed=cT_thawed; +% +% %preallocate variables +% water_c2=ones(length(a),length(1:arraySize-2)+1); +% water_c2(:,1)=freezeC(water, 1-mineral-organic, a, K_frozen, PARA); % JAN: this is identical to water_c for -%preallocate variables -water_c=ones(length(a),length(1:arraySize-2)+1); -water_c(:,1)=freezeC(water, 1-mineral-organic, a, K_frozen, PARA); -conductivity=water_c; +conductivity=water_c; % initialize to same size as water_c + +% for i=1:arraySize-2 +% water_c2(:,i+1)=freezeC(water, 1-mineral-organic, a, K_thawed+(K_frozen-K_thawed)*(arraySize-2-i)/(arraySize-2), PARA); +% end +% +% assert( isequal(water_c, water_c2), 'initializeSoilThermalProperties - water_c not equal for cap and cond calculations'); -for i=1:arraySize-2 - water_c(:,i+1)=freezeC(water, 1-mineral-organic, a, K_thawed+(K_frozen-K_thawed)*(arraySize-2-i)/(arraySize-2), PARA); -end for i=1:size(a,1) ice_c=water(i,1)*ones(1,arraySize-1)-water_c(i,:); - %plot(water_c(i,:)') conductivity(i,:)=conductivity2(water_c(i,:), ice_c, mineral(i,1), organic(i,1), PARA); - %plot(heatcond(water_c(i,:)', ice_c, mineral(i,1), organic(i,1), 0, 0.15, 2)) end conductivity=[conductivity conductivity(:,size(conductivity,2))]; %conductivity matrix for soil filled -% lastSnowCell=find((:,1)<0); -% lastSnowCell=lastSnowCell(end); -% conductivity(1:lastSnowCell,:) = repmat(kh_snow, lastSnowCell, size(conductivity,2)); - - %----------- write lookup tables to GRID struct liquidWaterContent = real(liquidWaterContent); @@ -139,6 +125,8 @@ GRID.soil.cT_frozen = cT_frozen; GRID.soil.cT_thawed = cT_thawed; +K_frozen=cT_frozen; +K_thawed=cT_thawed; GRID.soil.K_frozen = K_frozen; GRID.soil.K_thawed = K_thawed; GRID.soil.conductivity = conductivity; @@ -152,61 +140,31 @@ %content is 'water' by default function waterC = freezeC(thetaTot, thetaSat, soilType, T, PARA) T=T+273.15; - % thetaTot=0.3; - % thetaSat=0.4; + thetaTot=min(thetaSat, thetaTot); thetaRes=zeros(size(soilType)); alpha=zeros(size(soilType)); n=zeros(size(soilType)); + %set conditions for soil types + thetaRes(soilType==1) = PARA.soil.soilTypes(1,1); + alpha(soilType==1) = PARA.soil.soilTypes(1,3); + n(soilType==1) = PARA.soil.soilTypes(1,4); - %set conditions for soil rypes - thetaRes(soilType==1) = 0; - alpha(soilType==1) = 4; - n(soilType==1) = 2; - - thetaRes(soilType==2) = 0.05; - alpha(soilType==2) = 0.65; - n(soilType==2) = 1.7; - - - % thetaTot=0.2; - % thetaSat=0.5; - % thetaRes=0.05; - % alpha=0.15; %units meter - % n=1.25; + thetaRes(soilType==2) = PARA.soil.soilTypes(2,1); + alpha(soilType==2) = PARA.soil.soilTypes(2,3); + n(soilType==2) = PARA.soil.soilTypes(2,4); + m=1-1./n; waterPotZero=-1./alpha .*( ((thetaTot-thetaRes)./(thetaSat-thetaRes) ).^(-1./m) -1 ).^(1./n); - %Tstar=273.15+9.81.*273.15./3.34e5.*waterPotZero; Tstar = 273.15 + PARA.constants.g .* 273.15 ./ PARA.constants.L_sl .* waterPotZero; waterC=zeros(size(T)); - % for i=1:size(soilType,1) - % if T(i)>=273.15 - % waterC(i,1)=thetaTot(i,1); - % else - % - % if T(i,1)>273.1 - % - % - % waterPot=waterPotZero(i,1)+(3.34e5./9.81./Tstar(i,1).*(273.1-Tstar(i,1))).*(273.1=273.15) = thetaTot(T>=273.15); - % OLD implementation ( plateau from 0 to -0.05 ) + % implementation ( linear from 0 to -0.05 ) waterPot(T<273.15 & T>273.1) = waterPotZero(T<273.15 & T>273.1)... + (3.34e5./9.81./Tstar(T<273.15 & T>273.1).*(273.1-Tstar(T<273.15 & T>273.1)))... .* (273.1273.1)); @@ -220,10 +178,14 @@ waterPot(T<=273.1)= waterPotZero(T<=273.1)+(3.34e5./9.81./Tstar(T<=273.1).*(T(T<=273.1)-Tstar(T<=273.1))).*(T(T<=273.1)0; % Condition to work on infiltration + T=T(GRID.soil.cT_domain); + i=1; + i_max=200; %jjj + while T(i)>0 && i<=i_max + i=i+1; + end + ald_cT_index=i-1; + ald_altitude = PARA.location.initial_altitude-GRID.general.K_grid(GRID.soil.cT_domain_ub+ald_cT_index); + end + + +end diff --git a/modules/cryoGridTechnical/getAltitude.m b/modules/cryoGridTechnical/getAltitude.m new file mode 100644 index 0000000..376cf53 --- /dev/null +++ b/modules/cryoGridTechnical/getAltitude.m @@ -0,0 +1,13 @@ +function altitude = getAltitude( PARA, GRID ) + %zzz same as get soil altitude! used? +%tsvd account for lake surface (snow can accumulate on lake ice) +if ~isempty( GRID.lake.water.cT_domain_ub )% lake water is present + altitude = PARA.location.initial_altitude - GRID.general.K_grid(GRID.lake.water.cT_domain_ub); +elseif ~isempty( GRID.lake.ice.cT_domain_ub ) % lake ice is present + altitude = PARA.location.initial_altitude - GRID.general.K_grid(GRID.lake.ice.cT_domain_ub); + +else + altitude = PARA.location.initial_altitude - GRID.general.K_grid(GRID.soil.cT_domain_ub); % no lake +end + +end \ No newline at end of file diff --git a/modules/cryoGridTechnical/getSoilAltitude.m b/modules/cryoGridTechnical/getSoilAltitude.m new file mode 100644 index 0000000..93e6f9b --- /dev/null +++ b/modules/cryoGridTechnical/getSoilAltitude.m @@ -0,0 +1,8 @@ +function soil_altitude = getSoilAltitude(PARA, GRID) + +%tsvd GRID.lake.cT_domain replaced by GRID.lake.water.cT_domain +if ~isempty( GRID.lake.water.cT_domain_ub ) %zzz rm what is soil_altitude needed for...? check.... + soil_altitude = PARA.location.initial_altitude - GRID.general.K_grid(GRID.lake.water.cT_domain_lb+1); +else + soil_altitude = PARA.location.initial_altitude - GRID.general.K_grid(GRID.soil.cT_domain_ub); +end \ No newline at end of file diff --git a/modules/cryoGridTechnical/getSurfaceAltitude.m b/modules/cryoGridTechnical/getSurfaceAltitude.m new file mode 100644 index 0000000..4f5956b --- /dev/null +++ b/modules/cryoGridTechnical/getSurfaceAltitude.m @@ -0,0 +1,10 @@ +function surface_altitude = getSurfaceAltitude(PARA, GRID) + +surface_altitude = PARA.location.initial_altitude - GRID.general.K_grid(GRID.air.cT_domain_lb+1); + +%if ~isempty(GRID.snow.cT_domain_ub) +% surface_altitude = PARA.location.altitude + sum( GRID.general.K_delta(GRID.snow.cT_domain) ); +%else +% surface_altitude = PARA.location.altitude; +%end + diff --git a/modules/cryoGridTechnical/getWaterTableAltitude.m b/modules/cryoGridTechnical/getWaterTableAltitude.m new file mode 100644 index 0000000..906223f --- /dev/null +++ b/modules/cryoGridTechnical/getWaterTableAltitude.m @@ -0,0 +1,25 @@ +function waterTable = getWaterTableAltitude(T, wc, GRID, PARA) +%jjj zzz if lake -> watertable = lake surface + + T=T(GRID.soil.cT_domain); + K_delta=GRID.general.K_delta(GRID.soil.cT_domain); %in m + + if ~isempty(GRID.snow.cT_domain_ub) || T(1)<=0 + waterTable=NaN; + else + waterTable = PARA.location.altitude; % zzz ???! + water = wc; + porosity=(1. - GRID.soil.cT_mineral - GRID.soil.cT_organic); + i=1; + while water(i)0 + waterTable = waterTable - K_delta(i); + i=i+1; + end + + if T(i)<=0 + waterTable=NaN; + %disp('Dry to permafrost - waterTable=NaN \n')% Loop was stopped by the temperature condition so no water table + end + end + +end diff --git a/modules/cryoGridTechnical/iSaveOUT.m b/modules/cryoGridTechnical/iSaveOUT.m new file mode 100644 index 0000000..85603c8 --- /dev/null +++ b/modules/cryoGridTechnical/iSaveOUT.m @@ -0,0 +1,3 @@ +function iSaveOUT( fname, OUT ) + save( fname, 'OUT' ); +end \ No newline at end of file diff --git a/modules/cryoGridTechnical/iSaveSettings.m b/modules/cryoGridTechnical/iSaveSettings.m new file mode 100644 index 0000000..8a3bcdd --- /dev/null +++ b/modules/cryoGridTechnical/iSaveSettings.m @@ -0,0 +1,3 @@ +function iSaveSettings( fname, FORCING, PARA, GRID) + save( fname, 'FORCING', 'PARA', 'GRID' ); +end \ No newline at end of file diff --git a/modules/cryoGridTechnical/iSaveState.m b/modules/cryoGridTechnical/iSaveState.m new file mode 100644 index 0000000..7554638 --- /dev/null +++ b/modules/cryoGridTechnical/iSaveState.m @@ -0,0 +1,3 @@ +function iSaveState( fname, T, wc, t, SEB, PARA, GRID) + save( fname, 'T', 'wc', 't', 'SEB', 'PARA', 'GRID' ); +end \ No newline at end of file diff --git a/modules/cryoGridTechnical/loadConstants.m b/modules/cryoGridTechnical/loadConstants.m index 276fc90..98189ab 100644 --- a/modules/cryoGridTechnical/loadConstants.m +++ b/modules/cryoGridTechnical/loadConstants.m @@ -1,5 +1,5 @@ function PARA = loadConstants( PARA ) - % important natural constants, given in SI units [m, kg, s, J, K] + % important natural constants, given in SI units PARA.constants.kappa = 0.4; % von Kármán constant [-] PARA.constants.sigma = 5.6704e-8; % Stefan-Boltzmann constant [ W / (m^2 K^4) ] PARA.constants.g = 9.81; % gravitational acceleration [m/s^2] @@ -12,7 +12,7 @@ PARA.constants.rho_i = 1000; % density of ice, assumed to be equal to that of water [kg/m^3] PARA.constants.c_i = 1900 * PARA.constants.rho_i; % volumetric heat capacity of ice [ J / (m^3 K) ] PARA.constants.k_i = 2.2; % heat conductivity of ice [ W/(mK) ] [Hillel(1982)] - %latent heat of water phase changes + %latent heat of water PARA.constants.T_f = 273.15; % freezing point of water / zero degree Celsius [K] PARA.constants.L_sl = 334e3; % specific latent heat of fusion of water [J/kg] [AMS] PARA.constants.L_lg = 2501e3; % specific latent heat of vaporization of water [J/kg] [AMS] @@ -22,10 +22,12 @@ PARA.constants.c_a = 1005.7 * PARA.constants.rho_a; %c_a= 0.00125*10^6;%[J/m^3 K] % volumetric heat capacity of dry air [J/(m^3 K)] @ 0°C, sea level, isobar PARA.constants.k_a = 0.0243; %ka=0.025; [Hillel(1982)] % heat conductivity of air [ W/(mK)] @ 0 °C, sea level pressure PARA.constants.R_a = 287.058; % specific gas constant of air [ J/(kg K) ] - %organic + % organic + %PARA.constants.rho_o = 1; % n.a. PARA.constants.c_o = 2.5e6; %[J/(K m^3)] % volumetric heat capacity of organic material [J/(K m^3)] PARA.constants.k_o = 0.25; % heat conductivity of organic material [ W/(mK) ] [Hillel(1982)] - %mineral - PARA.constants.c_m = 2.0e6; %[J/(K m^3)] % volumetric heat capacity of minearal material [J/(K m^3)] - PARA.constants.k_m = PARA.soil.kh_bedrock; % heat conductivity of mineral material / bedrock [ W/(mK)] (specified above) %km=3.8 %mineral [Hillel(1982)] -end \ No newline at end of file + % mineral + %PARA.constants.rho_m = 1; % n.a. + PARA.constants.c_m = 2.0e6; %[J/(K m^3)] % volumetric heat capacity of minearal material [J/(K m^3)] + PARA.constants.k_m = PARA.soil.kh_bedrock; % heat conductivity of mineral material / bedrock [ W/(mK)] (specified above) %km=3.8 %mineral [Hillel(1982)] tsvd: km=3.0! +end diff --git a/modules/cryoGridTechnical/loadSoilTypes.m b/modules/cryoGridTechnical/loadSoilTypes.m new file mode 100644 index 0000000..6a5e4f6 --- /dev/null +++ b/modules/cryoGridTechnical/loadSoilTypes.m @@ -0,0 +1,6 @@ +function [PARA] = loadSoilTypes( PARA ) + + % specify one soil type per row: residualWC [%], fieldCapacity [%], alpha [1/m], n + PARA.soil.soilTypes = [ [ 0.00, PARA.soil.fieldCapacity, 4.00, 2.0 ]; ... % sand + [ 0.05, PARA.soil.fieldCapacity, 0.65, 1.7 ]; ... % silt + [ 0.00, 0.00 , 4.00, 2.0 ] ]; % pond (freeze curve of sand but fieldCapacity = 0) \ No newline at end of file diff --git a/modules/cryoGridTechnical/makeGrids.m b/modules/cryoGridTechnical/makeGrids.m index 2ee1150..843f31e 100644 --- a/modules/cryoGridTechnical/makeGrids.m +++ b/modules/cryoGridTechnical/makeGrids.m @@ -1,11 +1,20 @@ function GRID=makeGrids(PARA) - -GRID.snow.snowCellSize=PARA.technical.SWEperCell/(PARA.snow.rho_snow/PARA.constants.rho_w); +GRID.snow.snowCellSize=PARA.technical.SWEperCell/(PARA.snow.rho_snow/PARA.constants.rho_w); % only used to calculate length initial air grid... GRID.snow.snowGrid=[-1.*(PARA.technical.maxSWE./(PARA.technical.SWEperCell)+2).*GRID.snow.snowCellSize:GRID.snow.snowCellSize:-GRID.snow.snowCellSize]'; -GRID.soil.soilGrid=PARA.technical.subsurfaceGrid; +%tsvd LAKE grid +%GRID.lake.water.waterGrid=[0:0.02:PARA.water.depth-0.02]'; %with water (it is recommended to use a grid resolution of about 2cm) +GRID.lake.water.waterGrid=[0:0.04:PARA.water.depth-0.04]'; %with water (it is recommended to use a grid resolution of about 2cm) + +%GRID.lake.water.waterGrid=[]'; %no water +if ~isempty(GRID.lake.water.waterGrid) + GRID.soil.soilGrid=PARA.technical.subsurfaceGrid + (2*GRID.lake.water.waterGrid(end)-GRID.lake.water.waterGrid(end-1)); +else + GRID.soil.soilGrid=PARA.technical.subsurfaceGrid; +end -K_grid =[GRID.snow.snowGrid; GRID.soil.soilGrid]; %grid on which the conductivty information lives (edges of grid cells) +%tsvd K_grid =[GRID.snow.snowGrid; GRID.soil.soilGrid]; %grid on which the conductivty information lives (edges of grid cells) +K_grid =[GRID.snow.snowGrid; GRID.lake.water.waterGrid; GRID.soil.soilGrid]; %grid on which the conductivty information lives (edges of grid cells) cT_grid=(K_grid(1:end-1)+K_grid(2:end))/2; %grid on which heat capacity and temperature information lives (midpoints of grid cells) cT_delta=(-cT_grid(1:end-1,1)+cT_grid(2:end,1)); K_delta=(-K_grid(1:end-1,1)+K_grid(2:end,1)); @@ -25,7 +34,8 @@ %set soil grid GRID.soil.cT_domain= false(size(GRID.general.cT_grid)); -GRID.soil.cT_domain(GRID.general.cT_grid>0)=1; +%tsvd GRID.soil.cT_domain(GRID.general.cT_grid>0)=1; +GRID.soil.cT_domain(end-length(GRID.soil.soilGrid)+2:end)=1; %required if there is a lake on top [GRID.soil.cT_domain_lb, GRID.soil.cT_domain_ub] = LayerIndex(GRID.soil.cT_domain); GRID.soil.K_domain= false(size(GRID.general.K_grid)); GRID.soil.K_domain(GRID.soil.cT_domain_ub:end)=1; @@ -43,20 +53,38 @@ % different and mixing occurs in summer. Initially, the lake domain is % empty, but it is updated in the first time step. -%set water and ice cover grid -GRID.lake.cT_domain = false(size(GRID.general.cT_grid)); -%GRID.lake.cT_domain(GRID.air.cT_domain_lb+1:GRID.soil.cT_domain_ub-1)=1; -GRID.lake.K_domain= false(size(GRID.general.K_grid)); -%GRID.lake.K_domain(GRID.air.K_domain_lb+1:GRID.soil.K_domain_ub-1)=1; -[GRID.lake.cT_domain_lb, GRID.lake.cT_domain_ub] = LayerIndex(GRID.lake.cT_domain); -[GRID.lake.K_domain_lb, GRID.lake.K_domain_ub] = LayerIndex(GRID.lake.K_domain); +%tsvd replaced by Flake conditions below - zzz Jan, if you update your model version with using GRID.lake.water.* instead of GRID.lake.*, our handling of GRID.lake is consistent +% %set water and ice cover grid +% GRID.lake.cT_domain = false(size(GRID.general.cT_grid)); +% %GRID.lake.cT_domain(GRID.air.cT_domain_lb+1:GRID.soil.cT_domain_ub-1)=1; +% GRID.lake.K_domain= false(size(GRID.general.K_grid)); +% %GRID.lake.K_domain(GRID.air.K_domain_lb+1:GRID.soil.K_domain_ub-1)=1; +% [GRID.lake.cT_domain_lb, GRID.lake.cT_domain_ub] = LayerIndex(GRID.lake.cT_domain); +% [GRID.lake.K_domain_lb, GRID.lake.K_domain_ub] = LayerIndex(GRID.lake.K_domain); +% +% GRID.lake.water.cT_domain= false(size(GRID.general.cT_grid)); +% GRID.lake.water.K_domain= false(size(GRID.general.K_grid)); +% [GRID.lake.water.cT_domain_lb, GRID.lake.water.cT_domain_ub] = LayerIndex(GRID.lake.water.cT_domain); +% [GRID.lake.water.K_domain_lb, GRID.lake.water.K_domain_ub] = LayerIndex(GRID.lake.water.K_domain); +% +% GRID.lake.ice.cT_domain=false(size(GRID.general.cT_grid)); +% GRID.lake.ice.K_domain=false(size(GRID.general.K_grid)); +% [GRID.lake.ice.cT_domain_lb, GRID.lake.ice.cT_domain_ub] = LayerIndex(GRID.lake.ice.cT_domain); +% [GRID.lake.ice.K_domain_lb, GRID.lake.ice.K_domain_ub] = LayerIndex(GRID.lake.ice.K_domain); +%tsvd +%set water and ice cover grid GRID.lake.water.cT_domain= false(size(GRID.general.cT_grid)); -GRID.lake.water.K_domain= false(size(GRID.general.K_grid)); -[GRID.lake.water.cT_domain_lb, GRID.lake.water.cT_domain_ub] = LayerIndex(GRID.lake.water.cT_domain); -[GRID.lake.water.K_domain_lb, GRID.lake.water.K_domain_ub] = LayerIndex(GRID.lake.water.K_domain); - -GRID.lake.ice.cT_domain=false(size(GRID.general.cT_grid)); -GRID.lake.ice.K_domain=false(size(GRID.general.K_grid)); -[GRID.lake.ice.cT_domain_lb, GRID.lake.ice.cT_domain_ub] = LayerIndex(GRID.lake.ice.cT_domain); -[GRID.lake.ice.K_domain_lb, GRID.lake.ice.K_domain_ub] = LayerIndex(GRID.lake.ice.K_domain); \ No newline at end of file +GRID.lake.water.cT_domain(GRID.air.cT_domain_lb+1:GRID.soil.cT_domain_ub-1)=1; +GRID.lake.water.K_domain= false(size(GRID.general.cT_grid)); +GRID.lake.water.K_domain(GRID.air.K_domain_lb+1:GRID.soil.K_domain_ub-1)=1; +[GRID.lake.water.cT_domain_lb GRID.lake.water.cT_domain_ub] = LayerIndex(GRID.lake.water.cT_domain); +[GRID.lake.water.K_domain_lb GRID.lake.water.K_domain_ub] = LayerIndex(GRID.lake.water.K_domain); +GRID.lake.ice.cT_domain=logical(GRID.air.cT_domain.*0); +GRID.lake.ice.K_domain=logical(GRID.air.K_domain.*0); +[GRID.lake.ice.cT_domain_lb GRID.lake.ice.cT_domain_ub] = LayerIndex(GRID.lake.ice.cT_domain); +[GRID.lake.ice.K_domain_lb GRID.lake.ice.K_domain_ub] = LayerIndex(GRID.lake.ice.K_domain); +GRID.lake.ice.z_ice = 0; +GRID.lake.ice.dz_dt_ice = 0; +GRID.lake.ice.dE_dt_cond_residual=0; +GRID.lake.ice.melt_flag=false; \ No newline at end of file diff --git a/modules/cryoGridTechnical/sum_up_output_store.m b/modules/cryoGridTechnical/sum_up_output_store.m index 26a3b2a..c2695df 100644 --- a/modules/cryoGridTechnical/sum_up_output_store.m +++ b/modules/cryoGridTechnical/sum_up_output_store.m @@ -1,9 +1,16 @@ +function [TEMPORARY, OUT, BALANCE] = sum_up_output_store(t, T, wc, lwc, timestep, TEMPORARY, BALANCE, PARA, GRID, SEB, OUT, saveDir, run_number, water_fluxes, snow_fluxes, heat_fluxes) + +%tsvd GRID.lake.cT_domain replaced by GRID.lake.water.cT_domain + TEMPORARY.timestep_sum=TEMPORARY.timestep_sum+(timestep*24*3600)*timestep; TEMPORARY.T_sum=TEMPORARY.T_sum+T.*timestep; TEMPORARY.Qe_sum=TEMPORARY.Qe_sum+SEB.Qe.*timestep; TEMPORARY.Qh_sum=TEMPORARY.Qh_sum+SEB.Qh.*timestep; TEMPORARY.Qnet_sum=TEMPORARY.Qnet_sum+SEB.Qnet.*timestep; TEMPORARY.Qg_sum=TEMPORARY.Qg_sum+SEB.Qg.*timestep; + TEMPORARY.Qsurf_sum = TEMPORARY.Qsurf_sum + SEB.Qsurf * timestep; + TEMPORARY.dE_dt_SEB_sum = TEMPORARY.dE_dt_SEB_sum + SEB.dE_dt_SEB * timestep; + TEMPORARY.dE_dt_cond_sum = TEMPORARY.dE_dt_cond_sum + SEB.dE_dt_cond * timestep; %----store in output table -------------------------------------------- if t==TEMPORARY.outputTime @@ -22,6 +29,11 @@ TEMPORARY.Qnet=TEMPORARY.Qnet_sum./TEMPORARY.dt_out; TEMPORARY.Qg=TEMPORARY.Qg_sum./TEMPORARY.dt_out; + TEMPORARY.Qsurf = TEMPORARY.Qsurf_sum ./ TEMPORARY.dt_out; + TEMPORARY.dE_dt_SEB=TEMPORARY.dE_dt_SEB_sum./TEMPORARY.dt_out; + TEMPORARY.dE_dt_cond=TEMPORARY.dE_dt_cond_sum./TEMPORARY.dt_out; + + TEMPORARY.timestep_out=TEMPORARY.timestep_sum./TEMPORARY.dt_out; %reset sum variables @@ -30,22 +42,23 @@ TEMPORARY.Qh_sum=0; TEMPORARY.Qe_sum=0; TEMPORARY.Qnet_sum=0; - TEMPORARY.Qg_sum=0; + TEMPORARY.Qg_sum=0; + TEMPORARY.Qsurf_sum = 0; + TEMPORARY.dE_dt_SEB_sum=0; + TEMPORARY.dE_dt_cond_sum=0; TEMPORARY.timestep_sum=0; - %store new values in OUT struct ----------------------------------- + %------ store new values in OUT struct ----------------------------------- + + % key state variables OUT.cryoGrid3=[OUT.cryoGrid3 [NaN(GRID.air.cT_domain_lb,1); TEMPORARY.T_out(GRID.air.cT_domain_lb+1:end,1)]]; OUT.water=[OUT.water [ NaN( GRID.soil.cT_domain_ub-1,1) ; wc ] ]; OUT.liquidWater=[OUT.liquidWater [ NaN( GRID.soil.cT_domain_ub-1,1) ; lwc ] ]; OUT.TIMESTEP=[OUT.TIMESTEP; TEMPORARY.timestep_out]; OUT.timestamp=[OUT.timestamp; t]; - OUT.snow.outSnow_i=[OUT.snow.outSnow_i GRID.snow.Snow_i]; - OUT.snow.outSnow_a=[OUT.snow.outSnow_a GRID.snow.Snow_a]; - OUT.snow.outSnow_w=[OUT.snow.outSnow_w GRID.snow.Snow_w]; - - % surface energy balance + % realated to surface energy balance OUT.SEB.Lsta=[OUT.SEB.Lsta; mean(SEB.L_star)]; OUT.SEB.QE=[OUT.SEB.QE; TEMPORARY.Qe]; OUT.SEB.QH=[OUT.SEB.QH; TEMPORARY.Qh]; @@ -53,27 +66,50 @@ OUT.SEB.QNET=[OUT.SEB.QNET; TEMPORARY.Qnet]; OUT.SEB.Tsurf=[OUT.SEB.Tsurf; TEMPORARY.T_out(GRID.air.cT_domain_lb+1)]; OUT.SEB.albedo_stored=[OUT.SEB.albedo_stored; PARA.surf.albedo]; - - % complete energy balance (EB) - OUT.EB.Qg = [OUT.EB.Qg; TEMPORARY.Qg]; % ground heat flux (positive into ground) - OUT.EB.Qe = [OUT.EB.Qe; TEMPORARY.Qe]; % latent heat flux (positive into ground) - OUT.EB.Qh = [OUT.EB.Qh; TEMPORARY.Qh]; % sensible heat flux (positive into ground) - OUT.EB.Qnet = [OUT.EB.Qnet; TEMPORARY.Qnet]; - OUT.EB.Qgeo = [OUT.EB.Qgeo; PARA.soil.Qgeo]; % geothermal heat flux - OUT.EB.dE_soil_sens = [OUT.EB.dE_soil_sens; BALANCE.energy.dE_soil_sens ]; - BALANCE.energy.dE_soil_sens = 0; - OUT.EB.dE_soil_lat = [OUT.EB.dE_soil_lat; BALANCE.energy.dE_soil_lat ]; - BALANCE.energy.dE_soil_lat = 0; - OUT.EB.dE_soil = [OUT.EB.dE_soil; BALANCE.energy.dE_soil ]; - BALANCE.energy.dE_soil = 0; - OUT.EB.dE_snow_sens = [ OUT.EB.dE_snow_sens; BALANCE.energy.dE_snow_sens ]; - BALANCE.energy.dE_snow_sens = 0; - OUT.EB.dE_snow_lat = [ OUT.EB.dE_snow_lat; BALANCE.energy.dE_snow_lat ]; - BALANCE.energy.dE_snow_lat = 0; - OUT.EB.dE_snow = [ OUT.EB.dE_snow; BALANCE.energy.dE_snow ]; - BALANCE.energy.dE_snow = 0; - - % water balance (WB) + + + % related to soil + OUT.soil.topPosition=[OUT.soil.topPosition; -GRID.general.K_grid(GRID.soil.cT_domain_ub)]; + if ~isempty( GRID.lake.water.cT_domain_ub ) + OUT.soil.lakeFloor = [OUT.soil.lakeFloor; - GRID.general.K_grid(GRID.lake.water.cT_domain_lb+1) ]; + else + OUT.soil.lakeFloor = [OUT.soil.lakeFloor; NaN]; + end + OUT.soil.soil{1, size(OUT.soil.soil,2)+1}=[GRID.soil.cT_water GRID.soil.cT_mineral GRID.soil.cT_organic]; + + + % related to snow + OUT.snow.outSnow_i=[OUT.snow.outSnow_i GRID.snow.Snow_i]; + OUT.snow.outSnow_a=[OUT.snow.outSnow_a GRID.snow.Snow_a]; + OUT.snow.outSnow_w=[OUT.snow.outSnow_w GRID.snow.Snow_w]; + if ~isempty(GRID.snow.cT_domain_ub) + TEMPORARY.topPosition=-GRID.general.K_grid(GRID.snow.cT_domain_ub); + TEMPORARY.botPosition=-GRID.general.K_grid(GRID.snow.cT_domain_lb+1); + else + TEMPORARY.topPosition=NaN; + TEMPORARY.botPosition=NaN; + end + OUT.snow.topPosition=[OUT.snow.topPosition; TEMPORARY.topPosition]; + OUT.snow.botPosition=[OUT.snow.botPosition; TEMPORARY.botPosition]; + + + + % derived characteristics and related to geometry + OUT.location.area = [OUT.location.area; PARA.location.area]; + OUT.location.altitude=[OUT.location.altitude; PARA.location.altitude]; + OUT.location.soil_altitude= [OUT.location.soil_altitude; PARA.location.soil_altitude]; + OUT.location.surface_altitude=[OUT.location.surface_altitude; PARA.location.surface_altitude]; + OUT.location.active_layer_depth_altitude = [OUT.location.active_layer_depth_altitude; PARA.location.active_layer_depth_altitude]; + OUT.location.water_table_altitude=[OUT.location.water_table_altitude; PARA.location.water_table_altitude]; + + % lateral fluxes + OUT.lateral.terrain_index_snow=[ OUT.lateral.terrain_index_snow; PARA.ensemble.terrain_index_snow ]; +%tsvd OUT.lateral.terrain_index_snow=[ OUT.lateral.terrain_index_snow ]; % exclude ensemble struct for lateral=0 zzz define switch Abfrage... lateral 0/1? + OUT.lateral.water_fluxes = [ OUT.lateral.water_fluxes; water_fluxes' ]; % vector containing water fluxes in [m/s] to the current worker + OUT.lateral.snow_fluxes = [ OUT.lateral.snow_fluxes; snow_fluxes' ]; % vector containing snow fluxes in [m SWE / s] to the current worker + OUT.lateral.heat_fluxes = [ OUT.lateral.heat_fluxes; heat_fluxes' ]; % vector containing depth-integrated heat fluxes in [J/m^2 s ] to the current worker + + % water balance (WB) % all flows are defined as positive when they go into the soil/snow column % cumulative values per output interval in [mm] % storage @@ -86,12 +122,17 @@ OUT.WB.de = [ OUT.WB.de; BALANCE.water.de ]; OUT.WB.ds = [ OUT.WB.ds; BALANCE.water.ds ]; % runoff - OUT.WB.dr_surface=[ OUT.WB.dr_surface; BALANCE.water.dr_surface ]; - OUT.WB.dr_subsurface = [ OUT.WB.dr_subsurface; BALANCE.water.dr_subsurface ]; + OUT.WB.dr_surface= [ OUT.WB.dr_surface; BALANCE.water.dr_surface ]; + OUT.WB.dr_external = [ OUT.WB.dr_external; BALANCE.water.dr_external ]; OUT.WB.dr_snowmelt = [ OUT.WB.dr_snowmelt; BALANCE.water.dr_snowmelt ]; OUT.WB.dr_excessSnow=[ OUT.WB.dr_excessSnow; BALANCE.water.dr_excessSnow ]; - OUT.WB.dr_rain = [ OUT.WB.dr_rain; BALANCE.water.dr_rain ]; % this is only rain on frozen ground + OUT.WB.dr_lateralSnow=[ OUT.WB.dr_lateralSnow; BALANCE.water.dr_lateralSnow ]; % lateral snow flux to other realizations + OUT.WB.dr_rain = [ OUT.WB.dr_rain; BALANCE.water.dr_rain ]; % this is only rain on frozen ground + OUT.WB.dr_lateral = [OUT.WB.dr_lateral; BALANCE.water.dr_lateral ]; % lateral water flux to other realizations + % mismatch + OUT.WB.dm_lacking = [OUT.WB.dm_lacking; BALANCE.water.dm_lacking ]; % set accumulated fluxes in BALANCE.water struct to zero + % storage BALANCE.water.dW_soil = 0; BALANCE.water.dW_snow = 0; % precipitation @@ -102,42 +143,54 @@ BALANCE.water.ds=0; % runoff BALANCE.water.dr_surface=0; - BALANCE.water.dr_subsurface=0; + BALANCE.water.dr_external=0; BALANCE.water.dr_snowmelt=0; BALANCE.water.dr_excessSnow=0; - BALANCE.water.dr_rain=0; % this is only rain on frozen ground + BALANCE.water.dr_lateralSnow=0; + BALANCE.water.dr_rain=0; + BALANCE.water.dr_lateral=0; + %mismatch + BALANCE.water.dm_lacking=0; - % soil - OUT.soil.soil{1, size(OUT.soil.soil,2)+1}=[GRID.soil.cT_water GRID.soil.cT_mineral GRID.soil.cT_organic]; - OUT.soil.topPosition=[OUT.soil.topPosition; -GRID.general.K_grid(GRID.soil.cT_domain_ub)]; - - % water body - if ~isempty( GRID.lake.cT_domain_ub ) - OUT.soil.lakeFloor = [OUT.soil.lakeFloor; - GRID.general.K_grid(GRID.lake.cT_domain_lb+1) ]; - else - OUT.soil.lakeFloor = [OUT.soil.lakeFloor; NaN]; - end - - % snow - if ~isempty(GRID.snow.cT_domain_ub) - TEMPORARY.topPosition=-GRID.general.K_grid(GRID.snow.cT_domain_ub); - TEMPORARY.botPosition=-GRID.general.K_grid(GRID.snow.cT_domain_lb+1); - else - TEMPORARY.topPosition=NaN; - TEMPORARY.botPosition=NaN; - end - OUT.snow.topPosition=[OUT.snow.topPosition; TEMPORARY.topPosition]; - OUT.snow.botPosition=[OUT.snow.botPosition; TEMPORARY.botPosition]; + % complete energy balance (EB) + OUT.EB.Qg = [OUT.EB.Qg; TEMPORARY.Qg]; % ground heat flux (positive into ground) + OUT.EB.Qe = [OUT.EB.Qe; TEMPORARY.Qe]; % latent heat flux (positive into ground) + OUT.EB.Qh = [OUT.EB.Qh; TEMPORARY.Qh]; % sensible heat flux (positive into ground) + OUT.EB.Qnet = [OUT.EB.Qnet; TEMPORARY.Qnet]; + OUT.EB.Qgeo = [OUT.EB.Qgeo; PARA.soil.Qgeo]; % geothermal heat flux + OUT.EB.dE_soil_sens = [OUT.EB.dE_soil_sens; BALANCE.energy.dE_soil_sens ]; + BALANCE.energy.dE_soil_sens = 0; + OUT.EB.dE_soil_lat = [OUT.EB.dE_soil_lat; BALANCE.energy.dE_soil_lat ]; + BALANCE.energy.dE_soil_lat = 0; + OUT.EB.dE_soil = [OUT.EB.dE_soil; BALANCE.energy.dE_soil ]; + BALANCE.energy.dE_soil = 0; + OUT.EB.dE_snow_sens = [ OUT.EB.dE_snow_sens; BALANCE.energy.dE_snow_sens ]; + BALANCE.energy.dE_snow_sens = 0; + OUT.EB.dE_snow_lat = [ OUT.EB.dE_snow_lat; BALANCE.energy.dE_snow_lat ]; + BALANCE.energy.dE_snow_lat = 0; + OUT.EB.dE_snow = [ OUT.EB.dE_snow; BALANCE.energy.dE_snow ]; + BALANCE.energy.dE_snow = 0; + OUT.EB.Q_lateral = [OUT.EB.Q_lateral, [ BALANCE.energy.Q_lateral ] ]; + BALANCE.energy.Q_lateral = zeros( length(GRID.general.cT_grid) , 1 ); + + % for DEBUGGING + OUT.debugging.dE_dt_SEB = [OUT.debugging.dE_dt_SEB [ TEMPORARY.dE_dt_SEB ] ]; + OUT.debugging.dE_dt_cond = [OUT.debugging.dE_dt_cond [ TEMPORARY.dE_dt_cond ] ]; + OUT.debugging.K_grid = [OUT.debugging.K_grid, GRID.general.K_grid ]; + %------------------------------------------------------------------ - disp([datestr(now,'yyyy-mm-dd HH:MM:SS'),': at ',datestr(t), ', Average timestep: ', num2str(TEMPORARY.timestep_out), ' seconds']) + disp([datestr(now,'yyyy-mm-dd HH:MM:SS'),': at ', datestr(t), ', Average timestep: ', num2str(TEMPORARY.timestep_out), ' seconds']) TEMPORARY.outputTime=round((TEMPORARY.outputTime+PARA.technical.outputTimestep)./PARA.technical.outputTimestep).*PARA.technical.outputTimestep; - + %write output files if round((t-TEMPORARY.saveTime).*48)==0 - save([run_number '/' run_number '_output' datestr(t,'yyyy') '.mat'], 'OUT') + iSaveOUT( [ saveDir '/' run_number '/' run_number '_' datestr(t,'yyyy') '.mat' ], OUT); + iSaveState( [ saveDir '/' run_number '/' run_number '_finalState' datestr(t,'yyyy') '.mat' ], T, wc, t, SEB, PARA, GRID); + %ttt iPlotAltitudes( [ saveDir '/' run_number '/' run_number '_realization' num2str(labindex) '_altitudes_vs_time_' datestr(t,'yyyy') '.png' ], OUT, PARA ); OUT = generateOUT(); TEMPORARY.saveTime=datenum(str2num(datestr(t,'yyyy'))+1, str2num(datestr(t,'mm')), str2num(datestr(t,'dd')), str2num(datestr(t,'HH')), str2num(datestr(t,'MM')), 0); end - end \ No newline at end of file + end +end diff --git a/modules/cryoGridTechnical/updateWaterEnergyBalance.m b/modules/cryoGridTechnical/updateWaterEnergyBalance.m deleted file mode 100644 index 9d4e980..0000000 --- a/modules/cryoGridTechnical/updateWaterEnergyBalance.m +++ /dev/null @@ -1,32 +0,0 @@ - % at this point the thermal and hydrological state of the soil and snow is calculated - % energy content soil domain in [J/m^2] - % distinguished by sensible and latent part - E_soil_sens_old = E_soil_sens; - E_soil_lat_old = E_soil_lat; - E_soil_old = E_soil; - E_soil_sens = nansum( ( PARA.constants.c_w .* lwc + PARA.constants.c_i .* (wc-lwc) + PARA.constants.c_m .* GRID.soil.cT_mineral + PARA.constants.c_o .* GRID.soil.cT_organic ) .* T(GRID.soil.cT_domain) ... - .* GRID.general.K_delta(GRID.soil.cT_domain) ); - E_soil_lat = nansum( PARA.constants.rho_w .* PARA.constants.L_sl .* lwc .* GRID.general.K_delta(GRID.soil.cT_domain) ); - E_soil = E_soil_sens + E_soil_lat; - TEMPORARY.dE_soil_sens = TEMPORARY.dE_soil_sens + E_soil_sens - E_soil_sens_old; - TEMPORARY.dE_soil_lat = TEMPORARY.dE_soil_lat + E_soil_lat - E_soil_lat_old; - TEMPORARY.dE_soil = TEMPORARY.dE_soil + E_soil - E_soil_old; - % energy content snow domain in [J/m^2] - E_snow_sens_old = E_snow_sens; - E_snow_lat_old = E_snow_lat; - E_snow_old = E_snow; - E_snow_sens = nansum( c_cTgrid(GRID.snow.cT_domain) .* T(GRID.snow.cT_domain) .* GRID.general.K_delta(GRID.snow.cT_domain) ); - E_snow_lat = nansum( PARA.constants.rho_w .* PARA.constants.L_sl .* lwc_cTgrid(GRID.snow.cT_domain).* GRID.general.K_delta(GRID.snow.cT_domain) ); - E_snow = E_snow_sens + E_snow_lat; - TEMPORARY.dE_snow_sens = TEMPORARY.dE_snow_sens + E_snow_sens - E_snow_sens_old; - TEMPORARY.dE_snow_lat = TEMPORARY.dE_snow_lat + E_snow_lat - E_snow_lat_old; - TEMPORARY.dE_snow = TEMPORARY.dE_snow + E_snow - E_snow_old; - % water content soil domain in [m] - W_soil_old = W_soil; - W_soil = nansum( wc .* GRID.general.K_delta(GRID.soil.cT_domain) ); - HYDRO.dW_soil = HYDRO.dW_soil + (W_soil - W_soil_old)*1000; % in [mm] - % water content snow domain in [m] - W_snow_old = W_snow; - W_snow = nansum( GRID.snow.Snow_i + GRID.snow.Snow_w ) + GRID.snow.SWEinitial; - HYDRO.dW_snow = HYDRO.dW_snow + (W_snow - W_snow_old)*1000; % in [mm] - \ No newline at end of file diff --git a/run_batch.m b/run_batch.m new file mode 100644 index 0000000..590f2ba --- /dev/null +++ b/run_batch.m @@ -0,0 +1,99 @@ +% script to excecute multiple independent runs of CryoGrid3 in parallel +% using the job/task batch framework + +add_modules_function; + +startDate=datenum( 1979, 6, 1); +endDate=datenum( 2014, 6, 1); + +rainFrac=1; +snowFrac=1; +waterTable = 0; +waterTables = [ 0.0, 10.0 ]; +maxSnow = 0.4; +maxSnows = [ 0.2, 0.4 ]; +snowDens = 200; +snowDensities = [ 200, 250 ]; +extFlux = 0; +externalFluxes = [ -2e-3, 0, 2e-3 ]; +fieldCapacity = 0.3; +exices = [ 0.9, 0.8, 0.7, 0.6, 0.5, 0.4]; +natPor = 0.4; + + +% combinations = {}; +% +% i=1; +% for ms=maxSnows +% for sd=snowDensities +% for ef=externalFluxes +% combinations{i} = [ms, sd, ef]; +% i=i+1; +% end +% end +% end + +combinations = {}; + +i=1; +for ex=exices + for wt=waterTables + combinations{i}= [ex,wt]; + i=i+1; + end +end + +%% + +numTasks = length( combinations ); + +jobName = 'SPINUP-EXICE'; + +parallel.defaultClusterProfile('local'); +c = parcluster(); + +job = createJob( c, 'Name', jobName ); +disp( [datestr(now) ': created job ' jobName ] ); + +tasks = {}; +% for i=1:numTasks +% maxSnow=combinations{i}(1); +% snowDens=combinations{i}(2); +% extFlux = combinations{i}(3); +% +% taskName = sprintf( [ jobName '_' datestr( startDate, 'yyyymm' ) '-' datestr( endDate, 'yyyymm' ) '_stratSam_rf%d_sf%d_maxSnow%0.1f_snowDens=%0.1f_wt%0.1f_extFlux%0.4f_fc%0.2f' ], ... +% [ rainFrac, snowFrac, maxSnow, snowDens, ... +% waterTable, extFlux, fieldCapacity ] ); +% tasks{i} = createTask( job , @CryoGrid3_function_spinup, 0 , { taskName, startDate, endDate, rainFrac, snowFrac, waterTable, maxSnow, snowDens, extFlux, fieldCapacity }, 'CaptureDiary', true, 'Name', taskName ); +% disp( [ datestr(now) ': created task ' taskName ] ); +% end + +for i=1:numTasks + exice=combinations{i}(1); + waterTable=combinations{i}(2); + + taskName = sprintf( [ jobName '_' datestr( startDate, 'yyyymm' ) '-' datestr( endDate, 'yyyymm' ) '_stratSamExice_rf%d_sf%d_maxSnow%0.2f_snowDens=%d_wt%0.1f_extFlux%0.4f_fc%0.2f_exice%0.2f_natPor%0.2f' ], ... + [ rainFrac, snowFrac, maxSnow, snowDens, waterTable, extFlux, fieldCapacity, exice, natPor ] ); + + tasks{i} = createTask( job, @CryoGrid3_function_variableExice, 0, ... + { taskName, startDate, endDate, rainFrac, snowFrac, waterTable, maxSnow, snowDens, extFlux, fieldCapacity, exice, natPor }, ... + 'CaptureDiary', true, 'Name', taskName); + disp( [ datestr(now) ': created task ' taskName ] ); +end + +submit(job); +disp( [ datestr(now) ': submitted job ' jobName ] ); + + +wait(job); +disp( [ datestr(now) ': finished job ' jobName ] ); +% +% for i=1:numTasks +% diary( [ './runs/' tasks{i}.Name '/' tasks{i}.Name '_diary.txt' ] ); +% tasks{i}.Diary +% diary off +% disp( [datestr(now) ': saved diary of task ' tasks{i}.Name ] ); +% end + +%delete(job); +%disp( [datestr(now) ': deleted job ' jobName ' - Done.'] ); diff --git a/run_batch_CryoGrid3_lake.m b/run_batch_CryoGrid3_lake.m new file mode 100644 index 0000000..8140fd4 --- /dev/null +++ b/run_batch_CryoGrid3_lake.m @@ -0,0 +1,122 @@ +% run CryoGrid3 in batch modus +clear all; close all; delete( gcp('nocreate') ); +add_modules; % needs to be called in main.m ... (on cluster) +SETUP = {}; % auxiliary struct containing parameters which are often varied. this struct is passed as an argument to the main script. + +parallel.defaultClusterProfile('local'); c = parcluster(); delete( c.Jobs ); % jjj ? +jobName = 'Lateral_Heat_Lake'; +jobs = {}; + +%% shallow lake (1-3: SSW, 4-6:MSW) +SETUP.LD = 1; % lake depth in meter +SETUP.Tini = [ -5 15 15;... % SSW setting + 0 10 6;... % surface + 1 -3 6;... % lake bottom + 2 -4 0;... % talik depth + 10 -7 -5;... + 20 -10 -10;... + 100 -10 -10;... + 2000 10 10]; +% SSW + SETUP.LR = 10; % lake radius in meter +% 1) LD 1m LR 10m LC=0.1 +i=1; +SETUP.LF= 0.1; % lake landscape coverage +SETUP.endtime = datenum(2015, 12, 31); +jobs{i} = batch( @CryoGrid3_lake_batch, 0, { SETUP }, 'CaptureDiary',true, 'Pool', 2 ); +disp( [datestr(now) ': created job ',num2str(i) ] ); +% 2) LD 1m LR 10m LC=0.25 +i=2; +SETUP.LF= 0.25; +SETUP.endtime = datenum(2099, 12, 31); +jobs{i} = batch( @CryoGrid3_lake_batch, 0, { SETUP }, 'CaptureDiary',true, 'Pool', 2 ); +disp( [datestr(now) ': created job ',num2str(i) ] ); +% 3) LD 1m LR 10m LC=0.5 +i=3; +SETUP.LF= 0.5; +SETUP.endtime = datenum(2015, 12, 31); +jobs{i} = batch( @CryoGrid3_lake_batch, 0, { SETUP }, 'CaptureDiary',true, 'Pool', 2 ); +disp( [datestr(now) ': created job ',num2str(i) ] ); + +% MSW + SETUP.LR = 100; % lake radius in meter +% 4) LD 1m LR 100m LC=0.1 +i=4; % counter for the jobs +SETUP.LF= 0.1; % lake landscape coverage +SETUP.endtime = datenum(2015, 12, 31); +jobs{i} = batch( @CryoGrid3_lake_batch, 0, { SETUP }, 'CaptureDiary',true, 'Pool', 2 ); +disp( [datestr(now) ': created job ',num2str(i) ] ); +% 5) LD 1m LR 100m LC=0.25 +i=5; +SETUP.LF= 0.25; +SETUP.endtime = datenum(2099, 12, 31); +jobs{i} = batch( @CryoGrid3_lake_batch, 0, { SETUP }, 'CaptureDiary',true, 'Pool', 2 ); +disp( [datestr(now) ': created job ',num2str(i) ] ); +% 6) LD 1m LR 100m LC=0.5 +i=6; +SETUP.LF= 0.5; +SETUP.endtime = datenum(2015, 12, 31); +jobs{i} = batch( @CryoGrid3_lake_batch, 0, { SETUP }, 'CaptureDiary',true, 'Pool', 2 ); +disp( [datestr(now) ': created job ',num2str(i) ] ); + +%% deep lake (7-9: SSW, 10-12: MSW) +SETUP.LD = 5; % lake depth in meter +SETUP.Tini = [ -5 15 15;... % MSW setting for summer + 0 10 5;... % soil surface + 5 -3 5;... % lake bottom + 8 -6 0;... % talik depth + 20 -10 -9;... + 100 -10 -10;... + 2000 10 10]; + +% SSW + SETUP.LR = 10; % lake depth in meter +% 7) LD 5m LR 10m LC=0.1 +i=7; +SETUP.LF= 0.1; % lake landscape coverage +SETUP.endtime = datenum(2015, 12, 31); +jobs{i} = batch( @CryoGrid3_lake_batch, 0, { SETUP }, 'CaptureDiary',true, 'Pool', 2 ); +disp( [datestr(now) ': created job ',num2str(i) ] ); +% 8) LD 5m LR 10m LC=0.25 +i=8; +SETUP.LF= 0.25; +SETUP.endtime = datenum(2099, 12, 31); +jobs{i} = batch( @CryoGrid3_lake_batch, 0, { SETUP }, 'CaptureDiary',true, 'Pool', 2 ); +disp( [datestr(now) ': created job ',num2str(i) ] ); +%9) LD 5m LR 10m LC=0.5 +i=9; +SETUP.LF= 0.5; +SETUP.endtime = datenum(2015, 12, 31); +jobs{i} = batch( @CryoGrid3_lake_batch, 0, { SETUP }, 'CaptureDiary',true, 'Pool', 2 ); +disp( [datestr(now) ': created job ',num2str(i) ] ); + +% MSW + SETUP.LR = 100; % lake depth in meter +% 10) LD 5m LR 100m LC=0.1 +i=10; % counter for the jobs +SETUP.LF= 0.1; % lake landscape coverage +SETUP.endtime = datenum(2015, 12, 31); +jobs{i} = batch( @CryoGrid3_lake_batch, 0, { SETUP }, 'CaptureDiary',true, 'Pool', 2 ); +disp( [datestr(now) ': created job ',num2str(i) ] ); +% 11) LD 5m LR 100m LC=0.25 +i=11; +SETUP.LF= 0.25; +SETUP.endtime = datenum(2099, 12, 31); +jobs{i} = batch( @CryoGrid3_lake_batch, 0, { SETUP }, 'CaptureDiary',true, 'Pool', 2 ); +disp( [datestr(now) ': created job ',num2str(i) ] ); +% 12) LD 5m LR 100m LC=0.5 +i=12; +SETUP.LF= 0.5; +SETUP.endtime = datenum(2015, 12, 31); +jobs{i} = batch( @CryoGrid3_lake_batch, 0, { SETUP }, 'CaptureDiary',true, 'Pool', 2 ); +disp( [datestr(now) ': created job ',num2str(i) ] ); +%% + +% delete(job) +% clear job + +% % to watch the status use c.Jobs(i)(.Tasks) or jobs{i}(.Tasks) +% % j = batch('CryoGrid3_xice_mpi','Pool',3,'CaptureDiary',true); +% % wait(j); % Wait for the job to finish +% % diary(j) % Display the diary +% % load(j) % Load job workspace data into client workspace diff --git a/run_parallel.m b/run_parallel.m new file mode 100644 index 0000000..2d9e494 --- /dev/null +++ b/run_parallel.m @@ -0,0 +1,83 @@ +% script to excecute multiple independent runs of CryoGrid3 in parallel + +delete( gcp('nocreate') ); + +add_modules_function; + +number_of_combinations = 12; + + +startDate=datenum( 1979, 6, 1); +endDate=datenum( 1989, 6, 1); + +rainFrac=1; +snowFrac=1; +waterTable = 0; +waterTables = [ 0.0, 10.0 ]; +maxSnow = 0.4; +maxSnows = [ 0.2, 0.4 ]; +snowDens = 200; +snowDensities = [ 200, 250 ]; +extFlux = 0; +externalFluxes = [ -2e-3, 0, 2e-3 ]; +fieldCapacity = 0.3; +exices = [ 0.9, 0.8, 0.7, 0.6, 0.5, 0.4]; +natPor = 0.4; + + +combinations = {}; + +i=1; +for ms=maxSnows + for sd=snowDensities + for ef=externalFluxes + combinations{i} = [ms, sd, ef]; + i=i+1; + end + end +end + +% +% i=1; +% for wt=waterTables +% for ex=exices +% combinations{i} = [wt, ex]; +% i=i+1; +% end +% end + +%start parallel pool +% parpool(number_of_combinations, 'SpmdEnabled', false ); +% +% parfor i=1:number_of_combinations +% fprintf ( 'Started execution of combination %d ...', i ); +% %CryoGrid3_function_variableExice( startYear, endYear, rainFrac, snowFrac, combinations{i}(1), maxSnow, snowDens, extFlux, fieldCapacity, combinations{i}(2), natPor); +% CryoGrid3_function_spinup( startDate, endDate, rainFrac, snowFrac, waterTable, combinations{i}(1), combinations{i}(2), combinations{i}(3), fieldCapacity ) +% fprintf ( '... finished execution of combination %d.', i ); +% end + +%delete( gcp('nocreate') ); + +parpool(number_of_combinations ); +%alternative implementation using spmd +spmd + i=labindex(); + CryoGrid3_function_spinup( startDate, endDate, rainFrac, snowFrac, waterTable, combinations{i}(1), combinations{i}(2), combinations{i}(3), fieldCapacity ); +end + +% alternative implementation using parfeval + +% parpool( number_of_combinations ); +% % To request multiple evaluations, use a loop. +% for idx = 1:number_of_combinations +% parfeval(CryoGrid3_function_spinup, 0, startDate, endDate, rainFrac, snowFrac, waterTable, combinations{i}(1), combinations{i}(2), combinations{i}(3), fieldCapacity); % Square size determined by idx +% end +% % Collect the results as they become available. +% % magicResults = cell(1,10); +% % for idx = 1:10 +% % % fetchNext blocks until next results are available. +% % [completedIdx,value] = fetchNext(f); +% % magicResults{completedIdx} = value; +% % fprintf('Got result with index: %d.\n', completedIdx); +% % end + diff --git a/run_single.m b/run_single.m new file mode 100644 index 0000000..b46df52 --- /dev/null +++ b/run_single.m @@ -0,0 +1,21 @@ +add_modules_function; + +startDate=datenum( 1979, 6, 1); +endDate=datenum( 1982, 7, 1); + +rainFrac=1; +snowFrac=1; +waterTable = 0; +waterTables = [ 0.0, 10.0 ]; +maxSnow = 0.4; +snowDens = 200; +extFlux = 2e-3; +fieldCapacity = 0.3; +exices = [ 0.9, 0.8, 0.7, 0.6, 0.5, 0.4]; +natPor = 0.4; + +jobName = 'TESTRUN'; +taskName = sprintf( [ jobName '_' datestr( startDate, 'yyyymm' ) '-' datestr( endDate, 'yyyymm' ) '_stratSam_rf%d_sf%d_maxSnow%0.1f_snowDens=%0.1f_wt%0.1f_extFlux%0.4f_fc%0.2f' ], ... + [ rainFrac, snowFrac, maxSnow, snowDens, ... + waterTable, extFlux, fieldCapacity ] ); +CryoGrid3_function_spinup(taskName, startDate, endDate, rainFrac, snowFrac, waterTable, maxSnow, snowDens, extFlux, fieldCapacity ); \ No newline at end of file