MN 113-9: Primmel Calculation Registry

#<Coradoc::CoreModel::CommentLine:0x00007fd9649f3270> #<Coradoc::CoreModel::CommentLine:0x00007fd9649f2dc0> #<Coradoc::CoreModel::CommentLine:0x00007fd9649f2b40> #<Coradoc::CoreModel::CommentLine:0x00007fd9649f2280> #<Coradoc::CoreModel::CommentLine:0x00007fd9649f1b00> #<Coradoc::CoreModel::CommentLine:0x00007fd9649f1330>

1. Calculations overview

1.1. General

Calculations are named, type-checked OCL computation primitives. They are referenced from form fields via the calculation property and the calculation_bindings block.

1.2. Why calculations exist

Without a calculation registry, OCL formulas are inlined wherever needed. For any standard with non-trivial calculations (OIML R 60 has ~30), this leads to:

  • The same formula copy-pasted across multiple forms
  • Typos in one copy that silently diverge from the others
  • No type checking on inputs
  • No unit checking on inputs and outputs
  • No reference traceability to the source formula

The calculation registry solves all five problems.

1.3. Relationship to other Primmel constructs

Construct Relationship
symbol Calculations reference symbols in their expression .
form Form fields invoke calculations via `calculation` and `calculation_bindings`.
measurement Calculations are similar to `measurement DERIVED` but with typed inputs and named reference. Use `measurement DERIVED` for inline shorthand; use `calculation` for reusable typed primitives.
validate_measurement A `validate_measurement` expression is an inline anonymous calculation with boolean output. It can be expressed as a named calculation when reuse is desired.

1.4. Identifier conventions

Calculation identifiers should be verbs or verb phrases that describe the operation: loadCellError , mean , repeatabilityError , humidityCHmin . They are PascalCase by convention.

2. Calculation syntax

2.1. General

A calculation declaration uses the calculation keyword followed by an identifier and a brace-delimited body.

2.2. Syntax

calculation <CalculationID> {
  name "Display name"
  description "..."
  inputs {
    <param> : <type> {
      unit "<unit>"
      description "..."
    }
  }
  output : <type> {
    unit "<unit>"
  }
  expression "ocl{...}"
  reference { <ReferenceID> }
}
  1. Display name. Required.
  2. Free-text description. Optional.
  3. Typed inputs. Zero or more (calculations can be nullary constants).
  4. Typed output. Required.
  5. OCL expression. Required.
  6. Normative references. Optional.

2.3. Properties

Property Required Description
name yes Display name.
description no Free-text description.
inputs no Typed input parameters. Zero or more.
output yes Typed output.
expression yes OCL expression that computes the output from the inputs.
reference no Normative references for traceability.

2.4. Example: OIML R 60 calculations

calculation loadCellError {
  name "Load cell error"
  description "Signed difference between observed and reference indication, divided by conversion factor."
  inputs {
    avgIndication : number { unit "counts" }
    referenceIndication : number { unit "counts" }
    conversionFactor : number { unit "counts/v" }
  }
  output : number { unit "v" }
  expression "ocl{(avgIndication - referenceIndication) / conversionFactor}"
  reference { R60-3-2-1-2-2 }
}
calculation repeatabilityError {
  name "Repeatability error"
  description "Max minus min of a list of values."
  inputs {
    values : list<number> { unit "1" }
  }
  output : number { unit "1" }
  expression "ocl{values->max() - values->min()}"
}
calculation creep {
  name "Creep error"
  description "Change in indication from initial D_max loading, divided by conversion factor."
  inputs {
    indicationAtTime : number { unit "counts" }
    initialIndication : number { unit "counts" }
    conversionFactor : number { unit "counts/v" }
  }
  output : number { unit "v" }
  expression "ocl{(indicationAtTime - initialIndication) / conversionFactor}"
  reference { R60-3-2-1-5 }
}

2.5. Nullary calculations (constants)

A calculation may have zero inputs. This represents a typed constant:

calculation halfVFixed {
  name "Fixed 0.5 v threshold"
  description "Dead load output return limit per R 60-1 §5.5.2."
  output : number { unit "v" }
  expression "ocl{0.5}"
  reference { R60-1-5-5-2 }
}

3. Inputs and outputs

3.1. General

Calculation inputs and outputs are typed parameters with optional unit annotations and descriptions.

3.2. Input syntax

inputs {
  <param> : <type> {
    unit "<unit>"
    description "..."
    default <value>
  }
}
  • <param> — parameter name, used in the OCL expression
  • <type> — number, integer, string, boolean, list<<type>>
  • unit — unit annotation; defaults to ” 1 “ (dimensionless)
  • description — free-text description
  • default — optional default value

3.3. Output syntax

output : <type> {
  unit "<unit>"
}

Output is singular (one per calculation).

