Macro.
This capability of the CRHM program allows users to create simple modules suitable for testing algorithms and for diagnosing CRHM model output.
Local Variables.
Local variables are defined using the the keyword "var". For example "var i", "var i var j" or "var i, j".
CRHM variables.
CRHM variables as those defined in the "declreadobs", "declgetvar", "declparam", "declvar" and "declobs" declarations. Note that the latter three types are defined in the current macro module and the first two types are derived from other CRHM modules in the model. The macro commands are enclosed in a for loop which is executed NHRU times. A local variable "hh" is defined so that values for every iteration may be saved in the CRHM macro module variable output. Note that local variables are not accessible outside the macro module except by saving their values into CRHM variables.
Arithmetical Operators.
+, - addition/subtraction
*. / multiplication/division
^ exponentiation
% modulus
(...) brackets enclosing an arithmetical expression.
[n] array element index. Order for 2-D is [hh][ll], i.e. hru first. Elements are referenced 1, 2, 3, 4 ... Cannot be an expression. Use var i; i = J+k; array[i], not array[j+k].
Logical Operators.
|| OR.
&& AND.
!= Not equal.
== Equal.
<= Less Than or Equal.
< Less Than.
>= Greater Than or Equal.
> Greater Than.
! Logical Not. (Faulty)
Control Statements.
if (condition) ... else ... endif
multiple statements or none are permitted in the TRUE and FALSE fields.
The "if" statement must always be followed by a closing "endif" statement.
"else" is optional if there are no FALSE statements to execute.
Multiple "if" statements are permitted.
"if" statements can appear within other "if" statements.
Lowercase must be used for "if', "else" and "endif".
"else" "if" must always be entered as two separate words.
Example :- if ... else if ...endif endif.
while(condition) ... endwhile.
while condition is true the code in the body of the while is executed.
for(initialization; condition; increment) ... endfor.
no field may be left empty.
initialization sets initial value of optional loop counter.
condition when FALSE terminates the loop.
condition can be a compound logical statement, e.g. "for (X = 0; lastX - X > 0.01 && max < 1000; max = max +1)".
initialization and increment fields can have multiple statements separated by commas, e.g. "for (i = 0, j = 0; i < 10; i = i+1, j = j+2)".
Subroutine Library.
sin deg, where deg(°)
cos deg, where deg (°)
exp
log
log10
min
max
estar t
patmos Ht, in kPa, where Ht (m) is the height.
rhoa t, ea, Pa, in (kg/m^3), where t (°C), ea (kPa) and Pa (kPa) is the atmospheric pressure.
spec_humid ea, Pa, in (kg/kg) where ea (kPa) and Pa (kPa) is the atmospheric pressure.
PI
DAY - current day
MONTH - current month
YEAR - current year
JULIAN - Julian day of the year.
FREQ - number of time intervals in a day.
STEP - current interval starting at 1.
GROUP - Current group index. 1 to maximum number of groups.
STRUCT - Current struct index. 1 to maximum number of structs.
FIRSTINT - True for the first interval of the day. When STEP % FREQ equals 1.
LASTINT - True for the last interval of the day. When STEP % FREQ equals 0.
NO_DISPLAY - If variable is set to this value it will not display. When exported creates a sparse file.
RAND - random numbers between 0.0 and 1.0.
ReadAheadObs - write to this function to read observations before and after the current interval. Writing -2 will cause all observations referenced by a "declreadobs" declaration in this module, to refer to the interval two periods earlier, +2 to the period two intervals later and 0 will return module read observations to the current interval. Reading from ReadAheadObs returns the status, 1 - error (outside available observation range). HRU_OBS is not used to access the observation. Observations are read in sequence as stored in the file.
WriteAheadObs - use this function to write the values of the current interval observations to permanent storage. Useage is to read from the desired interval using ReadAheadObs function. Then changing the value of the desired observation and then writing the new values to observation storage using the function WriteAheadObs with the same interval offset..
Macro Declarations.
N.B spaces may be included in text fields if the entire field is enclosed in double quotes.
To create a parameter in the module,
declparam, param, NHRU, 0.2, 0.0, 1.0, "my description", "(my units)"[,Int].
Parameter macro variables are by default floating point. If is necessary to use an existing CRHM integer parameter this can be done by adding "Int" to the end of the normall call;
declparam, inhibit_evap, NHRU, [0], 0, 1, "inhibit evapatation, 1 -> inhibit", "()", "Int".
To manage 2-D parameters the dimension NDEFN is implemented.
declparam, Distrib, NDEFN, [1.0], 0.0, 100.0, "Test 2D parametert", "()"
The order of element access to Distrib [HRU][LAY].
To change the value of a CRHM parameter declared in another module,
declputparam, module_name, variable_name, (units).
To use a CRHM observation within the module,
declreadobs, t, NOBS, description, (units). N.B. access is limited to the available observations. Last available observation is used to satisfy any remaining requests.
To use a CRHM observation function within the module,
declobsfunc, t, tfunc, FUNC. N.B. access is limited to primitive observations. FUNC from "AVG, MIN, MAX, DTOT, POS, TOT, FIRST, LAST, MJ_W and W_MJ".
To create a new CRHM variable for the module,
declvar, OutVar, NHRU, description, (units) [,Int].
decldiag, OutVar, NHRU, description, (units)[,Int].
decllocal, OutVar, NHRU, description, (units)[,Int].
To manage 2-D parameters the dimension NDEFN is implemented.
declvar, Test_NDEFN, NDEFN, "Test 2D variable", ().
The order of element access to Test_NDEFN [HRU][LAY].
To create a new state CRHM variable for the module,
declstatvar, OutVar, NHRU, description, (units).
To create a new CRHM local variable for the module. N.B. this a variable local to this module. Not to be confused with a parser local variable.
decllocal, OutVar, NHRU, description, (units).
To use a CRHM variable from another module,
declgetvar, module_name, variable_name, (units).
To use a CRHM variable declared in another module and alter its value,
declputvar, module_name, variable_name, (units).
To use a CRHM parameter declared in another module and alter its value,
declputparam, module_name, variable_name, (units).
To create a CRHM observation from existing observations, parameters and variables,
declobs, t2, NHRU, description, (units). N.B. if observation is already defined by an observation file - does nothing.
To force modules into a desired loading order,
setpeer, PeerVar, PeerRank, where PeerVar is a CRHM variable that the current module must be loaded after and the PeerRank is the offset at this level.
This command is required when a module has no input variables to allow CRHM to determine the position of the module in the model order. A typical case is a module whose inputs consist of observations. Automatically it will be loaded early in the model even if it uses declared observations from other modules because all types of observations have the same priority.
To force the module to load after a declared observation has been calculated set PeerVar to 'ObsName#'. The # symbol differentiates between a variable named 'ObsName' and a declared observation named 'ObsName'.
The PeerVar cannot be a variable that is accessed using a declputvar as these variables have no rank value. Examples of these variables are "SWE", Sd, soil_moist, soil_rechr, hru_actet and hru_cum_actet.
Macro Structure.
The first line of a Macro is its name. This is the name that it is identified by in the model. Macro and Module names must be unique. Text after the module name is handled as the module description.
Next follows the declaration section. Each declaration is on a new line.
The "command" line ends the declaration section and begins the code to be executed.
The execution code is free format and may be indented and commented.
The end of the macro definition is indicated by the "end" statement on a new line.
Comments.
Code may be documented line using "//". Any text after the "//" is ignored and handled as a comment.
To use spaces in declaration descriptive(text) fields enclose the desired text in double quotes, e.g. declparam, param, NHRU, 0.2, 0.0, 1.0, "my description", "(my units)"
Array references are in the range of 1 to the maximum number of HRUs.
Element[0] is illegal. When using a standard observation variable the element access is [1], e.g. T[1], u[1] etc. If the array element is not specified it will default to [1]. Not recommended.
When accessing observations, the element is limited to the maximum defined element for the observation.
Macro Edit Screen.
This screen is a simple text editor. At present no "smarts" are built in. The screen has the capability to cut and paste to and from itself and to and from other applications. Macro modules can be saved from the screen using the File menu. The default file extension is "*mcr". These macro files are never used by CRHM and are for the use of the user only. The two buttons allow the user to save the screen changes to CRHM or cancel current changes and return to the last saved CRHM screen in the model.
To create a new line use CTRL + Enter.
When loading a macro file (*mcr), it will by default insert the text into the edit screen at the position of the cursor. However, if the edit screen has a selection, it will be replaced by the contents of the file.
When saving a macro, the entire edit screen is saved to the file unless there is a selection and in that case only the selected text will be saved.
Saving Macros.
Macros are automatically saved to the CRHM project file when the model is saved as a project in the main screen file menu. A macro may also be saved as a file in the Macro Edit Screen for import into another project. The file extension used is ".mcr". Since CRHM loads executable Macros from the project file, to utilise code in a "*.mcr" file the file must be loaded into CRHM using the Macro Edit Screen and then the project saved. Exit from CRHM and then re-run CRHM and load the project file.
Flow Screen.
Since macro modules used for debugging may not be required to satisfy inputs to the current CRHM model, CRHM will detect them as unused. To keep the macro modules, always select "NO" in the "Remove module" dialogue box. Macro declared observations are labelled with a trailing "#". For example the Macro declared observation "MyObs" will be displayed as "MyObs#". This notation differentiates declared Macro Observations from declared Macro Variables.
Declared Observation Linking Priority.
When a model is run and an Observation is available from a file (field observation) and also from a Macro, CRHM by default will use the observation from the file.
When a Macro defines an observation that should have a higher priority than the file observation, its name should have a trailing "#' sign, e.g. "MyObs#" which will display in the flow screen as "MyObs#". When this convention is used, "hard code" Modules have to contain extra code to handle the special name.
Macro Example.
The following macro definitions demonstrate the following features.
Macros are named by the user.
Multiple macros may be defined at once.
Standard CRHM parameters allow macro physical outputs to be easily set and modified like normal CRHM modules.
Any Observations from the CRHM model may be accessed.
Any CRHM module/macro output variable may be accessed.
CRHM variable outputs may be generated to be used by other macro or standard CRHM modules.
Local variable values are preserved from time interval to time interval.
Writer should provide a description and units for the variables and parameters used to permit CRHM to supply help information to the user.
MyMacro1 optional module description
declparam, param, NHRU, 0.2, 0.0, 1.0, "my description", (my_units)
declreadobs, t, NOBS, description, (units)
declvar, OutVar, NHRU, description, (units)
declvar, XOutVar, NHRU, description, (units)
declgetvar, obs, hru_t, "(°C)"
command // code to be executed
OutVar[1]=param[1]*t[1] OutVar[2]=param[2]*t[1] OutVar[3]=param[3]*t[1] // array element access by numeric value (range 1 - # HRUs)
var i i=i+1 XOutVar= sin(i) var j j=i+180 XOutVar[2] = sin(j) XOutVar[3] = cos(PI/36*i)
end // end of code and end of module definition
MyMacro2 // beginning of next module definition
declparam, param2, NHRU, 0.2, 0.0, 1.0, description, (units)
declreadobs, t, NOBS, description, (units)
declvar, Z, NHRU, description, (units)
declvar, Y, NHRU, description, (units)
declgetvar, Macro1, OutVar, (units)
command
Z[hh]=param2[hh]*t[1]
Y[hh] = param2[hh]*OutVar[hh]
end
Evaporation Example.
Evaporation // module name
declparam, A, NHRU, 0.023, 0.0, 1.0, "description", (mm/day) // declarations
declparam, B, NHRU,17.8, 0.0, 100.0, "description", (°C)
declparam, Zref, NHRU,1.5, 0.001, 100.0, Zref, (m)
declparam, Zwind, NHRU,10, 0.001, 100.0, Zwinf, (m)
declparam, Z0, NHRU,0.001, 0.001, 100.0, Zo, (m)
declvar, EvapAlg, NHRU, "evaporation_algorithm", (MJ/(m2/day))
declvar,cum, NHRU, "cum_evaporation_algorithm", (mm)
declvar,Ra, NHRU,Ra, (s/m)
declgetvar, obs, hru_tmean, "(°C)" // mean air temperature
declgetvar, obs, hru_tmin, "(°C)" // minimum air temperature
declgetvar, obs, hru_tmax, "(°C)" // maximum air temperature
declgetvar, obs, u, "(m/s)" // wind velocity
command // module code
var U U=max(u[0], 0.2) // assume minimum wind velocity to prevent divide by zero errors
Ra[hh] = log(Zref[hh]/Z0[hh])* log(Zwind[hh]/Z0[hh])/0.4^2*U
EvapAlg[hh] =-A[hh]*( hru_tmean[1] - B[hh])*Ra[hh]*( hru_tmax[1] - hru_tmin[1])^0.5*1/(245*2.501)
cum[hh] = cum[hh] + EvapAlg[hh]
end // end of module
Macro Implementation of C++ module.
To relate to a practical example we will design a macro to simulate the module ClassExample described earlier which converts interval net radiation in MJ/(m2-Int) calculated by an earlier module to mm/(m2-Int) of water, i.e. kg/(m2-Int) of water. The air temperature from an observation is required to carry out the conversion. Two other outputs are calculated as a fraction of the module output. These fractions are specified by the parameters F_Qg and F_Qs.
Example // name of micro module
declreadtobs(t, OBS, Temperature, (°C))
declgetvar(netall, net, "(MJ/m^2*int)")
declparam(F_Qg, NHRU, 3*0.2, 0.0, 1.0, Qg=F_Qg*Rn, ())
declparam(F_Qs, NHRU, [0.0], 0.0, 1.0, Qs=F_Qg*Rn, ())
declvar(Rn, NHRU, net, (mm/Int))
declvar(Qg, NHRU, ground_flux, (mm/Int))
declvar(Qs, NHRU, storage_flux, (mm/Int))
// The algorithm code to be executed every time interval and for every NHRU is written into the command area. The program code follows:
command // code is executed for number of HRUs with hh varying between 1 and # HRUs.
Rn[hh] = net[hh]/(2.501-0.002361*t[1])
Qg[hh] = Rn[hh]*F_Qg[hh]
Qs[hh] = Rn[hh]*F_Qs[hh]
end
Since the command code applies to every HRU, it is executed inside a for loop. The output variable Rn, is calculated from the observation temperature and an output variable net calculated in another module. Outputs Qg and Qs from this module are the product of the output Rn and the parameters F_Qg and F_Qs.
Example of "for" and 2-D arrays.
Test_declvar
declvar, Test_NDEFN, NDEFN, "Test 2D variable", ()
declvar, Test_NDEFN2, NDEFN, "Test 2D variable", ()
declparam, Test_par_NDEFN, NDEFN, [1.0], 0.0, 100.0, "Test 2D parametert", "()"
command
var Fred [NHRU][7]
var ll
ll = 1
for(ll = 1; ll <= NHRU; ll = ll +1)
Fred[hh] [ll] = Test_par_NDEFN[hh][ll]*5
Test_NDEFN[hh][ll] = Test_par_NDEFN[hh][ll]
Test_NDEFN2[hh][ll] = Fred[hh][ll]
endfor
end
Example of accessing variables and parameters from another module, in this case pbsm_M.
Test_getvar
declvar, Test_NDEFN, NDEFN, "Test 2D variable", ()
declvar, Test_NDEFN_P, NDEFN, "Test 2D variable", ()
declgetvar, pbsm_M, Results, ()
declparam, distrib, NDEFN, 1.0, 0.0, 100.0, "Test 2D parametert", "()"
command
var ll
for(ll = 1; ll <= NHRU; ll = ll +1)
Test_NDEFN[hh][ll] = Results[hh][ll]
Test_NDEFN_P[hh][ll] = distrib[hh][ll]
endfor
end
As always when sharing a parameter between modules, all values of the parameter should be made the same in every module, then the project saved and reloaded when the shared parameter should appear only in the "basin" module.
Known problems.
If a major change is made to a macro, i.e. insertion or deletion of a declaration, the user should exit from the macro entry screen and immediately save the project and then exit from CRHM. When CRHM is restarted it will execute properly. At present CRHM is not handling some aspects of allocation/deallocation of variables correctly.