For the complete documentation index, see llms.txt. This page is also available as Markdown.

Parametric Engine Guide

Overview

OpenBrIM is a cloud-based parametric bridge information modeling platform with an extensible ecosystem where bridge components and concepts are defined in a parametric way and assembled like LEGO pieces to represent bridge projects (3D views, finite element analysis models, CAD drawings, design documents, reports, and more).

This guide explains the core parametric engine for AI agents who will create XML-based components and models using this system.

Core Concepts

1. Objects (Obj)

Objects are the fundamental building blocks in the system. Every component, structure, or entity is represented as an object.

Key characteristics:

  • Every object has a unique ID (GUID)

  • Every object has a Name (N attribute)

  • Every object has a Type (T attribute) that defines its class or base object

  • Objects can contain parameters and child objects

  • Objects form a hierarchical tree structure through parent-child relationships

  • Objects can inherit from other objects by using that object's name as their Type

1.1 Library Objects

Library objects are reusable template objects that other objects can inherit from. They follow a specific pattern:

Library Object Structure:

Key Attributes:

  • N: The library object name (used by other objects to inherit from this)

  • T="Project": Marks this as a library/template object

  • Tags: Classification tags for organizing and filtering library objects

    • @secondlayer: Visibility layer in UI

    • usastandard, steelIGirder, release: Descriptive tags

  • ObjLabel: Human-readable display name

  • Category: Organizational category (uses : for subcategories)

Using Library Objects (Inheritance):

Other objects reference library objects by setting their T attribute to the library object's N value:

How Inheritance Works:

  1. The child object (T="OBP_GirderStressSummaryTable") inherits all parameters and structure from the library object

  2. Child objects can override inherited parameter values

  3. Child objects can add new parameters not in the library object

  4. Changes to the library object automatically affect all instances (unless overridden)

Common Library Object Categories:

  • Code Check Objects: Design code compliance checks

  • Report Objects: Report templates and layouts

  • Component Objects: Reusable structural components (beams, columns, connections)

  • Material Objects: Material property definitions

  • Section Objects: Cross-section definitions

Example: Creating a Custom Library Object

1.2 Export Objects

Export objects are containers within library objects that expose computed parameters and generated child objects to instances. Parameters placed inside <O T="Export"> containers become accessible to other objects.

Purpose:

  • Export computed/calculated parameters from library objects to instances

  • Make generated child objects (geometry, components, etc.) accessible

  • Share internal calculations with other objects in the project

  • Parameters with Role="Input" are automatically exported - no need to add them to Export objects

Important Dependency Rule: If an exported parameter depends on other parameters, those dependencies must also be exported OR have Role="Input". Parameters without a role (private by default) cannot be accessed by exported parameters.

Export Object Structure:

Complete Example:

How Export Works:

  1. Input Parameters (Role="Input"):

    • Automatically exported to instances

    • Users can modify these values

    • No need to include in Export objects

    • Can be referenced by parameters inside Export objects

  2. Export Container (<O T="Export">):

    • Parameters inside are accessible to other objects

    • Computed from Input parameters or other exported params

    • Read-only in instances (calculated from library)

    • Child objects (geometry, etc.) inside are also exported

  3. Private Parameters (no role):

    • NOT accessible to Export parameters

    • Cannot be used as dependencies for exported calculations

    • For internal use only within the same scope

Using Exported Values:

When you create an instance of a library object, exported parameters become accessible:

Best Practices:

  1. Ensure Proper Dependencies:

    • Exported parameters can ONLY depend on: Role="Input" params OR other exported params

    • If you need intermediate calculations, put them inside the Export object

  2. Don't Export Inputs:

    • Input parameters are already exported automatically

    • Only use Export objects for computed/calculated results

  3. Name Export Objects for Override:

    • Give Export objects a name (e.g., <O N="Results" T="Export">) when you want child objects to override them

    • Named Export groups can be overridden using Override="1" in extended objects:

  4. ⚠️ CRITICAL: Never Add Guard Directly to T="Export" Objects:

    Problem: When Guard is added directly to T="Export" objects in library objects, the Guard appears on the library object instance in the source code, which can cause the entire object to not execute.

    Solution: Add Guard to a nested Group within the Export object:

    Rule: Always wrap guarded parameters in a nested T="Group" within the Export object.

For more details: See AI-Export.md


1.3 DesignRun Objects

DesignRun objects are bridge objects that override parameters of a target library object specified by LibObjTypeName. They work similarly to Extends + Override="1", but with a critical restriction: only parameters that exist within the LibObjTypeName object can be overridden (scoped override, not hierarchical).