3.4. Type checking

At parse time, the parser validates:

  • Every input referenced in the expression is declared
  • Every declared input is used in the expression (warning, not error)
  • The output type is consistent with what the expression produces

3.5. Unit checking

At parse time, the parser validates:

  • Unit compatibility between inputs and output (where derivable from the expression)
  • Unit compatibility between form field bindings and calculation input units

3.6. List types

Inputs and outputs can be list-typed:

inputs {
  values : list<number> { unit "counts" }
  threshold : number { unit "v" }
}

Use list types for aggregations (mean, max, min, sum, count).

3.7. Optional inputs

If an input has a default value, the binding is optional at the call site. Otherwise, all inputs must be bound.

inputs {
  required_param : number { unit "v" }
  optional_param : number { unit "1" default 1.0 }
}

At the call site:

calculation_bindings {
  required_param: some_field        // required
  // optional_param omitted → uses default 1.0
}

4. OCL expressions

4.1. General

Calculation expression values are written in a subset of Object Constraint Language (OCL). This section documents the supported subset.

4.2. Literals

expression "ocl{42}"             // numeric literal
expression "ocl{3.14}"
expression "ocl{true}"
expression "ocl{false}"
expression "ocl{\"hello\"}"      // string literal (escaped quotes)

4.3. Identifier references

Identifiers inside an OCL expression reference either:

  • Calculation inputs (by name)
  • Symbols (via [SymbolID] syntax)
  • Other calculations (via calculationID(...) syntax — future extension)
expression "ocl{(avgIndication - referenceIndication) / conversionFactor}"
//   ^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^
//   inputs
expression "ocl{[E_L] <= [MPE]}"
//          ^^^^      ^^^^
//          symbols

4.4. Arithmetic operators

Expr = Expr "+" Expr
      | Expr "-" Expr
      | Expr "*" Expr
      | Expr "/" Expr
      | Expr "mod" Expr
      | Expr "div" Expr
      | "-" Expr
      | Atom ;

4.5. Comparison operators

Expr = Expr "<" Expr
      | Expr "<=" Expr
      | Expr ">" Expr
      | Expr ">=" Expr
      | Expr "=" Expr
      | Expr "<>" Expr ;

4.6. Boolean operators

Expr = Expr "and" Expr
      | Expr "or" Expr
      | Expr "xor" Expr
      | "not" Expr
      | Expr "implies" Expr ;

4.7. Path expressions

expression "ocl{runs.indication}"          // array of indications
expression "ocl{runs->first().indication}" // first run's indication
expression "ocl{runs->last().time}"        // last run's time

4.8. Collection operations

Expr = Expr "->" "size" "(" ")"
      | Expr "->" "sum" "(" ")"
      | Expr "->" "mean" "(" ")"
      | Expr "->" "min" "(" ")"
      | Expr "->" "max" "(" ")"
      | Expr "->" "count" "(" ")"
      | Expr "->" "first" "(" ")"
      | Expr "->" "last" "(" ")"
      | Expr "->" "forAll" "(" Var "|" Expr ")"
      | Expr "->" "exists" "(" Var "|" Expr ")"
      | Expr "->" "collect" "(" Var "|" Expr ")"
      | Expr "->" "select" "(" Var "|" Expr ")"
      | Expr "->" "reject" "(" Var "|" Expr ")" ;

4.9. Functions

Expr = "abs" "(" Expr ")"
      | "sqrt" "(" Expr ")"
      | "pow" "(" Expr "," Expr ")"
      | "min" "(" Expr "," Expr ")"
      | "max" "(" Expr "," Expr ")"
      | "mean" "(" Expr { "," Expr } ")" ;

4.10. Conditional

Expr = "if" Expr "then" Expr "else" Expr "endif" ;

4.11. Example

expression "ocl{if values->size() > 0 then values->sum() / values->size() else 0 endif}"

4.12. Out of scope

The following OCL features are NOT required for Primmel 1.0:

  • Tuple types
  • Set/Bag/OrderedSet distinctions (we use list for all collections)
  • Iterate expressions (use the named aggregates instead)
  • Message expressions

These may be added in future versions if use cases emerge.

5. Binding resolution

5.1. General

When a form field references a calculation via the calculation property, the calculation_bindings block maps the calculation's named inputs to form fields or sub-paths.

5.2. Binding syntax

field error_EL : number {
  unit "v"
  measurement_method computed
  calculation loadCellError
  calculation_bindings {
    avgIndication: average_indication
    referenceIndication: reference_indication
    conversionFactor: f
  }
}

Each binding entry is <input_name>: <path_expression> . The <input_name> must match a declared input on the calculation.

5.3. Path expressions

A path expression is ID { "." ID } — one or more identifiers joined by dots.

  • f — references the field f (or symbol f) in the current form
  • runs.indication — references the indication field of each item in the runs array
  • temperature_series->first().test_loads->last().indication — deeply nested path

