Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
.data/
run_model_on_snellius/exe/includedSupportPackages.txt
run_model_on_snellius/exe/mccExcludedFiles.log
run_model_on_snellius/exe/readme.txt
run_model_on_snellius/exe/requiredMCRProducts.txt
run_model_on_snellius/exe/run_STEMMUS_SCOPE.sh
run_model_on_snellius/exe/unresolvedSymbols.txt
14 changes: 11 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
# Unreleased

**Added:**
**Changed:**
- Change array shape of precipitation and runoff fluxes from time-series array to 1D array (for BMI purposes) discussed in [#280](https://github.com/EcoExtreML/STEMMUS_SCOPE/pull/280) and added in [#282](https://github.com/EcoExtreML/STEMMUS_SCOPE/pull/282)

**Fixed:**
- Correct snow precipitation calcuations discussed in [#279](https://github.com/EcoExtreML/STEMMUS_SCOPE/issues/279) and fixed in [#282](https://github.com/EcoExtreML/STEMMUS_SCOPE/pull/282)

<a name="1.6.2"></a>
# [1.6.2](https://github.com/EcoExtreML/STEMMUS_SCOPE/releases/tag/1.6.2) - 17 Dec 2024

The 1.6.2 release comes with minor changes as:

**Added:**
- Documentation using mkdocs and a github action workflow to publish the
documentation in [#264](https://github.com/EcoExtreML/STEMMUS_SCOPE/pull/264)

**Changed:**

<a name="1.6.1"></a>
# [1.6.1](https://github.com/EcoExtreML/STEMMUS_SCOPE/releases/tag/1.6.1) - 26 Sep 2024

Expand Down
Binary file modified run_model_on_snellius/exe/STEMMUS_SCOPE
Binary file not shown.
27 changes: 23 additions & 4 deletions src/+energy/calculateBoundaryConditions.m
Original file line number Diff line number Diff line change
@@ -1,8 +1,27 @@
function [RHS, EnergyMatrices] = calculateBoundaryConditions(BoundaryCondition, EnergyMatrices, HBoundaryFlux, ForcingData, SoilVariables, ...
Precip, Evap, Delt_t, r_a_SOIL, Rn_SOIL, RHS, L, KT, ModelSettings, GroundwaterSettings)
Evap, Delt_t, r_a_SOIL, Rn_SOIL, RHS, L, KT, ModelSettings, GroundwaterSettings)
%{
Determine the boundary condition for solving the energy equation, see
STEMMUS Technical Notes.
Determine the boundary condition for solving the energy equation, see
STEMMUS Technical Notes.
Inputs:
BoundaryCondition : structure
EnergyMatrices : structure
HBoundaryFlux : structure
ForcingData : structure
SoilVariables : structure
ModelSettings : structure
GroundwaterSettings: structure

Evap : [g cm^-2 s^-1] Evaporation
Delt_t : [s] Time step, here it is 1800 s
r_a_SOIL: [s cm^-1] Soil surface aerodynamic resistance
Rn_SOIL : [] Net radiation reaching the soil surface
L : [] Latent heat of vaporization
KT : [-] Number of time step
Outputs:
RHS : [degree C] Soil temperature

EnergyMatrices : structure
%}

n = ModelSettings.NN;
Expand Down Expand Up @@ -46,6 +65,6 @@
else
L_ts = L(n);
SH = 0.1200 * Constants.c_a * (SoilVariables.T(n) - ForcingData.Ta_msr(KT)) / r_a_SOIL(KT);
RHS(n) = RHS(n) + 100 * Rn_SOIL(KT) / 1800 - Constants.RHOL * L_ts * Evap - SH + Constants.RHOL * Constants.c_L * (ForcingData.Ta_msr(KT) * Precip(KT) + BoundaryCondition.DSTOR0 * SoilVariables.T(n) / Delt_t); % J cm-2 s-1
RHS(n) = RHS(n) + 100 * Rn_SOIL(KT) / 1800 - Constants.RHOL * L_ts * Evap - SH + Constants.RHOL * Constants.c_L * (ForcingData.Ta_msr(KT) * ForcingData.effectivePrecip + BoundaryCondition.DSTOR0 * SoilVariables.T(n) / Delt_t); % J cm-2 s-1
end
end
2 changes: 1 addition & 1 deletion src/+energy/calculateEnergyFluxes.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function [QET, QEB] = calculateEnergyFluxes(SAVE, TT, ModelSettings)
function [QET, QEB] = calculateEnergyFluxes(SAVE, TT, ModelSettings, GroundwaterSettings)
%{
Calculate the energy fluxes on the boundary nodes, see STEMMUS Technical
Notes.
Expand Down
18 changes: 9 additions & 9 deletions src/+energy/solveEnergyBalanceEquations.m
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
function [RHS, SAVE, CHK, SoilVariables, EnergyVariables] = solveEnergyBalanceEquations(InitialValues, SoilVariables, HeatVariables, TransportCoefficient, ...
AirVariabes, VaporVariables, GasDispersivity, ThermalConductivityCapacity, ...
HBoundaryFlux, BoundaryCondition, ForcingData, DRHOVh, DRHOVT, KL_T, ...
Xah, XaT, Xaa, Srt, L_f, RHOV, RHODA, DRHODAz, L, Delt_t, P_g, P_gg, ...
TOLD, Precip, EVAP, r_a_SOIL, Rn_SOIL, KT, CHK, ModelSettings, GroundwaterSettings)
function [RHS, SAVE, CHK, SoilVariables, EnergyVariables, gwfluxes] = solveEnergyBalanceEquations(InitialValues, SoilVariables, HeatVariables, TransportCoefficient, ...
AirVariabes, VaporVariables, GasDispersivity, ThermalConductivityCapacity, ...
HBoundaryFlux, BoundaryCondition, ForcingData, DRHOVh, DRHOVT, KL_T, ...
Xah, XaT, Xaa, Srt, L_f, RHOV, RHODA, DRHODAz, L, Delt_t, P_g, P_gg, ...
TOLD, EVAP, r_a_SOIL, Rn_SOIL, KT, CHK, ModelSettings, GroundwaterSettings)
%{
Solve the Energy balance equation with the Thomas algorithm to update
the soil temperature 'SoilVariables.TT', the finite difference
Expand All @@ -19,7 +19,7 @@
[RHS, EnergyMatrices, SAVE] = energy.assembleCoefficientMatrices(InitialValues, EnergyMatrices, SoilVariables, Delt_t, P_g, P_gg, ModelSettings, GroundwaterSettings);

[RHS, EnergyMatrices] = energy.calculateBoundaryConditions(BoundaryCondition, EnergyMatrices, HBoundaryFlux, ForcingData, SoilVariables, ...
Precip, EVAP, Delt_t, r_a_SOIL, Rn_SOIL, RHS, L, KT, ModelSettings, GroundwaterSettings);
EVAP, Delt_t, r_a_SOIL, Rn_SOIL, RHS, L, KT, ModelSettings, GroundwaterSettings);

[SoilVariables, CHK, RHS, EnergyMatrices] = energy.solveTridiagonalMatrixEquations(EnergyMatrices, SoilVariables, RHS, CHK, ModelSettings, GroundwaterSettings);

Expand All @@ -42,7 +42,7 @@
end
end

% These are unused vars, but I comment them for future reference,
% See issue 100, item 2
% [QET, QEB] = energy.calculateEnergyFluxes(SAVE, TT)(SAVE, SoilVariables.TT);
[QET, QEB] = energy.calculateEnergyFluxes(SAVE, SoilVariables.TT, ModelSettings, GroundwaterSettings);
gwfluxes.energyTopflux = QET; % energy flux at the top boundary node
gwfluxes.energyBotmflux = QEB; % energy flux at the bottom boundary node
end
2 changes: 1 addition & 1 deletion src/+groundwater/calculateGroundwaterRecharge.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function gwfluxes = calculateGroundwaterRecharge(EnergyVariables, SoilVariables, KT, ModelSettings, GroundwaterSettings)
function gwfluxes = calculateGroundwaterRecharge(EnergyVariables, SoilVariables, KT, gwfluxes, ModelSettings, GroundwaterSettings)
%{
Added by Mostafa, modified after Lianyu
The concept followed to calculate groundwater recharge can be found in:
Expand Down
6 changes: 3 additions & 3 deletions src/+groundwater/updateDunnianRunoff.m
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
function [dunnianRunoff, update_Precip_msr] = updateDunnianRunoff(Precip_msr, groundWaterDepth)
function [runoffDunnian, update_Precip_msr] = updateDunnianRunoff(Precip_msr, groundWaterDepth)
% Dunnian runoff = Direct water input from precipitation + return flow
% (a) direct water input from precipitation when soil is fully saturated (depth to water table = 0)
% (b) Return flow (from groundwater exfiltration) calculated in MODFLOW and added to Dunnian runoff (through BMI)
% here approach (a) is implemented
dunnianRunoff = zeros(size(Precip_msr));
runoffDunnian = zeros(size(Precip_msr));
update_Precip_msr = Precip_msr;

if groundWaterDepth <= 1.0
dunnianRunoff = Precip_msr;
runoffDunnian = Precip_msr;
update_Precip_msr = zeros(size(Precip_msr));
end

Expand Down
2 changes: 1 addition & 1 deletion src/+init/defineInitialValues.m
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@

Nmsrmn = Dur_tot * 10; % Here, it is made as big as possible, in case a long simulation period containing many time step is defined.
fields = {
'Precip', 'Ta', 'Ts', 'U', 'HR_a', 'Rns', 'Rnl', 'Rn', ...
'Ta', 'Ts', 'U', 'HR_a', 'Rns', 'Rnl', 'Rn', ...
'h_SUR', 'SH', 'MO', 'Zeta_MO', 'TopPg'
};
structures{4} = helpers.createStructure(zeros(Nmsrmn, 1), fields);
Expand Down
22 changes: 5 additions & 17 deletions src/+io/loadForcingData.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,16 @@
ForcingData.G_msr = Mdata{:, 7} * 0.15;
Precip_msr = Mdata{:, 6}; % (cm/sec)

%%%%%%%%%% Adjust precipitation to get applied infiltration after removing: (1) saturation excess runoff %%%%%%%%%%
%%%%%%%%%% (2) infiltration excess runoff %%%%%%%%%%
% Note: Code changes are related to the issue: https://github.com/EcoExtreML/STEMMUS_SCOPE/issues/232
% Note: Adjusting the precipitation after the canopy interception is not implemented yet.

% (1) Saturation excess runoff (Dunnian runoff)
% Calculate saturation excess runoff (Dunnian runoff)
if ~GroundwaterSettings.GroundwaterCoupling % Groundwater Coupling is not activated
% Concept is adopted from the CLM model (see issue 232 in GitHub for more explanation)
% Concept is adopted from the CLM model (see issue 232, https://github.com/EcoExtreML/STEMMUS_SCOPE/issues/232)
% Check also the CLM documents (https://doi.org/10.5065/D6N877R0, https://doi.org/10.1029/2005JD006111)
wat_Dep = Tot_Depth / 100; % (m)
wat_Dep = Tot_Depth / 100; % (m), this assumption water depth = total soil depth is not fully correct (to be improved)
fover = 0.5; % decay factor (fixed to 0.5 m-1)
fmax = SoilProperties.fmax; % potential maximum value of fsat
fsat = (fmax .* exp(-0.5 * fover * wat_Dep)); % fraction of saturated area (unitless)
ForcingData.R_Dunn = Precip_msr .* fsat; % Dunnian runoff (saturation excess runoff, in c/sec)
Precip_msr = Precip_msr .* (1 - fsat); % applied infiltration after removing Dunnian runoff
end

% (2) Infiltration excess runoff (Hortonian runoff)
ForcingData.R_Hort = zeros(size(Precip_msr)); % will be updated in +soilmoisture/calculateBoundaryConditions
ForcingData.runoffDunnian = Precip_msr .* fsat; % Dunnian runoff (saturation excess runoff, in cm/sec)
end % In case Groundwater Coupling is activated, Dunnian runoff is calculated in +groundwater/updateDunnianRunoff

% replace negative values
for jj = 1:Dur_tot
Expand All @@ -49,7 +40,4 @@
% Outputs to be used by other functions
ForcingData.Tmin = min(ForcingData.Ta_msr);
ForcingData.Precip_msr = Precip_msr;
% Applied infiltration (= precipitation after removal of Dunnian runoff)
ForcingData.applied_inf = Precip_msr; % later will be updated in the ....
% +soilmoisture/calculateBoundaryConditions after removal of Hortonian runoff
end
94 changes: 58 additions & 36 deletions src/+soilmoisture/calculateBoundaryConditions.m
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
function [AVAIL0, RHS, HeatMatrices, Precip, ForcingData] = calculateBoundaryConditions(BoundaryCondition, HeatMatrices, ForcingData, SoilVariables, InitialValues, ...
TimeProperties, SoilProperties, RHS, hN, KT, Delt_t, Evap, ModelSettings, GroundwaterSettings)
function [AVAIL0, RHS, HeatMatrices, ForcingData] = calculateBoundaryConditions(BoundaryCondition, HeatMatrices, ForcingData, SoilVariables, InitialValues, ...
TimeProperties, SoilProperties, RHS, hN, KT, KIT, Delt_t, Evap, ModelSettings, GroundwaterSettings)
%{
Determine the boundary condition for solving the soil moisture equation.
%}

% n is the index of n_th item
n = ModelSettings.NN;

C4 = HeatMatrices.C4;
C4_a = HeatMatrices.C4_a;

Precip = InitialValues.Precip;
Precip_msr = ForcingData.Precip_msr;
Precipp = 0;

% Apply the bottom boundary condition called for by BoundaryCondition.NBChB
%%%%%% Apply the bottom boundary condition called for by BoundaryCondition.NBChB %%%%%%
if ~GroundwaterSettings.GroundwaterCoupling % no Groundwater coupling
if BoundaryCondition.NBChB == 1 % Specify matric head at bottom to be ---BoundaryCondition.BChB;
RHS(1) = BoundaryCondition.BChB;
Expand Down Expand Up @@ -46,7 +40,42 @@
end
end

% Apply the surface boundary condition called for by BoundaryCondition.NBCh
%%%%%% Apply the surface boundary condition called for by BoundaryCondition.NBCh %%%%%%
Precip = ForcingData.Precip_msr(KT); % total precipitation (liquid + snow)
runoffDunn = ForcingData.runoffDunnian(KT); % Dunnian runoff (calculated in +io/loadForcingData file)

% Check if surface temperature is less than zero, then Precipitation is snow (modified by Mostafa)
if KT == 1 % see issue 279 (https://github.com/EcoExtreML/STEMMUS_SCOPE/issues/279)
Precip_snowAccum = 0; % initalize accumulated snow for first time step
else
Precip_snowAccum = ForcingData.Precip_snowAccum;
end

if SoilVariables.Tss(KT) <= 0 % surface temperature is equal or less than zero
Precip_snow = Precip; % snow precipitation
Precip_liquid = 0; % liquid precipitation (rainfall)
runoffDunn = 0; % update Dunnian runoff in case precpitation is snow
if KIT == 1 % accumulate snow at one iteration only within the time step
Precip_snowAccum = Precip + Precip_snowAccum;
else
Precip_snowAccum = Precip_snowAccum;
end
else % surface temperature is more than zero
if KIT == 1 % add accumulated snow of previous time steps to liquid precipitation at first time step when surface temperature > zero
Precip_liquid = Precip + Precip_snowAccum;
Precip_snowAccum = 0;
else
Precip_liquid = ForcingData.Precip_liquid;
end
Precip_snow = 0;
end

%%% Calculate effective precipitation after removing canopy interception and total runoff %%%
% effective precipitation = precipitation - canopy interception - (Dunnian runoff + Hortonian runoff)
% Currently, canopy interception is not implemented in the code yet
% (1) Remove saturation excess runoff (Dunnian runoff)
effectivePrecip = Precip_liquid - runoffDunn; % Hortonian runoff is removed below

if BoundaryCondition.NBCh == 1 % Specified matric head at surface---equal to hN;
% h_SUR: Observed matric potential at surface. This variable
% is not calculated anywhere! see issue 98, item 6
Expand All @@ -65,41 +94,25 @@
RHS(n) = RHS(n) - BoundaryCondition.BCh; % a specified matric head (saturation or dryness) was applied;
end
else % (BoundaryCondition.NBCh == 3, Specified atmospheric forcing)

% Calculate applied infiltration and infiltration excess runoff (Hortonian runoff), modified by Mostafa
% (2) Calculate infiltration excess runoff (Hortonian runoff) and update effective precpitation, modified by Mostafa
Ks0 = SoilProperties.Ks0 / (3600 * 24); % saturated vertical hydraulic conductivity. unit conversion from cm/day to cm/sec
% Note: Ks0 is not adjusted by the fsat as in the CLM model (Check CLM document: https://doi.org/10.5065/D6N877R)
% Check applied infiltration doesn't exceed infiltration capacity
topThick = 5; % first 5 cm of the soil
satCap = SoilProperties.theta_s0 * topThick; % saturation capacity represented by saturated water content of the top 5 cm of the soil
actTheta = ModelSettings.DeltZ(end - 3:end) * SoilVariables.Theta_UU(end - 4:end - 1, 1); % actual moisture of the top 5 cm of the soil
infCap = (satCap - actTheta) / TimeProperties.DELT; % (cm/sec)
infCap_min = min(Ks0, infCap);

% Infiltration excess runoff (Hortonian runoff). Note: Dunnian runoff is calculated in the +io/loadForcingData file
if Precip_msr(KT) > infCap_min
ForcingData.R_Hort(KT) = Precip_msr(KT) - infCap_min;
else
ForcingData.R_Hort(KT) = 0;
end

Precip(KT) = min(Precip_msr(KT), infCap_min);
ForcingData.applied_inf(KT) = Precip(KT); % applied infiltration after removing Hortonian runoff
infCap = (satCap - actTheta) / TimeProperties.DELT; % infiltration capcaity (cm/sec)
infCap_min = min(Ks0, infCap); % minimum infiltration capcaity

if SoilVariables.Tss(KT) > 0
Precip(KT) = Precip(KT);
if effectivePrecip > infCap_min
runoffHort = effectivePrecip - infCap_min; % Hortonian runoff
else
Precip(KT) = Precip(KT);
Precipp = Precipp + Precip(KT);
Precip(KT) = 0;
runoffHort = 0;
end
% Update effective precipitation after removing Hortonian runoff
effectivePrecip = min(effectivePrecip, infCap_min);

if SoilVariables.Tss(KT) > 0
AVAIL0 = Precip(KT) + Precipp + BoundaryCondition.DSTOR0 / Delt_t; % (cm/sec)
Precipp = 0;
else
AVAIL0 = Precip(KT) + BoundaryCondition.DSTOR0 / Delt_t;
end
% Add depression water to effective precipitation
AVAIL0 = effectivePrecip + BoundaryCondition.DSTOR0 / Delt_t; % (cm/sec)

if BoundaryCondition.NBChh == 1
RHS(n) = hN;
Expand All @@ -111,6 +124,15 @@
RHS(n) = RHS(n) + AVAIL0 - Evap;
end
end

% Outputs to be exported or used in other functions
HeatMatrices.C4 = C4;
HeatMatrices.C4_a = C4_a;
ForcingData.Precip = Precip;
ForcingData.Precip_liquid = Precip_liquid;
ForcingData.Precip_snow = Precip_snow;
ForcingData.Precip_snowAccum = Precip_snowAccum;
ForcingData.effectivePrecip = effectivePrecip;
ForcingData.runoffDunn = runoffDunn;
ForcingData.runoffHort = runoffHort;
end
Loading