Purpose:

  • Override parameters of a specific library object (scoped to that object only)

  • Act as a bridge between user-defined parameters and library object parameters

  • Execute design calculations with custom parameter values

Key Parameters:

  • LibObjTypeName (CRITICAL): Specifies which library object's parameters to override

  • LibObjVersion: Version number of the target library object

Key Restrictions:

  1. Parameters only - Cannot override child objects (cannot use Override="1" on objects)

  2. Must pre-exist - Can only override parameters that already exist within LibObjTypeName scope

  3. Scoped to LibObjTypeName - Overrides parameters within that library object and its extended objects

  4. Includes extended objects - If LibObjTypeName has child objects using Extends, their parameters can also be overridden

Common Practice:

  • Typically override Role="Input" parameters - These are data inputs from users

  • Rarely override calculated parameters - If logic needs changing, modify the base library object instead

  • Rationale: DesignRun is for data override, not logic override

DesignRun Object Structure:

How DesignRun Works:

  1. DesignRun specifies target library object via LibObjTypeName

  2. Parameters in DesignRun override corresponding parameters within that library object's scope

  3. Extended objects included - Parameters from objects extended within LibObjTypeName can also be overridden

  4. If the same parameter name appears multiple times in the library scope, all instances are overridden

  5. Library object uses the overridden parameter values in its calculations

  6. Only parameters that exist in LibObjTypeName scope can be overridden (no new parameters)

CRITICAL: Bidirectional Parameter Synchronization

DesignRun overrides are scoped to the LibObjTypeName object only (unlike Extends + Override="1" which is hierarchical). This means parameter names must match between the library object and DesignRun. When you rename a parameter:

Direction 1: Library → DesignRun If you rename a parameter in the library object that is overridden by DesignRun:

  1. Find all DesignRun objects with LibObjTypeName pointing to that library

  2. Update the parameter name in those DesignRun objects

Direction 2: DesignRun → Library If you rename a parameter in DesignRun that overrides a library parameter:

  1. Check the library object specified in LibObjTypeName

  2. Update the parameter name in that library object

  3. Update all internal references to that parameter in the library

Example 1: Scoped Override (Only LibObjTypeName)

Example 2: Overriding Extended Object Parameters

Example 3: Renaming Parameters

Step-by-Step: Renaming Parameters

When you need to rename a parameter that's involved in DesignRun override:

Starting from Library Object:

  1. Identify the library object (e.g., OBPBase_MyCodeCheck)

  2. Change the parameter name in the library object (e.g., oldNamenewName)

  3. Update all internal references to that parameter within the library

  4. Search for all DesignRun objects with LibObjTypeName = "OBPBase_MyCodeCheck"

  5. Update parameter names in those DesignRun objects to match the new name

Starting from DesignRun Object:

  1. Find LibObjTypeName value to identify the target library object

  2. Change the parameter name in DesignRun (e.g., oldNamenewName)

  3. Open the library object specified in LibObjTypeName

  4. Update the parameter name in the library object

  5. Update all internal references to that parameter within the library

⚠️ WARNING: Non-Input Parameter Override Behavior

DesignRun has a dangerous capability that must be carefully controlled:

The Problem: DesignRun can override ANY parameter in the LibObjTypeName scope, not just Role="Input" parameters. When you override a non-Input parameter:

  • ALL occurrences of that parameter name in the library object scope are overridden

  • This includes uses in calculations, expressions, and child objects

  • The override affects the entire LibObjTypeName object and its extended objects

Example - Unintended Override:

Result:

  • width_input = 15 (from UserWidth) ✅ Expected

  • safety_factor = 2.0 (from UserSafetyFactor) ⚠️ Overridden everywhere

  • capacity = (15 * 20) * 2.0 = 600 (uses overridden safety_factor)

  • check1 = 600 * 2.0 = 1200 (uses overridden safety_factor)

  • check2 = 15 * 2.0 = 30 (uses overridden safety_factor)

When This Happens:

  • Breaks library object's internal logic - calculated parameters no longer use intended values

  • Creates maintenance nightmare - changes to library don't behave as expected

  • Violates separation of concerns - DR should only provide input data, not override calculations

  • Hard to debug - override affects multiple locations silently

  • Unexpected behavior - other developers don't expect internal parameters to be overridden

What To Do Instead:

