Overview
Constants and Lookup Tables are how you separate a model’s parameters from its logic. Instead of hardcoding values inside expressions, you name them, store them in one place, and reference them from anywhere. Change the named value once, and every expression that references it updates automatically. This matters for three reasons:- Tuning: adjust model parameters without hunting through expressions. Change
BASE_PROCESSING_TIMEfrom 60 to 75 and every relevant component follows. - Readability:
SHIFT_EFFICIENCY_FACTORis easier to understand than0.87sprinkled across expressions. - Non-technical access: operators and analysts who shouldn’t modify expressions can still tune the model by changing constant values.
Constants
A Constant is a named value with a specific type:number, text, or boolean. Each constant has:
- Name: how expressions reference it. Names use SCREAMING_SNAKE_CASE (e.g.,
BASE_CYCLE_TIME) - Description: what it represents and what units
- Type:
number,text, orboolean - Value: the actual data
Names auto-uppercase as you type. The name input converts what you type to SCREAMING_SNAKE_CASE: typing
base rate saves as BASE_RATE. Letters are uppercased and spaces become underscores. Reference the name as it ends up after the conversion.SIM_TIME or a function name), and constants and lookup tables can’t share a name with each other. Either conflict fails validation (for example, “Constant ‘SIM_TIME’ collides with reserved identifier.”). State variables can’t reuse a constant or lookup table name either.
Reference a constant by its name in any expression:
Constants vs. State Variables
Constants are static for the duration of a run: they cannot be reassigned. To mutate a value during simulation, use a State Variable instead. State variables expose a runtimeassign action that constants don’t.
The scope differs too: constants are factory-scoped and shared across every model that opts in, while state variables are model-scoped. A constant is the right tool for a parameter you tune between runs or share between models. A state variable is the right tool for a value the simulation itself changes: current shift, rolling WIP counter, demand signal.
When to Use a Constant
- Any value that appears in multiple places. If you find yourself typing
0.87in several expressions, extract it to a constant. - Any value you might want to change between runs. Edit the constant, run again, and compare the runs.
- Any value a non-technical stakeholder might need to adjust. Analysts can tune a constant without knowing the expression language.
Constants are factory-scoped. Changing one changes it for every model that uses it, including copies of a model. To compare values, run with one value, change it, run again, and compare the two runs.
Managing Constants
Constants appear in the Constants section of the Modeler’s left panel, in the Overview view. Each row shows a constant the model uses and its value.- + Add Constant at the bottom of the section adds an existing factory constant to this model, or choose Create new… to define one and add it in one step.
- The × on a row removes the constant from this model without deleting it. It’s disabled while the model still references the constant (“In use — remove its references first”).
- The section’s pencil icon (View and manage all) opens the Lookups dialog on its Constants tab, which lists every constant in the factory. Add, edit, or delete constants there, and use the In Model checkbox to add or remove one for the current model. The same dialog also has Lookup Tables, State Variables, and Topics tabs.
Lookup Tables
A Lookup Table is a named, typed-key table queryable in expressions. Each table has a value type (number, text, or boolean) and one or more keys (called “Attributes” in the UI — these are the table’s key columns, unrelated to entity attributes). Keys are themselves typed (each can be boolean, text, or number), so a multi-dimensional lookup can mix key types.
Query a table with LOOKUP:
Providing a Fallback Value
LOOKUP accepts an optional default:= keyword argument that returns the supplied value when no row matches:
default:= over wrapping in IF(): it’s a single expression with no double-evaluation of the same lookup.
Without
default:=, missing rows return type-specific zero: 0 for a numeric table, "" (empty string) for a text table, FALSE for a boolean table. That zero is silent and easily mistaken for a real result. Use default:= whenever the difference between “no row matched” and “value is exactly zero” matters.:=, not = (which means equality in the DSL). Despite the visual resemblance to Python’s walrus operator, := here is simply a named-argument marker, not an assignment. The same := marks the filter:= and type:= keyword args on aggregation functions and ndigits:= on ROUND.
Multi-Dimensional Lookups
A Lookup Table can be configured with more than one key column, so a single table can capture data that varies across two or more axes at once, like “processing time by (product type, station)” or “yield rate by (shift, material class).” Query it with one positional argument per key column, in the order the table defines them:LOOKUP calls match rows where all key columns equal the values you pass in.
One argument per key column. There’s no wildcard or partial-match syntax: always pass exactly as many keys as the table defines.
Composite keys via CONCAT. When a natural key is a compound string, build it in the expression rather than adding a column per fragment:
Managing Lookup Tables
Lookup tables appear in the Lookup tables section of the Overview view, right below Constants, and work the same way: + Add Lookup Table adds an existing factory table to this model (Create new… opens the dialog to define one), the × on a row removes it from this model, and the pencil icon opens the Lookups dialog on its Lookup Tables tab. In the dialog, each table has:- A Name (SCREAMING_SNAKE_CASE, converted as you type, like constant names)
- A Value Type: Number, Text, or Boolean
- One or more Attributes: the typed key columns, each with a name, a type, and its possible values
- Entries: one row per combination of key values, each with its value
When to Use a Lookup Table
- Values that vary by a categorical attribute. Processing time by product type, yield rate by material, priority weight by customer tier, all natural lookups.
- Routing and proportional splits. Different entities route differently; store the routing rules in a table rather than deeply nested
IFexpressions. - Data-driven configuration. When the “configuration” of a model is really a set of numbers tied to categories, a lookup table is clearer than encoding the same information across many fields.
Constants vs. Lookup Tables: Which to Use?
- Single value used in many places → Constant
- Value that depends on a categorical attribute → Lookup Table
- Needs to vary between comparison runs → Constant (one value per run)
- Large reference dataset → Lookup Table
Patterns
Processing time by product:Tips
- DSL arithmetic is type-strict. Numbers add to numbers; text doesn’t quietly coerce. Catch type mismatches at validation rather than relying on silent conversions.
- The value type must match the context that reads it. A
booleanconstant in a numeric expression, or atextlookup value feeding a numeric slot, fails validation with a type error — a common trip point after refactoring a table from number to text values. - Use
default:=defensively. If a missing lookup row should be anything other than the type’s zero,default:=is shorter and clearer than anIF()wrapper around twoLOOKUPcalls. - Each model opts in. Creating a constant or lookup table at the factory level is the first step. Adding it to the model (from the Overview section or the dialog’s In Model checkbox) decides whether that model sees it.