Table of Contents

Good TOOL Coding Practices

Create time sequences dynamically

There are many ways to create a time sequence dimension in an object:

  1. myObj[t] = create (; dim=SEQ;time:1990:2000:1;year)
  2. myObj[t] = create (; dimFrom1=myTimeObj[t]), where dimension t in myTimeObj is the time sequence 1990 to 2000
  3. myObj[t] = create (; dim=SEQ;time:$startYear:$endYear:1;year), where $startYear=1990 and $endYear=2000

Method 1 causes problems if you later update the time sequence in the model. If the time sequence changes, all code that hard-coded references for the years in that sequence need to be updated in the procedure code, model views, and any TOOL scripts. For larger models, this can be cumbersome and error-prone.

Method 2 causes problems if you later change the dimensions in myTimeObj. For example, if you add a new dimension so to myTimeObj so that it is now myTimeObj[x,t], myObj in the above code would now be getting its dimension from x, which is now the first dimension, instead of t. This error would most likely be detected during dimensional analysis, but if it somehow were to pass dimensional analysis then the error would be much harder to detect. In either case, the code would have to be updated in the model procedures, model views, and TOOL scripts.

Method 3 is the recommended method for creating time sequences in an object. Use getobjinfo to derive the values for $startYear and $endYear as shown below

integer $startYear, $endYear
getobjinfo (myTimeObj[t], $startYear; info=sequenceStart, dim=time)
getobjinfo (myTimeObj[t], $endYear; info=sequenceEnd, dim=time)
myObj[t] = create (; dim=SEQ;time:$startYear:$endYear:1;year)

With this method changes to the time sequence will be automatically inherited because the sequence start and end years are derived dynamically. Also, this method will still work if new dimensions are added to myTimeObj because the getobjinfo statement explicitly looks for the dimension time regardless of whether it is the first dimension.

Consolidate Objects Using Sum Instead of Insert

Object consolidation is when multiple objects are combined into one along a certain dimension. In this example, we will create an object fuelUseAll[tm], where dimension tm is transportation mode (air, rail, car). Assuming that we have calculated the fuel use for each mode so that we have the objects airFuelUse[], railFuelUse[], and carFuelUse[], we can create fuelUseAll[tm] in two ways:

  1. fuelUseAll[tm] = insert (airFuelUse[]; transMode:air)
    fuelUseAll[tm] = insert (railFuelUse[]; transMode:rail)
    fuelUseAll[tm] = insert (carFuelUse[]; transMode:car)
  2. local airFuelUseAll[tm] = copyshape (fuelUseAll[tm])
    local railFuelUseAll[tm] = copyshape (fuelUseAll[tm])
    local carFuelUseAll[tm] = copyshape (fuelUseAll[tm])
    
    airFuelUseAll[tm] = insert (airFuelUse[]; transMode:air)
    railFuelUseAll[tm] = insert (railFuelUse[]; transMode:rail)
    carFuelUseAll[tm] = insert (carFuelUse[]; transMode:car)
    
    fuelUseAll[tm] = airFuelUseAll[tm] + railFuelUseAll[tm] + carFuelUseAll[tm]

Method 2 is the recommended method for consolidating objects. The main advantage is that with method 2 if airFuelUse[], railFuelUse[], or carFuelUse[] were empty objects, this would be detected when trying to view fuelUseAll[tm]. With method 1, an empty object would not necessarily be detected when viewing fuelUseAll[tm]. If railFuelUse[] were empty, the values for rail in fuelUseAll would just become 0 after carFuelUse[] is inserted so the empty object would go undetected.

Variable description and name conventions

Stub: best practices for variable naming, esp. locals FIXME

It is generally recommended that variable names use the lowerCamelCase convention. However, in some cases the use of underscores in conjunction with lowerCamelCase for coding clarity is appropriate (especially for procedures, views and source datasets).

Variable description modifiers should be used as suffixes, rather than prefixes (e.g. dwelling unit share rather than share of dwelling units). The same is true for variable names, but for these modifier abbreviations are recommended to keep name lengths manageable (e.g. dwellUnitShr). A table of suggested abbreviations is provided below:

Descriptor Abbreviation
share Shr
intensity Int
rate Rt
efficiency Eff
plan Plan
actual Act

Local variables in a procedure or view can proliferate as a result of reorder(), sum(), etc. operations. For code readability it is recommended that local variables created from a modelbase variables be named using the modelbase variable name followed by underscore and a meaningful descriptor. For example:

local population_tot[ts] = sum (population[s,ts,a]; dim1=sex, dim2=age)

Using underscores can also improve code readability when dealing with raw datasets, especially in calibrator frameworks (e.g. CANSIM_Manitoba_imm_1991_2006).