If user needs to control a parameter that's not Role="Input":

  1. Add Role="Input" to that parameter in the library object (RECOMMENDED)

    • Makes it officially part of the library object's API

    • Clear to all developers that this parameter is meant to be configurable

    • Proper design pattern - data inputs belong in Role="Input"

  2. If parameter should remain internal:

    • Do NOT override it from DesignRun

    • Modify library object logic if behavior needs to change

    • Keep data override (DR) separate from logic (library)

Comparison:

Aspect

Role="Input" Parameter

Non-Input Parameter

Purpose

User-configurable data input

Internal constant/calculation

Recommended for DR?

YES - primary use case

NO - problematic

Override behavior

Standard, expected

Overrides ALL occurrences

Best practice

Always map these in DR

NEVER override from DR

User expectation

Should be controllable

Should be internal/fixed

When found in DR

Normal, correct usage

Flag as error in code review

Best Practices:

  1. Parameters only, not objects - DesignRun can only override parameters, not child objects

  2. Must pre-exist in library scope - Cannot add new parameters; can only override existing ones (including extended objects)

  3. Scoped to LibObjTypeName - Overrides parameters within library object and its extended objects

  4. Extended objects included - Parameters from Extends objects within library can be overridden

  5. ONLY override Role="Input" parameters - These are data inputs designed to be overridden

  6. NEVER override non-Input parameters - This breaks library logic and creates bugs

    • ⚠️ If you find a non-Input override in DR: Add Role="Input" to library or remove from DR

  7. Multiple same-name parameters - If the library has the same parameter name multiple times, all are overridden

  8. Data override, not logic override - Use DesignRun for data inputs, use library edits for logic changes

  9. Bidirectional sync is critical - Changes in either location require updates in the other

  10. Always check LibObjTypeName to find which library object is being overridden

  11. Check extended objects too - If library uses Extends, those parameters can also be overridden

  12. Search across multiple files - DesignRun objects may be in different files than the library

  13. Test after renaming to ensure overrides still work correctly

  14. Code review must flag non-Input overrides - These are bugs that need fixing

2. Parameters (Param)

Parameters store data and expressions within objects. They are the "DNA" of parametric behavior.

Parameter Types:

  • Number (ParamType.Number): Numeric values or mathematical expressions

  • Text (ParamType.Text): String values or object references

Value Types:

  • Expression (ValueType.Expression): Contains formulas that are evaluated (e.g., "L/2 + 10")

  • Value (ValueType.Value): Contains literal values (e.g., 100)

Parameter Properties:

  • Name (N): Parameter identifier

  • Value (V): The expression or value

  • Type (T): Expr, Num, Text, or ObjectType (for references)

  • Description (D): Human-readable description

  • UnitType (UT): Length, Force, Stress, etc.

  • UnitCategory (UC): Default, Metric, Imperial, etc.

  • Role: None, Input, Private, Export, Import

  • Category: For grouping parameters in UI

  • DisplayWidth: UI display width

  • Attr: If "1", parameter is serialized as XML attribute (for brevity)

  • Optional: If "1", parameter is optional

3. Expression Evaluation (Eval)

The evaluation engine computes parameter values by:

  1. Parsing expressions using JavaScript-compatible syntax

  2. Resolving identifiers to their values from the parametric model

  3. Executing mathematical operations and functions

  4. Caching results for performance

Supported Operations:

  • Arithmetic: +, -, *, /, %, ^

  • Comparison: <, >, <=, >=, ==, !=

  • Logical: &&, ||, !

  • Ternary: condition ? value1 : value2

  • Mathematical functions: sqrt, abs, sin, cos, tan, asin, acos, atan, atan2, min, max, pow, exp, log, floor, ceil, round, etc.

Identifier Resolution:

  • Parameters are referenced by name (e.g., Length, Width)

  • The system uses a distance-based algorithm to resolve identifiers (detailed below)

  • Objects are referenced by name and accessed with dot notation (e.g., Girder1.Length)

  • Use brackets for indexed access (e.g., Girders[0].Width)

3.1 Lazy Evaluation

The parametric engine uses lazy evaluation, meaning expressions are not computed when they are defined—they are evaluated only when their values are actually needed.

How It Works:

  1. Definition Time: Expressions are stored as text strings and compiled into an Abstract Syntax Tree (AST)

  2. Evaluation Time: When a parameter value is requested (e.g., for display, geometry generation, or use in another expression), the GetValue() method is called

  3. Backward Evaluation: The engine works backward through dependencies—if ParamA needs ParamB, it evaluates ParamB first, which might need ParamC, and so on

  4. Caching: Computed values are cached to avoid redundant calculations. The cache is invalidated when dependencies change

  5. Circular Dependency Detection: The execution stack tracks the evaluation chain to detect and prevent circular dependencies