5.4. Type validation

At parse time, the parser validates:

  • Every calculation input has a binding (or a default)
  • Every binding points to an existing field or symbol
  • The bound field's type matches the calculation input's type
  • The bound field's unit is compatible with the calculation input's unit

5.5. Unit compatibility

Units are compatible when:

  • They are equal
  • One is ” 1 “ (dimensionless) and the other is anything
  • They differ only by SI prefix ( kg ↔ g)

Otherwise the binding is rejected.

5.6. Missing bindings

If a calculation input has no binding and no default, the parser emits an error:

calculation loadCellError input "conversionFactor" has no binding
and no default

5.7. Default values

If an input has a default , the binding is optional. The default is used when no binding is provided.

5.8. Example: complete binding

Given the calculation:

calculation loadCellError {
  inputs {
    avgIndication : number { unit "counts" }
    referenceIndication : number { unit "counts" }
    conversionFactor : number { unit "counts/v" }
  }
  output : number { unit "v" }
  expression "ocl{(avgIndication - referenceIndication) / conversionFactor}"
}

And the form:

field f : number {
  unit "counts/v"
  measurement_method declared
}
field average_indication : number {
  unit "counts"
  measurement_method derived
  derivation "ocl{mean(runs.indication)}"
}
field reference_indication : number {
  unit "counts"
  measurement_method computed
  calculation referenceIndication
  calculation_bindings { /* ... */ }
}
field error_EL : number {
  unit "v"
  measurement_method computed
  calculation loadCellError
  calculation_bindings {
    avgIndication: average_indication
    referenceIndication: reference_indication
    conversionFactor: f
  }
}

The parser:

1. Looks up loadCellError — found 2. Validates bindings: avgIndication , referenceIndication , conversionFactor all declared inputs 3. Validates types: all number (matches) 4. Validates units: counts / counts / counts-v (matches counts / counts / counts-v ) 5. Field error_EL gets type number , unit v from calculation output

6. Built-in library

6.1. General

Primmel ships with a standard library of calculations available to all models without explicit declaration. These correspond to OCL operators and provide common aggregates.

6.2. Aggregates

Calculation Signature Description
size list<T> → integer Number of items in the list.
sum list<number> → number Sum of all items.
mean list<number> → number Arithmetic mean (sum / size).
min list<number> → number Minimum value.
max list<number> → number Maximum value.
count list<T> → integer Same as size (alias).
first list<T> → T First item.
last list<T> → T Last item.

These are accessed via the -> operator on collections:

expression "ocl{runs.indication->mean()}"
expression "ocl{runs.indication->max() - runs.indication->min()}"

6.3. Functions

Function Signature Description
abs number → number Absolute value.
sqrt number → number Square root.
pow (number, number) → number Exponentiation.
min (number, number) → number Minimum of two values.
max (number, number) → number Maximum of two values.

These are accessed as function calls:

expression "ocl{abs(error_EL)}"
expression "ocl{pow(conversionFactor, 2)}"

6.4. Quantifiers

Quantifier Signature Description
forAll (list<T>, T → boolean) → boolean True if predicate holds for every item.
exists (list<T>, T → boolean) → boolean True if predicate holds for at least one item.
collect (list<T>, T → U) → list<U> Map each item through a function.
select (list<T>, T → boolean) → list<T> Filter items by predicate.
reject (list<T>, T → boolean) → list<T> Inverse of select .
expression "ocl{runs->forAll(r | r.indication <= threshold)}"
expression "ocl{runs->exists(r | r.indication > threshold)}"
expression "ocl{runs->collect(r | r.indication)->sum()}"

6.5. Overriding built-ins

A model may declare its own calculation with the same name as a built-in. The model's declaration takes precedence within that model. A warning is emitted.

7. Formal grammar

7.1. Calculation declaration

CalculationDecl = "calculation" ID "{" { CalculationProperty } "}" ;
CalculationProperty = "name" STRING
                    | "description" STRING
                    | "inputs" "{" { InputDecl } "}"
                    | "output" ":" TypeSpec "{" { OutputProperty } "}"
                    | "expression" STRING
                    | "reference" "{" { ID } "}" ;
InputDecl    = ID ":" TypeSpec "{" { InputProperty } "}" ;
InputProperty = "unit" STRING
               | "description" STRING
               | "default" Value ;
OutputProperty = "unit" STRING
               | "description" STRING ;
TypeSpec     = BaseType [ "<" TypeSpec ">" ]
             | "list" "<" TypeSpec ">" ;
BaseType     = "number" | "integer" | "string" | "boolean" | "enum" | ID ;

7.2. Lexical conventions

As per the forms grammar (see formal-grammar-forms ).