Name

repeat — repeat the enclosed block with variable substitution

Synopsis

Content Model

   <repeat> = (repeat | curve | curvetable | table | define | select | attribute | treatment | assign )+.  
    

Attributes

NameTypeDefault
namethe name to be used for the substitution variableRequired
lista list of values to iterate overOptional
fromthe starting value for a numeric iterationOptional
tothe ending value for a numeric iterationOptional
bythe step size for a numeric iterationOptional
tablethe name of a lookup tableOptional
selecta string containing a selection expressionOptional

Description

<repeat> elements allow for the repetition of the enclosed <select> statements with variable substitutions. The <repeat> element specifies values to iterate over, either as a list of items, a list of records from a table, or as a numeric iteration similar to a do loop in other programming languages. The <repeat> element will first capture all enclosed elements. The <repeat> directive will iterate over all of the repetition values, perform variable substitutions on the enclosed elements, and provide the results to the Matrix Builder for processing.

Variable substitution may occur in any place within the <repeat> element where expressions are allowed. The substitution is invoked by using the getEnv function, The value passed to the function is the literal 'repeat.' and the name attribute of the enclosing <repeat> element (for example if the name attribute of the <repeat> element is 'basis' then the argument to the getEnv function would be 'repeat.basis').

The <repeat> element is a convenience feature, allowing a concise definition of a set of enclosed elements that vary in only a few respects.

<repeat> elements may be nested, allowing for the expression of all combinations of multiple factors.

[Note]Note

<repeat> elements provide two significant benefits for modelers. The first is the reduction in the amount of code that needs to be written to express the model. The common code within the <repeat> blocks only needs to be written once and is thus less prone to copy/paste errors that often occur. Having fewer lines makes code review and quality control easier and reduces programmer fatigue.

Secondly, and perhaps more valuable, <repeat> elements can make use of values from the input data sets to configure the model to match the dataset. For example, the curveName() and filter() functions can be used to populate a list of values that can be used to create attributes. ForestModels can be set up as self-configuring templates that adapts to changing input datasets, leading to reduced set up costs and increased model translation reliability.

Parents

These elements contain repeat: ForestModel, repeat, select, features, products, transition, produce, track.

Children

The following elements can occur in repeat: repeat, curve | curvetable | table | define | select | attribute | treatment | assign | succession.

Attributes

The <repeat> element must include a 'name' attribute and one set of attributes that describe the source of the repetition values: either list; from, to, by; or table, select.

name

This attribute provides an alphanumeric name label that can be used to reference the iteration value of the repeat loop. The name being used must be unique within the scope of all enclosing and enclosed repeat blocks.

list

This attribute provides a list of values to be iterated over. The value provided will be interpreted as an expression that will be evaluated when the looping begins. If the result of the evaluation is a list data type then the elements of the list will be used. If the result of the evaluation is a string data type then the string will be split on comma characters and the loop will iterate over the resulting string parts. To convert the string parts to a number use one of the casting conversion functions (int() or number()).

from, to, by

The from attribute provides an expression that evaluates to the starting value of a numeric looping iteration.

The to attribute provides an expression that evaluates to the ending (inclusive) value of a numeric looping iteration.

The by attribute provides an expression that evaluates to the step size value that is used to increment through a numeric iteration. If not specified the default value of the 'by' attribute is 1.

table, select

The table attribute is a literal string that references the name of a previously defined lookup table.

The select attribute is optional, and if specified is a string containing a Patchworks Query Language expression that is evaluated in the context of the lookup table. The expression will be evaluated against each row in the table. Only rows where the expression evaluates to true will be used.

It is an error to specify attributes from more than one of the above repetition sources.

Repetition values

The value of the repetition value depends on the repetition source. When using a list source the repetition value will be each successive value from the list, in the data type of that list. If using a numerical loop repetition source (from, to , by) the repetition value will be the numeric value of the loop index.

If using a table source the repetition value is the numeric value of the current lookup table row number. In addition the environment will be populated with the cell values from the selected rows. These values can be retrieved from within the <repeat> block with the getEnv() function using an argument that is the word 'repeat' followed by the repeat block name followed by the lookup table column, each element separated with a period. For example,

getEnv('repeat.blocks.MartStrata')

Examples

Example 1

An existing model has a repetitious definition of mappings between external ind internal variables.

  <define field="theme1" column="Upper(String(THEME1))" />
  <define field="theme2" column="Upper(String(THEME2))" />
  <define field="theme3" column="Upper(String(THEME3))" />
  <define field="theme4" column="Upper(String(THEME4))" />
  <define field="theme5" column="Upper(String(THEME5))" />

We can use the tablelist() function to retrieve the names of all columns from the input fragments file. These are filtered to select desired values and the enclosed <define> element creates the required mappings.

 <repeat name="theme" list="sort(filter(tablelist('fragments'),'THEME(\d+)'))">
  <define field="lower(getEnv('repeat.theme'))" column="Upper(String(column(getEnv('repeat.theme'))))" />
 </repeat>

This may seem like a trivial usage for this simple model, but this represents a very goo design for more complex models having dozens of stratification variables, possibly changing between estates.

Example 2

An existing model had a repetitious set of attribute definitions. The coding is the same other than the attribute suffix.

  <select statement="theme5 ne 'NS'">
    <features>
      <attribute label="%f.Seral.regen">
        <expression statement="curveId(theme5+'.Seral.regen')" by="1" ignoreMissingAttributes="false"/>
      </attribute>
      <attribute label="%f.Seral.young">
        <expression statement="curveId(theme5+'.Seral.young')" by="1" ignoreMissingAttributes="false"/>
      </attribute>
      <attribute label="%f.Seral.mature">
        <expression statement="curveId(theme5+'.Seral.mature')" by="1" ignoreMissingAttributes="false"/>
      </attribute>
      <attribute label="%f.Seral.old">
        <expression statement="curveId(theme5+'.Seral.old')" by="1" ignoreMissingAttributes="false"/>
      </attribute>
    </features>
  </select>

We can replace these repetitive attribute definitions with a loop:

  <select statement="theme5 ne 'NS'">
    <features>
     <repeat name="seral" list="unique(filter(curveNames(),'.*Seral.(.*)','{1}'))">
      <attribute label=" '%f.Seral.'+getEnv('repeat.seral')">
        <expression statement="curveId(theme5+'.Seral.'+getEnv('repeat.seral'))" by="1" ignoreMissingAttributes="false"/>
      </attribute>
     </repeat>
    </features>
  </select>

Once again this may seem trivial, but useful for complex models with many attribute values or where the set of attribute values may change as the project evolves.

Example 3

In the following example thinning can occur from ages 15 to 30 inclusive. All stands in the 'Unthinned' status are eligible. This loop will set the set the treatment name and the minimum and maximum operability ages. The treatment field is assigned a code indicating the age of the thinning which will be used to determine the thinning yields, costs and other characteristics. The status field is updated with a status condition code indicating the age of the thinning, which will be used to determine the next eligible treatment.

The <repeat> block wraps the <treatment> element. The getEnv function is used to retrieve the loop value and substitute it the appropriate locations within the <treatment> and other enclosed elements. Without the <repeat> loop all of the enclosed elements would have to be repeated 16 times, leading to a greater opportunity for copy/paste errors.

  <select statement="strata in Act and status in 'Unthinned'">
    <track>
     <repeat name='age' from='15' to='30'>
      <treatment label="'Thin'+getEnv('repeat.age')" 
                 minage="getEnv('repeat.age')" maxage="getEnv('repeat.age')">
        <produce>
          <assign field="treatment" value="'Thin'+getEnv('repeat.age')"/>
        </produce>
        <transition>
          <assign field="status" value="'Thinned'+getEnv('repeat.age')"/>
        </transition>
      </treatment>
     </repeat>
    </track>
  </select>

Example 4

This next example is complementary to the previous. A mid-rotation fertilization treatment may be applied one year after thinning, from ages 8 to 18. This loop will set the select condition to stands with a status of being thinned in the designated year, set the operability limits to the next year after the thinning, and update the status to indicate that the thinning occurred.

The repeat loop wraps the <select> block so that the indexing value can be used in the select statement. The getEnv function is used to retrieve the loop value and substitute it the appropriate locations. Without the <repeat> loop all of the enclosed elements would have to be repeated 11 times.

 <repeat name="age" from="8" to="18">
  <select statement="status in 'Thinned'+getEnv('repeat.age') and regime in FertYes">
    <track>
      <treatment label="MRFert" minage="getEnv('repeat.age')+1" 
                 maxage="getEnv('repeat.age')+1" adjust="R" retain="5">
        <produce>
          <assign field="treatment" value="'MRFert'"/>
        </produce>
        <transition>
          <assign field="status" value="status+'FERT'"/>
        </transition>
      </treatment>
    </track>
  </select>
 </repeat>

Example 5

In this example two nested <repeat> elements iterate over the variables 'treat' and 'start'. The values retrieved by the getEnv function are used separately and in combination within the enclosed elements.

In the select statement the two values are used in combination to create the name of a stratification variable. This name is formed as a string (not a variable name), so we use the column function lookup the stratification variable matches this string return it's value.

In this simplified example the inner statements are repeated 12 times (factoring 4 for the outer loop times 3 for the inder loop), so the savings are considerable. The real life case that this example was extracted from was actually a 9 x 4 factors, so the savings can be substantial.

<repeat name="treat" list="'U120075,U130085,U140095'" >
 <repeat  name="start" from="1" to="4">
  <select statement="column('f'+getEnv('repeat.treat')+'_'+getEnv('repeat.start')) ge 1">
    <track>
      <treatment label="getEnv('repeat.treat')+'_'+getEnv('repeat.start')" adjust="R">
        <produce>
          <assign field="activity" value="getEnv('repeat.treat')"/>
        </produce>
        <transition>
          <assign field="Regime" value="getEnv('repeat.treat')+'_'+getEnv('repeat.start')"/>
        </transition>
      </treatment>
    </track>
  </select>
 </repeat>
</repeat>