Benefits:

  • Performance: Only computes what's actually needed

  • Flexibility: Parameters can reference objects/parameters that are defined later in the XML

  • Dynamic Updates: When an input parameter changes, only affected downstream parameters are re-evaluated on next access

Example:

Evaluation Chain Example:

3.2 Distance-Based Parameter Resolution

When multiple parameters or objects have the same name, the engine uses a distance-based algorithm to determine which one to use. This allows parameters to be overridden at different levels of the hierarchy.

Resolution Algorithm:

When an expression references an identifier (e.g., Width or Beam1):

  1. Search Parent Chain First: Walk up from current object to root, looking for the parameter

    • Current object's parameters

    • Parent's parameters

    • Grandparent's parameters, etc.

    • Stop if found or root is reached

  2. Search Entire Project (if not found in parent chain): Get ALL parameters/objects with that name from the entire project

  3. Calculate Distance for each candidate:

  4. Pick Closest: Select the parameter/object with the minimum distance

Example: Basic Distance Resolution

Example: Parameter Shadowing (Override Pattern)

Example: Scope Penalty (+100 Distance)

Distance Calculation Reference:

Scenario
Steps
Penalties
Total Distance
Notes

Same object

0

0

0

Always wins

Parent object

1

0

1

Grandparent

2

0

2

Sibling (same parent)

2

0

2

Up 1, down 1

Through scoped parent

1

+100

101

Scope creates strong boundary

Sibling in scoped object

2

+100

102

Cross 2 scope boundaries

varies

+200

high

Rarely wins

