JEXL Rule Engine

Top  Previous  Next

Rules are

Arithmetic/logical expressions or small scripts that evaluate to a value

Can be used in solution or project files to enhance SimComponents and control the simulation flow

They can be added via the rule=““ attribute to several elements

Examples:

 ${Phenology.DevStage} == 2 (returns true or false)

 ${weather.VP}*10  (returns a numeric value)

How and where to use rules/expressions in your solution/project.

 

Purpose

 

Triggering the output
<output frequence=“COMPLEX“ rule=“${Pheno.DVS}==2.0“ …

Triggering the updating of resources (e.g. for crop rotation)
<resource id=“crop“ frequence=“COMPLEX“
 rule=“${CURRENT.DOY}==${vSowingDoy}“…

Modify output values (unit conversion, aggregation of layered values)

Management rules (determine Sowing/Harvesting, Irrigation etc.)

Simple SimComponents – e.g. to combine different modules that use different variable types (layered vs. non-layered, different units, etc.)

 

Syntax

 

SimVariables have to be enclosed in ${..}, e.g. ${vSowingDOY}, ${crop.LUE}, ${Lintul.sWSO}

 

Operators:

+,-,*,/

and (&&), or (||), not (!)

lt (<), le (<=), gt (>), ge (>=), eq (==), ne (!=)  

 

Expressions can be grouped as usual by round parantheses () – eg. (a+b)*c

 

Local variables

var x = 3;

 

Scripts

 

Rules can contain more than one expressions, even more complex scripts

The result of the last expression will be returned to Simplace
rule = “
 var x = ${vConversionFactor};
 var y = ${Module.State};
 x * y

Conditions and Loops:

Conditions
if(condition) {doSomething} else {doElse}

Looping over an array
var sum = 0;
for(var wc in ${Slim.WCperLayer})
{
 sum = sum + wc
}

While-Loop
var i = 0;
while (i lt 10)
{
 doSomething;
 i = i + 1;
}

Blocks of code are grouped by curly braces { }

 

Where to use

 

In Solutions/Projects

The rules normally go to the rule=““ attribute (or resetrule=““) of the elements capable to handle rules like <resource>, <output>, <out>, <selector>, <action>, <mgm>, <var>

The rule attribute can handle both: SimVariables or expressions
<out rule=“Lintul.WSO“ … gives the value directly
<out rule=“${Lintul.WSO}“ … same as above – but slower
<out rule=“${Lintul.WSO}*10“… gives WSO multiplied by 10
<out rule=“Lintul.WSO * 10“  does not work!

Simplace seeks inside the rule attribute text for ${…} to decide whether it‘s a real rule or simply a SimVariable

 

In Management

Rules can appear in two places in management

 A boolean rule, that determines when the action (calculation) should be performed

Rules for each management variables, that determines how the values should be calculated.
<action rule="${Phenology.sDVS} ge 2">
 <mgm id="DoHarvest" datatype="BOOLEAN"  
   default="false"
  rule="true“ />
 <mgm id=“HarvestIndex” datatype=“DOUBLE”
   default =“0.0”
  rule=“${Lintul.Yield}/${Lintul.AboveGroundBM}” />
</action>

 

For outputs

    Few examples from HillflowTest.sol.xml:

 1. To sum up all the values from a double array(Hillflow1D.sTHETA)

 <out id="VolumetricWatercontentSumAll" datatype="DOUBLE" rule="var res=0;for (var item : ${Hillflow1D.sTHETA}){res = res + item;}; return res;"/>

 

 2. To take an element from an array use the following syntax: Hillflow1D.sTHETA[index], where 'index' starts from 0, in order to sum up first five elements from the array use the following example:

 <out id="VolumetricWatercontentSum1_5" datatype="DOUBLE" rule="${Hillwflow1D.sTHETA[0]}+${Hillflow1D.sTHETA[1]}+${Hillflow1D.sTHETA[2]}+${Hillflow1D.sTHETA[3]}+${Hillflow1D.sTHETA[4]}"/>

 

Output Frequency

Frequency of outputs can be

DAILY

YEARLY (on 31.12.)

BOOLEAN by using

 frequency=“BOOLEAN“

 rule=“Component.SomeBooleanValue“

COMPLEX by using a rule that evaluates to a boolean value (i.e. true or false)
frequency=“COMPLEX“
rule=“${CURRENT.DOY} == ${vSowDOY}+20“

 

Same apply if you want to determine when resources should be updated.

 

Resetting Output

Non-Daily outputs can aggregate the values from the previous output time up to the actual output time.

Aggregation method is supplied via the mode attribute of each output variable (SUM, AVG, FIRST, LAST)

Outputs have an attribute resetrule that works similar to rule to determine when the outputs should be reset:

<output frequency=“COMPLEX“

 rule=“${P.IsMaturity}“
 resetrule=“${CURRENT.DOY}==${vSowDOY}“ …>

<out id=“EvapDuringVegetation“ mode=“SUM“
     rule=“ET.Evaporation“>

 

Transform Output Variables

In the <out> element one can use rules attribute to modify the value of a variable or compute new values (aggregation etc)
<out id=“EvapTran“
    rule=“${ET.Evap}+${ET.Transp}“ />

One can set the value explicitly to null, thus avoiding that the value is taken into account when averaging.

Modifying individual output variables with rules can be done independently whether the ouput is daily, yearly or triggerd by a complex rule.

 

 

If the number of elements which you want to take, lets say, is more than 20, then you can use the javascript syntax as in the first example and loop through the array.

 

Array Function in Rules

There are many fancy array functions one can use in rules:

array:clone(arr)- make a copy of an array, which you can modify without changing the original
 

array:Double(n,v) - Create an array with n elements by repeating the value v (similar functions array:Integer, array:Char)
 

array:sum(arr), array:sum(arr,start,end) – sum of an array or a part of an array from start to end (inclusive), (similar functions array:avg, array:min, array:max)
 

array:calc(arr1,op,arr2), array:calc(arr,op,scalar) performs an operation (‘+‘,‘-‘,‘*‘,‘/‘,‘^‘) elementwise – no need for loops in rules anymore to do these calculations

 

Expressions

 

Expressions involve arithmetical, logical and comparison operators.

 

Evaluation of Rules in SIMPLACE Framework with the SchmitzM Operation Tree Parser

The Operation Tree Parser evaluates mathematical rules of different type. Logical or numerical operations can be evaluated. Before evaluating the given rules the parameters given in the algorithm are replaced. If a parameter was not correctly found the rule cannot be evaluated. Make sure that either a value or a default is given for the specific parameter.

 

Variables enclosed in ${variable.name}

 

First parameters are replaced hierarchically, then calculation takes place.

 

         ${some.parameter.name} * 15 / ${some.other.param${param.index.param}}

 

Constant numerical values:

 

Numbers in double type

rand or random as alias for random numbers between 0 and 1

"Not a number" (NaN): "NaN"

Boolean TRUE (1)

Boolean FALSE (0)

Current time ms (System.currentTimeMillis()): "CURR_MILLIS"

One day in ms (LangUtil?.DAY_MILLIS): "DAY_MILLIS"

One Week in ms (LangUtil?.WEEK_MILLIS): "WEEK_MILLIS"

 

Constant String values

 

Character arrays are started and ended by " or '.

 

2-partner arithmetic operation

 

Summation: "+"

Subtraction: "-"

Multiplication: "*"

Division: "/"

Exponent: ""

 15 + 5.6 (20.6)

 

The 2-partner Boolean operator

 

Boolean AND: "&"

Boolean OR: "|"

Equals: "=" or "=="

Unequal: "!=" or "<>"

Smaller than: "<"

Greater than ">"

Smaller equals: "<="

Greater equals: ">="

 

The 2-partner character operation

 

String-concatenation: "+"

Regular expression check: "regex(expression,regular expr)"

First value of a String-Splits via regular expression: "split(expression,regular expr)"

N-th value of the string split via regular expression: "split(expression,regular expr,n)"

String-Replace: "replAll(expression,pattern expr,repl expr)"

 

The singular operation

 

Absolute Value: "abs(.)"

Square root: "sqr(.)" or "sqrt(.)"

Round: "rnd(.)" or "round(.)"

Cut: "int(.)" or "trunc(.)"

Test Not A Number (NaN): "isNaN(.)"

Sinus: "sin(.)"

Co sinus: "cos(.)"

Tangent: "tan(.)"

Arcos-Sinus: "arcsin(.)" or "asin(.)"

Arcos - Co sinus: "arccos(.)" or "acos(.)"

Arcos - Tangent: "arctan(.)" or "atan(.)"

Exponential: "exp(.)"

Logarithmic base e: "ln(.)"

Logarithmic base 10: "log(.)"

 

The singular Boolean operation

 

Boolean NOT: "!(.)"

 

The singular String operation

 

Boolean NOT: "!(.)"

Convert Number to String: "str(.)"

Convert String to Number: "val(.)"

Convert to upper case: "toupper(.)"

Convert to lower case: "tolower(.)"

Convert date-String to Milli-Seconds: str2millis(string expr, format string expr)

Convert date to Milli-Seconds: date2millis(year, month, day)

Convert Milli-Seconds to date-String: millis2Str(millis, format string expr)

Convert date to date-String: date2Str(year, month, day, format string expr)

 

The 3-partner operation ITE(.,.,.)

 

The ITE-operation: "If .. Then .. Else ..".

         ITE(${some.parameter.name} > 5,0,1)

 

First parameter is replaced, then checked first partner. Result (0) if parameter was > 5, (1) if not

 

To combine Boolean and arithmetic operations the operands have to be coded as (1) or (0), not as TRUE or FALSE where op > 0 is TRUE and 0 is FALSE.

 

 

Rule-Engine

 

net.simplace.simulation.io.resources.transformer.DefaultRuleTransformer

 

rule_engine1

 

Where in XML you have rule="the rule" Jexl is used.

All other places OperationTreeParser with Replacement Factory is used!

 

tNewParameter = tNewParameter.replace("_MODEL_DOY_", "CURRENT.DOY");

tNewParameter = tNewParameter.replace("_MODEL_DAY_", "CURRENT.DAY");

tNewParameter = tNewParameter.replace("_MODEL_MONTH_", "CURRENT.MONTH");

tNewParameter = tNewParameter.replace("_MODEL_YEAR_", "CURRENT.YEAR");

tNewParameter = tNewParameter.replace("=", "==");

tNewParameter = tNewParameter.replace("&", "&&");

tNewParameter = tNewParameter.replace("|", "||");

 

No use of !EXP:…! For rules any more.

 

Rule-Engine: Examples

 

Boolean expressions

 ${CURRENT.DOY}==${vIDPL} && ${Phenology.sDVS}>=2.1

Calculate simple result

 (${weather.AirTemperatureMax}+${weather.AirTemperatureMin})/2

Add explicitly values from arrays

 ${Hillflow1D.sTHETA[0]}+${Hillflow1D.sTHETA[1]}

Sum all values from an array

 var res=0;for (var item : ${Hillflow1D.sTHETA}){res = res + item;}; return res;

 

An example from HillflowTest.sol.xml:

 <out id="VolumetricWatercontentSumAll" datatype="DOUBLE" rule="var res=0;for (var item : ${Hillflow1D.sTHETA}){res = res + item;}; return res;"/>

 <out id="VolumetricWatercontentSum1_5" datatype="DOUBLE" rule="${Hillflow1D.sTHETA[0]}+${Hillflow1D.sTHETA[1]}+${Hillflow1D.sTHETA[2]}+${Hillflow1D.sTHETA[3]}+${Hillflow1D.sTHETA[4]}"/>

 

 

rule_engine2