Uncle (parent's sibling)

3

0

3

Up 2, down 1

Example: Nested Scopes

4. Repeat/Iteration (Repeat)

The Repeat object enables parametric arrays and iterations.

Key Parameters:

  • CTRL: Name of the control parameter (loop variable)

    • ⚠️ WARNING: Do NOT use reserved keywords as CTRL names (e.g., pi, e, sin, cos, etc.)

    • pi is reserved for π (3.14159...) and cannot be used as a loop variable

    • Use descriptive names like i, j, k, idx, pileIndex, etc.

  • S: Start value

  • E: End value

  • I: Increment (default: 1)

During compilation, the Repeat generates copies of its child content for each iteration.

Example: Create 5 points along a line

For details See AI-Core-Objects.md

Guards - Conditional Object Activation

Guard is a special parameter that activates or deactivates objects based on conditions.

Purpose

Guards allow conditional geometry, calculations, or object creation based on user selections or parameter values.

Syntax

The Guard parameter must evaluate to a boolean (true/false):

  • True → Object is active and evaluated

  • False → Object is deactivated (skipped)

Common Guard Operators

  • .EQ. - Equal to

  • .NE. - Not equal to

  • .GT. - Greater than

  • .LT. - Less than

  • .GE. - Greater than or equal

  • .LE. - Less than or equal

  • .AND. - Logical AND

  • .OR. - Logical OR

Example: Conditional Column Geometry

How it works:

  1. User selects ColumnType (Rectangular or Circular)

  2. Each geometry object has a Guard checking the selection

  3. Only the matching object is activated and creates geometry

  4. The other object is skipped

Multiple Conditions

Guards can use complex conditions:

Common Patterns

Option Selection:

Threshold-Based:

Multiple Criteria:


XML Format

Basic Structure

Concise vs. Verbose Format

Concise (Preferred): When possible, define parameters as attributes on the object node:

Verbose: When you need to specify parameter metadata (units, description, role, etc.):

Common Patterns

1. Simple Numeric Parameter

2. Expression Parameter

3. Parameter with Units

4. Text Parameter

5. Object Reference

6. Object Reference (Attribute Style)

When defining as an attribute, prefix with @ and optionally specify type with |:

7. User Input Parameter

8. List/Array Parameter

9. Referencing Objects Pattern (Bidirectional References)

This pattern establishes automatic two-way references between parent and child objects. When a child object references a parent, it automatically appears in the parent's "Referencing Objects" list. This is essential for navigating object relationships in complex models.

Use Cases:

  • Girder → Cross Frames (bracings)

  • Pier Column → Foundation

  • Deck → Barrier

Parent Object (Girder):

Child Object (CrossFrame):

Key Components:

Component
Location
Purpose

bracings parameter

Parent's "Referencing Objects" group

Stores list of child objects

ReadOnly="1"

Parent's ParamInfo

Prevents manual editing, system-managed

RefParam="@bracings"

Child's ParamInfo

Tells system to add this object to parent's bracings list

How It Works:

  1. User creates child object (CrossFrame) and selects parent (Girder)

  2. System automatically adds CrossFrame to Girder's bracings list via RefParam

  3. Parent's "Referencing Objects" tab shows all children

  4. When child is deleted, it's automatically removed from parent's list

Navigation Through References:

Other objects can navigate through referencing objects to access nested data. For example, a Deck object takes a girder list as input, then can access each girder's cross frames via girder.bracings:

9. Referencing Objects Pattern (Bidirectional References)

This pattern establishes automatic two-way references between parent and child objects. When a child object references a parent, it automatically appears in the parent's "Referencing Objects" list. This is essential for navigating object relationships in complex models.

Use Cases:

  • Girder → Cross Frames (bracings)

  • Pier Column → Foundation

  • Deck → Barrier

Parent Object (Girder):

Child Object (CrossFrame):

Key Components:

Component
Location
Purpose

bracings parameter

Parent's "Referencing Objects" group

Stores list of child objects

ReadOnly="1"

Parent's ParamInfo

Prevents manual editing, system-managed

RefParam="@bracings"

Child's ParamInfo

Tells system to add this object to parent's bracings list

How It Works:

  1. User creates child object (CrossFrame) and selects parent (Girder)

  2. System automatically adds CrossFrame to Girder's bracings list via RefParam

  3. Parent's "Referencing Objects" tab shows all children

  4. When child is deleted, it's automatically removed from parent's list

Navigation Through References:

Other objects can navigate through referencing objects to access nested data. For example, a Deck object takes a girder list as input, then can access each girder's cross frames via girder.bracings:

Practical Examples

Example 1: Simple Rectangle

Example 2: Parametric Rectangle

Example 3: Array of Points Using Repeat

Example 4: Girder with Cross-Section

Example 5: Multi-Girder Bridge

Example 6: Conditional Objects (Guards)

Example 7: Object Inheritance (Creating Variations)

Advanced Features

Object Inheritance

Objects can inherit from other objects in the project by using the base object's name as their Type. When you do this, the new object inherits all parameters and child objects from the base object, and you can override specific parameters as needed.

Pattern:

  1. Define a base object with parameters

  2. Create derived objects using the base object's name as the T attribute

  3. Override parameters by specifying them as attributes or child parameters

Example:

In the above example, SkinnyCol and WideCol inherit all the parameters, child objects, and behavior from Col, but override the dimensional parameters. This is particularly useful for creating variations of standard components.

Extends - Importing Library Objects

Overview

The Extends attribute copies all parameters and child objects from one or more library objects into your current object. This is the primary mechanism for:

  • Importing AASHTO chapter implementations

  • Reusing calculation modules

  • Building complex objects from simpler components

  • Creating object namespaces

Key Concept: Think of Extends as copying the entire object tree from the library object(s) into your object.


Extends creates a new instance (copy) of the library object. Think of the library as a class/template, and each Extends creates a new object instance from that template.

Key Concept: Each time you use Extends, you create a separate instance with its own copy of the library's code.

Example - Creating Multiple Instances:

What Happens:

  1. Extends creates a new instance: Extends="AASHTO_Section3_4::v3" makes a complete copy of the library's structure into the new object

  2. Each Extends = Separate Instance: Instance1 and Instance2 are independent copies with their own parameter values

  3. Override="1" enables parameter replacement: Allows you to define parameters that replace same-named parameters in this instance's copy

  4. Original library unchanged: The AASHTO_Section3_4 library itself remains unchanged

  5. Independent calculations: Instance1 calculates with ADTTSL=5000, Instance2 with ADTTSL=8000

Object-Oriented Programming Analogy:

Without Override (Reading Default Values):

With Override (Setting Instance Parameters):

Key Takeaways:

  • Extends = Create new instance (copy) from library template

  • Override="1" = Enable replacing parameters and objects in this instance

  • Each Extends creates a separate, independent instance

  • Parameters/objects you define replace same-named parameters/objects in that instance only

  • Original library remains unchanged

For more detailed explanation: See AI-Extends.md

Overriding Objects (Not Just Parameters)

With Override="1", you can override child objects by defining objects with the same name, not just parameters.

Example - Overriding Child Objects:

How Override Works:

  1. The engine copies the library into the instance

  2. For each parameter/object you define in the instance

  3. It searches for a matching name in the copied library

  4. If found, it replaces that parameter/object entirely

  5. This works for both parameters (<P>) and objects (<O>)

Use Cases:

  • Override specific calculation modules while keeping the rest of the library

  • Replace default implementations with custom logic

  • Inject test values for specific sub-objects


Multiple Extends

You can extend from multiple objects by using array syntax:

Key Points:

  • Use array syntax: Extends="[Obj1, Obj2, Obj3]"

  • All listed objects are copied into your object

  • If objects have parameters with the same name, the last one in the list wins

  • Transitive: If you extend A, and A extends B, you also get B's content


Override Attribute - Setting Library Inputs

Library objects often define input parameters (with Role="Input"). The Override="1" attribute allows you to provide values for these inputs when extending the library.

Think of it Like a Function Call:

Conceptual Equivalent in Programming:


Scoped Attribute with Extends

The Scoped="1" attribute is commonly used with library imports to prevent parameter name conflicts.

Without Scoped:

With Scoped:

Best Practice: Always use Scoped="1" when importing library objects to avoid unintended parameter shadowing.


Versioning with ::v3

Library objects often include version tags (e.g., ::v3) to support multiple versions of the same code.

Benefits:

  • Backward compatibility - old projects keep using ::v3

  • Gradual migration - test ::v4 in parallel

  • Clear dependency tracking


Complete Real-World Example


Key Takeaways

  1. Extends = Copy: Copies the entire object tree from library into your object

  2. Container Pattern: Create <O N="ChapterName" T="Group" Extends="Library::v3" Scoped="1"/> for namespacing

  3. Override="1": Allows setting input parameters of the extended library object

  4. Scoped="1": Isolates library internals from your project namespace

  5. Dot Notation: Access library content via ContainerName.InternalPath.Parameter

  6. Multiple Extends: Use array syntax Extends="[Obj1, Obj2, Obj3]"

  7. Versioning: Use ::v3 for version control


Common Patterns Summary

Pattern 1: Import Library as Namespace

Pattern 2: Import Library with Input Override

Pattern 3: Extend Multiple Libraries

Pattern 4: Transitive Access


Station-Dependent Parameters

Some parameters can vary along a station (alignment coordinate):

Format: [[station_start, station_end], value, ...]

Built-in Functions

Common parametric functions available in expressions:

  • Geometric: online(p1, p2, t), intersect(line1, line2), point(obj, station)

  • Array: sum(array), min(...), max(...), length(array), first(array), last(array)

  • Units: convert(value, fromUnit, toUnit), withunits(value, unitType, unitCat)

  • Object queries: refs(objType), objs(objType), value(objName, paramName)

  • Geometry: surfarea(surface), volume(solid), perimeter(boundary)

Special Identifiers

  • self: Reference to current object

  • myself: Reference to current parameter's parent object

  • pi: π constant

  • NULL: Null object reference

Best Practices

1. Use Descriptive Names

2. Provide Default Values for Inputs

3. Use Appropriate Unit Types

4. Prefer Concise Format When Possible

6. Use Guards for Conditional Content

Choose the appropriate guard type for your use case:

  • Guard: For typical optional components (most common)

  • GuardX: For library development when you need to preserve the guard condition

  • DesignGuard: For design checks that may not apply in all cases

7. Use map() for Parameter Loops, Repeat Only for Objects

CRITICAL PERFORMANCE RULE: Never use Repeat for calculating parameter values—always use functional programming instead.

When to use each:

  • Use Repeat: Creating actual objects (Points, Lines, Volumes, Girders, etc.)

  • Use map(): Transforming data, extracting properties, calculating values

  • Use filter(): Selecting subset of data based on conditions

  • Use reduce(): Aggregating data to single value

See section 4.1 for detailed explanation and performance impact.

8. Document Complex Expressions

8. Use Scoped Objects Deliberately

Use Scoped="1" to create isolated parameter namespaces and prevent accidental parameter resolution from outside:

9. Avoid Unintentional Parameter Shadowing

Be deliberate when using the same parameter name at multiple hierarchy levels. If shadowing is unintentional, use unique names:

10. Understand Distance-Based Resolution

When multiple parameters share the same name, document the expected resolution in comments:

11. Optimize for Lazy Evaluation

Structure your model to take advantage of lazy evaluation:

12. Consider Hierarchy Performance

Avoid excessively deep hierarchies for frequently-accessed parameters:

Common Pitfalls to Avoid

  1. Using Repeat for Parameter Loops (CRITICAL PERFORMANCE ISSUE): This is the #1 performance mistake in ParamML models.

    ❌ WRONG:

    Problem: Creates 100 objects in project tree, slow compilation, high memory usage

    ✅ CORRECT:

    Why: Single parameter, instant evaluation, minimal memory

    Rule: Use Repeat ONLY to create objects that must exist in the 3D model, FEA mesh, or project tree. For all data transformations, use map(), filter(), or reduce().

    See section 4.1 for detailed explanation.

  2. Circular Dependencies: Ensure Parameter A doesn't depend on Parameter B if B depends on A. The lazy evaluation system will detect this and throw an error.

  3. Undefined References: Always ensure referenced objects/parameters exist in the hierarchy or project.

  4. Type Mismatches: Don't use numeric expressions for Text parameters.

  5. Missing Units: Specify unit types for physical quantities (Length, Force, Stress, etc.).

  6. Invalid Guards: Guard expressions must evaluate to 0 (false) or 1 (true).

  7. Repeat Issues: Ensure S, E, and I parameters create finite iterations.

  8. Unintended Parameter Shadowing: When multiple parameters have the same name at different hierarchy levels, the closest one (by distance) is used. This can cause unexpected behavior if you're not aware of which parameter is being resolved.

  9. Scope Boundary Surprises: Objects with Scoped="1" add +100 to distance calculations, creating strong isolation boundaries. Parameters inside scoped objects are "far away" from outside objects.

  10. Distance vs. XML Order Confusion: Parameter resolution is based on hierarchy distance, not the order parameters appear in XML. The engine always picks the closest parameter by distance, even if another parameter with the same name was defined first.

  11. Lazy Evaluation Timing: Remember that expressions are NOT evaluated when defined. They're evaluated on-demand. Changing an input parameter doesn't automatically update dependent parameters until they're accessed.

  12. Guard Expression Restrictions: Guard/GuardX expressions cannot reference parameters inside the object they guard (this would create a circular dependency during compilation). Always reference external parameters for guard conditions.

    Additionally, remember that Guard and GuardX are compile-time (object removed if false), while DesignGuard is runtime (object marked "not applicable" in reports).

  13. Deep Hierarchy Performance: Extremely deep hierarchies (10+ levels) can slow down parameter resolution. Consider flattening the structure or using scoped objects strategically to limit search scope.

Debugging Tips

  1. Check Parameter Names: Names are case-sensitive.

  2. Verify Object Types: Ensure T attribute matches actual class names.

  3. Test Expressions: Start with simple literals before adding complexity.

  4. Use Descriptive Names: Makes debugging much easier and reduces shadowing issues.

  5. Check Parent-Child Structure: Ensure objects are properly nested.

  6. Validate XML: Ensure proper closing tags and attribute quoting.

  7. Trace Parameter Resolution: When a parameter has an unexpected value, manually calculate the distance from the evaluation point to all candidates with that name. The one with minimum distance wins.

  8. Check Scope Boundaries: Look for Scoped="1" attributes in parent objects. They create isolation that may prevent expected parameter resolution.

  9. Verify Lazy Evaluation: If a parameter doesn't seem updated after changing an input, check if anything has actually requested its value. Try explicitly accessing it to trigger re-evaluation.

  10. Use Unique Names Strategically: If shadowing is causing confusion, use unique parameter names across the hierarchy, or use very deliberate shadowing for override patterns.

  11. Debug with Comments: Add XML comments showing expected distance calculations to document your resolution assumptions:

Advanced Library Development Features

The following features are essential for creating reusable library components with rich UI integration and metadata.

1. ParamInfo Object

ParamInfo is a special object type that provides metadata and configuration for parameters, controlling their UI behavior, validation, and visibility.

Common ParamInfo Attributes:

  • Param: Name of the parameter being configured (use @ prefix for object references)

  • Required: If "1", parameter must be provided (UI enforces)

  • Pick: If "1", enables object picker UI for selection

  • List: If "1", parameter is a list/array

  • Min: Minimum number of list items

  • Max: Maximum number of list items

  • ListColumnHeaders: Array of column header names for list UI

  • ListColumnTypes: Array of types for each column

  • ParamGuard: Conditional visibility expression (see ParamGuard section below)

  • ParamInterface: Custom parameter input interface (see ParamInterface section below)

⚠️ CRITICAL: Min and Max for List Parameters

When using Pick="1" with list parameters, BOTH Min and Max must be specified if you want to allow multiple selections. If only Min is provided without Max, the system defaults Max to 1, allowing only a single selection even if Min > 1.

Rule: For list inputs with unknown maximum count, always specify a reasonable Max value (e.g., Max="100").

Example: Required Object with Picker

This makes the Girder parameter required and adds a UI picker button for selecting girder objects.

Example: List Parameter with UI Configuration

This configures NonCompCase as a list with 1-50 items and uses a custom interface for input.

Example: List with Column Headers

This creates a list parameter with a single column labeled "Additional Stations".

Complete Example:

2. Object Metadata Attributes

Library objects can include metadata attributes that control organization, categorization, and UI display.

Metadata Attributes:

  • Tags: Comma-separated tags for filtering and categorization (prefix with @ for tag names)

  • ObjLabel: User-friendly display name for the object

  • Category: Hierarchical category path (use : separator for subcategories)

Example:

Tag Usage:

  • @secondlayer: Indicates this is a secondary-level component

  • @usastandard: US design standards

  • @steelIGirder: Applies to steel I-girder bridges

  • @release: Ready for production release

Category Hierarchy: The category path creates a tree structure in the UI:

Best Practices:

  • Use descriptive ObjLabel values that users will recognize

  • Create consistent tag vocabularies across your library

  • Organize categories hierarchically from general to specific

  • Use tags for filtering (e.g., bridge type, code standard, region)

3. Extends Attribute

The Extends attribute provides a different inheritance mechanism than the T (Type) attribute. While T specifies the object's class or base type, Extends allows you to inherit parameters and structure from another object while maintaining a different base type.

Syntax:

Key Differences:

  • Type (T): Defines the object's fundamental class (e.g., Project, Group, Volume)

  • Extends: Imports parameters and child objects from another object without changing the base type

Example:

Versioning Pattern: Use ::vN suffix for versioned objects to maintain backward compatibility:

When to Use Extends vs Type:

  • Use Type (T): To define what kind of object it is (Volume, Group, Project, etc.)

  • Use Extends: To import a set of default parameters and structure from a template

4. ParamGuard

ParamGuard is different from Guard - while Guard removes entire objects, ParamGuard controls parameter visibility in the UI based on conditions.

Key Differences:

Feature
Guard
ParamGuard

Applied To

Objects

Parameters (via ParamInfo)

Effect

Removes object if false

Hides parameter UI if false

Evaluation

Compile-time

Runtime (UI update)

Use Case

Conditional components

Conditional parameter visibility

Syntax:

Example: Conditional Parameter Visibility

In this example:

  • When ReinfDataPref == 0: Only LumpedTopReinfArea is visible in UI

  • When ReinfDataPref == 1: DetailedBarSize and DetailedBarSpacing are visible

Complex Conditions:

Best Practices:

  • Use ParamGuard to simplify UI by hiding irrelevant parameters

  • Guard expressions should reference other parameters in the same object or parent objects

  • Combine with Required flag: Required="1" only enforces when ParamGuard is true

  • Keep guard expressions simple and fast (evaluated on every UI update)

5. ParamInterface

ParamInterface specifies a custom UI component for parameter input, enabling specialized editing experiences beyond standard text/number inputs.

Syntax:

Common Custom Interfaces:

  • @OBPParamInterfaceCombinationAndFactors: Load combination editor

  • @OBPParamInterfaceStationPicker: Station selection with alignment visualization

  • @OBPParamInterfaceColorPicker: Color selection UI

  • @OBPParamInterfaceMaterialPicker: Material database browser

Example: Load Combination Interface

Example: Custom Table Interface

Creating Custom Interfaces: Custom interfaces are defined as objects with specific UI behavior:

6. List Parameters with UI Configuration

List (array) parameters can be configured with rich UI metadata using ParamInfo.

List Configuration Attributes:

  • List="1": Declares parameter as a list

  • Min: Minimum number of items (enforced by UI)

  • Max: Maximum number of items

  • ListColumnHeaders: Array of column names (as JSON array string)

  • ListColumnTypes: Array of types for each column ('Number', 'Text', 'Object', etc.)

Example: Simple List

Example: Multi-Column List

Example: List of Object References

Accessing List Items in Expressions:

Best Practices:

  • Always set reasonable Min and Max limits

  • Provide descriptive column headers

  • Match ListColumnTypes to actual data types for proper validation

  • Use Pick="1" for lists of object references to enable picker UI

7. NAME_ALIAS and EXPR_ALIAS

These special documentation markers in parameter descriptions provide human-readable aliases for parameters and expressions, improving code readability and auto-generated documentation.

Syntax in Description Field:

NAME_ALIAS - Readable Parameter Names:

When referenced in reports or UI:

  • Shows "My_Total" instead of "My_Pos_MaxF_Total"

  • Shows "Mx_DL" instead of "Mx_Neg_DL_Max"

EXPR_ALIAS - Readable Expressions: