translator

Modules

translator module

class And(rule_a, rule_b)[source]

Bases: Rule

apply(sm_subject, sm_object, ruleset, sp_sm_map_list=None, master_rule=None)[source]
get_sp_node()[source]
class Exact(**kwargs)[source]

Bases: Rule

Rule that requires exact matches between pattern and semantic model elements.

The Exact rule is the most restrictive rule type, requiring that the semantic model contains exactly the same relationship as specified in the signature pattern. This rule is used when you need precise control over the pattern matching process.

Priority: 10 (highest priority)

Behavior

  • Requires that the semantic model contains the exact relationship specified

  • No traversal or flexibility in matching

  • Used for critical relationships that must be present exactly as specified

Examples

Controller-property relationships (from PID controller system):

>>> # Define controller nodes using real ontology classes
>>> controller_node = Node(cls=core.namespace.S4BLDG.SetpointController)
>>> sensor_node = Node(cls=core.namespace.SAREF.Sensor)
>>> property_node = Node(cls=core.namespace.SAREF.Property)
>>>
>>> # Define exact relationships for precise control logic
>>> controller_observes = Exact(
...     subject=controller_node,
...     object=property_node,
...     predicate=core.namespace.SAREF.observes
... )
>>> sensor_observes = Exact(
...     subject=sensor_node,
...     object=property_node,
...     predicate=core.namespace.SAREF.observes
... )

Damper control relationships (from damper system):

>>> # Define damper control nodes
>>> damper_node = Node(cls=core.namespace.S4BLDG.Damper)
>>> controller_node = Node(cls=core.namespace.S4BLDG.Controller)
>>> position_node = Node(cls=core.namespace.SAREF.OpeningPosition)
>>>
>>> # Controller must directly control the opening position
>>> control_relationship = Exact(
...     subject=controller_node,
...     object=position_node,
...     predicate=core.namespace.SAREF.controls
... )
>>>
>>> # Position must be property of the damper
>>> property_relationship = Exact(
...     subject=position_node,
...     object=damper_node,
...     predicate=core.namespace.SAREF.isPropertyOf
... )

Building space topology (from building space system):

>>> # Define building space nodes
>>> supply_damper = Node(cls=core.namespace.S4BLDG.Damper)
>>> return_damper = Node(cls=core.namespace.S4BLDG.Damper)
>>> building_space = Node(cls=core.namespace.S4BLDG.BuildingSpace)
>>> space_heater = Node(cls=core.namespace.S4BLDG.SpaceHeater)
>>>
>>> # Exact fluid supply relationships
>>> supply_relationship = Exact(
...     subject=supply_damper,
...     object=building_space,
...     predicate=core.namespace.FSO.suppliesFluidTo
... )
>>> return_relationship = Exact(
...     subject=return_damper,
...     object=building_space,
...     predicate=core.namespace.FSO.hasFluidReturnedBy
... )
>>>
>>> # Space heater containment
>>> containment_relationship = Exact(
...     subject=space_heater,
...     object=building_space,
...     predicate=core.namespace.S4BLDG.isContainedIn
... )

BRICK ontology relationships (from BRICK damper system):

>>> # Define BRICK nodes
>>> damper_node = Node(cls=core.namespace.BRICK.Damper)
>>> position_setpoint = Node(cls=core.namespace.BRICK.Damper_Position_Setpoint)
>>> position_sensor = Node(cls=core.namespace.BRICK.Damper_Position_Sensor)
>>> flow_sensor = Node(cls=core.namespace.BRICK.Air_Flow_Sensor)
>>>
>>> # BRICK-specific exact relationships
>>> setpoint_relationship = Exact(
...     subject=position_setpoint,
...     object=damper_node,
...     predicate=core.namespace.BRICK.isPointOf
... )
>>> sensor_relationship = Exact(
...     subject=position_sensor,
...     object=damper_node,
...     predicate=core.namespace.BRICK.isPointOf
... )
>>> flow_relationship = Exact(
...     subject=flow_sensor,
...     object=damper_node,
...     predicate=core.namespace.BRICK.isPointOf
... )

Sensor-property relationships (from sensor system):

>>> # Define sensor nodes
>>> sensor_node = Node(cls=core.namespace.SAREF.Sensor)
>>> temperature_node = Node(cls=core.namespace.SAREF.Temperature)
>>> space_node = Node(cls=core.namespace.S4BLDG.BuildingSpace)
>>>
>>> # Sensor must observe the temperature property
>>> sensor_observes = Exact(
...     subject=sensor_node,
...     object=temperature_node,
...     predicate=core.namespace.SAREF.observes
... )
>>>
>>> # Temperature must be property of the space
>>> temperature_property = Exact(
...     subject=temperature_node,
...     object=space_node,
...     predicate=core.namespace.SAREF.isPropertyOf
... )
apply(sm_subject, sm_object, ruleset, sp_sm_map_list=None, master_rule=None)[source]
reset()[source]
PRIORITY = 10
class MultiPath(stop_early=True, **kwargs)[source]

Bases: Rule

Rule that allows traversal along multiple paths in the semantic model.

The MultiPath rule is the most flexible rule type, allowing the pattern matcher to explore multiple paths between the subject and object in the semantic model. This is useful when there are multiple valid ways to connect components or when the semantic model has complex relationship structures.

Priority: 1 (lower priority than Exact)

Behavior

  • Allows traversal through multiple paths in the semantic model

  • Finds all possible paths between subject and object

  • Most flexible rule type

  • Can stop early if stop_early=True (default)

Note: MultiPath rules can cause infinite recursion in some complex semantic models and are used sparingly in practice. Consider using SinglePath for most flexible matching needs.

Examples

Complex building space connections (theoretical usage):

>>> # Define building space nodes using real ontology classes
>>> building_space1 = Node(cls=core.namespace.S4BLDG.BuildingSpace)
>>> building_space2 = Node(cls=core.namespace.S4BLDG.BuildingSpace)
>>>
>>> # MultiPath for complex adjacent zone relationships
>>> # Note: This is commented out in real systems due to recursion issues
>>> # adjacent_connection = MultiPath(
>>> #     subject=building_space1,
>>> #     object=building_space2,
>>> #     predicate=core.namespace.S4SYST.connectedTo
>>> # )
>>>
>>> # This could theoretically match multiple connection types:
>>> # space1 -> shared_hvac_system -> space2
>>> # space1 -> structural_connection -> space2
>>> # space1 -> thermal_bridge -> space2
>>> # space1 -> common_corridor -> space2

Equipment network traversal (theoretical usage):

>>> # Define HVAC equipment nodes
>>> chiller_node = Node(cls=core.namespace.S4BLDG.Chiller)
>>> cooling_tower = Node(cls=core.namespace.S4BLDG.CoolingTower)
>>> heat_exchanger = Node(cls=core.namespace.S4BLDG.HeatExchanger)
>>>
>>> # MultiPath for complex chilled water systems with multiple paths
>>> # cooling_network = MultiPath(
>>> #     subject=chiller_node,
>>> #     object=cooling_tower,
>>> #     predicate=core.namespace.FSO.suppliesFluidTo
>>> # )
>>>
>>> # Could match various cooling system configurations:
>>> # chiller -> primary_loop -> heat_exchanger -> secondary_loop -> cooling_tower
>>> # chiller -> bypass_valve -> direct_connection -> cooling_tower
>>> # chiller -> buffer_tank -> distribution_system -> cooling_tower

BRICK equipment hierarchies (theoretical usage):

>>> # Define BRICK nodes for air handling systems
>>> ahu_node = Node(cls=core.namespace.BRICK.AHU)
>>> terminal_unit = Node(cls=core.namespace.BRICK.Terminal_Unit)
>>>
>>> # MultiPath for complex BRICK hierarchies
>>> # Note: Use with caution due to potential performance issues
>>> # brick_hierarchy = MultiPath(
>>> #     subject=ahu_node,
>>> #     object=terminal_unit,
>>> #     predicate=core.namespace.BRICK.feeds
>>> # )
>>>
>>> # Could traverse multiple BRICK relationship paths:
>>> # AHU -> VAV_Box -> Terminal_Unit
>>> # AHU -> Duct_System -> Zone_Equipment -> Terminal_Unit
>>> # AHU -> Distribution_System -> End_Use_Equipment -> Terminal_Unit

Practical alternatives to MultiPath:

>>> # Instead of MultiPath, consider using multiple SinglePath rules
>>> # or combining Optional_ rules for specific known alternatives
>>>
>>> # Define equipment nodes
>>> supply_equipment = Node(cls=(
...     core.namespace.S4BLDG.Coil,
...     core.namespace.S4BLDG.Fan,
...     core.namespace.S4BLDG.HeatExchanger
... ))
>>> distribution_node = Node(cls=(
...     core.namespace.S4BLDG.Duct,
...     core.namespace.S4BLDG.Pipe
... ))
>>>
>>> # Primary connection path
>>> primary_path = SinglePath(
...     subject=supply_equipment,
...     object=distribution_node,
...     predicate=core.namespace.FSO.suppliesFluidTo
... )
>>>
>>> # Alternative: Use Optional_ for specific alternative connections
>>> bypass_valve = Node(cls=core.namespace.S4BLDG.Valve)
>>> optional_bypass = Optional_(
...     subject=supply_equipment,
...     object=bypass_valve,
...     predicate=core.namespace.FSO.suppliesFluidTo
... )
>>>
>>> # This approach provides controlled flexibility without recursion risks

Best Practice: In most real-world implementations, use SinglePath for flexible connections and Optional_ for alternative configurations rather than MultiPath, which can cause performance issues in complex semantic models.

apply(sm_subject, sm_object, ruleset, sp_sm_map_list=None, master_rule=None)[source]
reset()[source]
PRIORITY = 1
class Node(cls, graph_name=None, hash_=None)[source]

Bases: object

eq(other)[source]
get_type_attributes()[source]
h()[source]
make_id()[source]
set_signature_pattern(signature_pattern)[source]

Set the signature pattern for this node

validate_cls()[source]
property id
node_instance_count = count(0)
property semantic_model

Get the semantic model associated with this node

property signature_pattern
class Optional_(**kwargs)[source]

Bases: Rule

Rule that makes pattern elements optional (may or may not be present).

The Optional_ rule allows signature patterns to include elements that may or may not be present in the semantic model. This is useful for creating flexible patterns that can match a variety of system configurations.

Priority: 1 (lowest priority)

Behavior

  • Makes the relationship optional - it may or may not exist in the semantic model

  • If the relationship exists, it must match the specified pattern

  • If the relationship doesn’t exist, the pattern can still match

  • Used to create flexible patterns that accommodate variations in system configurations

Examples

Optional damper parameters (from damper system):

>>> # Define damper nodes using real ontology classes
>>> damper_node = Node(cls=core.namespace.S4BLDG.Damper)
>>> property_value = Node(cls=core.namespace.SAREF.PropertyValue)
>>> float_value = Node(cls=core.namespace.XSD.float)
>>> flow_rate_node = Node(cls=core.namespace.S4BLDG.NominalAirFlowRate)
>>>
>>> # Optional parameter relationships - damper may have nominal flow rate
>>> optional_value = Optional_(
...     subject=property_value,
...     object=float_value,
...     predicate=core.namespace.SAREF.hasValue
... )
>>> optional_property = Optional_(
...     subject=property_value,
...     object=flow_rate_node,
...     predicate=core.namespace.SAREF.isValueOfProperty
... )
>>> optional_damper_param = Optional_(
...     subject=damper_node,
...     object=property_value,
...     predicate=core.namespace.SAREF.hasPropertyValue
... )
>>>
>>> # Pattern matches whether or not flow rate is specified:
>>> # - If flow rate exists: must match the pattern structure
>>> # - If flow rate doesn't exist: pattern still matches

Optional BRICK values (from BRICK damper system):

>>> # Define BRICK nodes
>>> flow_setpoint = Node(cls=core.namespace.BRICK.Air_Flow_Setpoint)
>>> float_value = Node(cls=core.namespace.XSD.float)
>>>
>>> # Optional BRICK value - flow setpoint may have a numeric value
>>> optional_brick_value = Optional_(
...     subject=flow_setpoint,
...     object=float_value,
...     predicate=core.namespace.BRICK.hasValue
... )
>>>
>>> # This allows the pattern to match BRICK models with or without
>>> # explicit setpoint values configured

Optional building space components (example pattern extension):

>>> # Define building space nodes
>>> building_space = Node(cls=core.namespace.S4BLDG.BuildingSpace)
>>> heat_recovery = Node(cls=core.namespace.S4BLDG.AirToAirHeatRecovery)
>>> humidity_sensor = Node(cls=core.namespace.SAREF.Sensor)
>>> humidity_property = Node(cls=core.namespace.SAREF.Humidity)
>>>
>>> # Optional heat recovery system
>>> optional_heat_recovery = Optional_(
...     subject=building_space,
...     object=heat_recovery,
...     predicate=core.namespace.S4BLDG.contains
... )
>>>
>>> # Optional humidity monitoring
>>> optional_humidity_sensor = Optional_(
...     subject=humidity_sensor,
...     object=humidity_property,
...     predicate=core.namespace.SAREF.observes
... )
>>> optional_humidity_in_space = Optional_(
...     subject=humidity_property,
...     object=building_space,
...     predicate=core.namespace.SAREF.isPropertyOf
... )
>>>
>>> # Pattern works for various building space configurations:
>>> # - Basic space without heat recovery or humidity sensing
>>> # - Space with heat recovery but no humidity sensing
>>> # - Space with humidity sensing but no heat recovery
>>> # - Fully equipped space with both features

Optional controller parameters (common in control systems):

>>> # Define controller nodes
>>> controller_node = Node(cls=core.namespace.S4BLDG.SetpointController)
>>> deadband_node = Node(cls=core.namespace.S4BLDG.Deadband)
>>> gain_node = Node(cls=core.namespace.S4BLDG.ProportionalGain)
>>> integral_time = Node(cls=core.namespace.S4BLDG.IntegralTime)
>>>
>>> # Optional controller tuning parameters
>>> optional_deadband = Optional_(
...     subject=controller_node,
...     object=deadband_node,
...     predicate=core.namespace.SAREF.hasProperty
... )
>>> optional_gain = Optional_(
...     subject=controller_node,
...     object=gain_node,
...     predicate=core.namespace.SAREF.hasProperty
... )
>>> optional_integral = Optional_(
...     subject=controller_node,
...     object=integral_time,
...     predicate=core.namespace.SAREF.hasProperty
... )
>>>
>>> # Controller pattern matches various configurations:
>>> # - Basic on/off controller (no tuning parameters)
>>> # - P controller (proportional gain only)
>>> # - PI controller (proportional + integral)
>>> # - Full PID controller with deadband

Flexible sensor configurations (from sensor system patterns):

>>> # Define sensor nodes for position measurement
>>> sensor_node = Node(cls=core.namespace.SAREF.Sensor)
>>> position_node = Node(cls=core.namespace.SAREF.OpeningPosition)
>>> valve_or_damper = Node(cls=(
...     core.namespace.S4BLDG.Valve,
...     core.namespace.S4BLDG.Damper,
... ))
>>> controller_node = Node(cls=core.namespace.S4BLDG.Controller)
>>>
>>> # Required: sensor observes position
>>> sensor_observes = Exact(
...     subject=sensor_node,
...     object=position_node,
...     predicate=core.namespace.SAREF.observes
... )
>>>
>>> # Required: position belongs to valve/damper
>>> position_property = Exact(
...     subject=position_node,
...     object=valve_or_damper,
...     predicate=core.namespace.SAREF.isPropertyOf
... )
>>>
>>> # Optional: controller controls the position
>>> optional_control = Optional_(
...     subject=controller_node,
...     object=position_node,
...     predicate=core.namespace.SAREF.controls
... )
>>>
>>> # Pattern matches:
>>> # - Manual valve with position sensor (no controller)
>>> # - Automated valve with controller and position feedback
apply(sm_subject, sm_object, ruleset, sp_sm_map_list=None, master_rule=None)[source]
reset()[source]
PRIORITY = 1
class Or(rule_a, rule_b)[source]

Bases: Rule

apply(sm_subject, sm_object, ruleset, sp_sm_map_list=None, master_rule=None)[source]
reset()[source]
class Rule(subject=None, object=None, predicate=None)[source]

Bases: object

Base class for pattern matching rules that define how signature pattern elements map to semantic model elements.

Rules are the fundamental building blocks of signature patterns, defining the constraints and flexibility of the pattern matching process. Each rule specifies how a relationship between two nodes in the signature pattern should be matched against the semantic model.

Overview

Rules define the mapping between signature pattern elements and semantic model elements through: - Subject: The source node in the signature pattern - Object: The target node in the signature pattern - Predicate: The relationship type between subject and object - Priority: The precedence level for rule application (higher values take precedence)

Rule Types

The Translator supports several types of rules, each with different matching behavior:

  • Exact: Requires exact matches between pattern and semantic model elements

  • SinglePath: Allows traversal along a single path in the semantic model

  • MultiPath: Allows traversal along multiple paths in the semantic model

  • Optional: Makes pattern elements optional (may or may not be present)

Rule Composition

Rules can be combined using logical operators: - And: Both rules must be satisfied - Or: Either rule can be satisfied

Examples

>>> # Create nodes for a fan pattern
>>> fan_node = Node(Fan)
>>> meter_node = Node(Meter)
>>> flow_node = Node(Flow)
>>>
>>> # Define relationships with different rule types
>>> exact_rule = Exact(meter_node, fan_node, "observes")
>>> path_rule = SinglePath(meter_node, flow_node, "hasValue")
>>> optional_rule = Optional_(fan_node, flow_node, "hasProperty")
>>>
>>> # Combine rules
>>> combined_rule = exact_rule & path_rule | optional_rule
subject

The source node in the signature pattern

Type:

Node

object

The target node in the signature pattern

Type:

Node

predicate

The relationship type between subject and object

Type:

str

PRIORITY

The precedence level for rule application

Type:

int

class SignaturePattern(semantic_model_=None, id=None, pedantic=False)[source]

Bases: object

A class for defining signature patterns that describe how component models map to semantic model instances.

Signature patterns are the core mechanism by which the Translator identifies where and how component models should be instantiated within a semantic model. Each signature pattern defines a graph structure that specifies the semantic context required for a component model to be applicable.

Overview

A signature pattern consists of: - Nodes: Represent semantic model elements (components, properties, values) - Edges: Represent relationships between nodes (predicates) - Rules: Define how pattern elements map to semantic model elements - Modeled Nodes: Specify which nodes correspond to the actual component being modeled - Parameters: Define which nodes provide parameter values for the component - Inputs: Define which nodes provide input connections for the component

Pattern Structure

Signature patterns are defined using a graph-based approach where:

  • Each node represents a semantic model element (e.g., a Damper, Sensor, or Property)

  • Each edge represents a relationship between elements (e.g., “observes”, “controls”)

  • Rules determine how flexible the matching process is (Exact, SinglePath, MultiPath, Optional_)

The pattern matching process finds subgraph isomorphisms between the signature pattern and the semantic model, allowing the Translator to identify valid contexts for component instantiation.

id

Unique identifier for the signature pattern

Type:

str

nodes

List of nodes in the signature pattern

Type:

List[Node]

required_nodes

List of nodes that must be present for a match

Type:

List[Node]

modeled_nodes

List of nodes that correspond to the component being modeled

Type:

List[Node]

parameters

Dictionary mapping parameter names to nodes that provide values

Type:

Dict[str, Node]

inputs

Dictionary mapping input names to nodes and their source mappings

Type:

Dict[str, Tuple[Node, Dict]]

ruleset

Dictionary mapping (subject, predicate, object) tuples to rules

Type:

Dict[Tuple, Rule]

Examples

Basic damper control signature pattern (from actual damper system):

>>> import twin4build.core as core
>>> from twin4build.translator.translator import SignaturePattern, Node, Exact, Optional_
>>>
>>> def get_signature_pattern():
...     '''Create signature pattern for damper system'''
...     # Define nodes using real ontology classes
...     damper_node = Node(cls=core.namespace.S4BLDG.Damper)
...     controller_node = Node(cls=core.namespace.S4BLDG.Controller)
...     position_node = Node(cls=core.namespace.SAREF.OpeningPosition)
...     property_node = Node(cls=core.namespace.SAREF.Property)
...     flow_rate_node = Node(cls=core.namespace.S4BLDG.NominalAirFlowRate)
...     float_value = Node(cls=core.namespace.XSD.float)
...
...     # Create signature pattern with real parameters
...     sp = SignaturePattern(
...         semantic_model_=core.ontologies,
...     )
...
...     # Add required relationships using Exact rules
...     sp.add_triple(
...         Exact(subject=controller_node, object=position_node,
...               predicate=core.namespace.SAREF.controls)
...     )
...     sp.add_triple(
...         Exact(subject=position_node, object=damper_node,
...               predicate=core.namespace.SAREF.isPropertyOf)
...     )
...     sp.add_triple(
...         Exact(subject=controller_node, object=property_node,
...               predicate=core.namespace.SAREF.observes)
...     )
...
...     # Add optional parameter using Optional_ rule
...     sp.add_triple(
...         Optional_(subject=damper_node, object=flow_rate_node,
...                   predicate=core.namespace.SAREF.hasPropertyValue)
...     )
...
...     # Configure inputs and parameters
...     sp.add_input("damperPosition", controller_node, "inputSignal")
...     sp.add_parameter("nominalAirFlowRate", float_value)
...     sp.add_modeled_node(damper_node)
...
...     return sp

PID controller pattern with exact relationships (from actual controller implementation):

>>> def get_signature_pattern():
...     '''Create signature pattern for PID controller'''
...     # Define controller nodes using real ontology classes
...     controller_node = Node(cls=core.namespace.S4BLDG.SetpointController)
...     sensor_node = Node(cls=core.namespace.SAREF.Sensor)
...     property_node = Node(cls=core.namespace.SAREF.Property)
...     schedule_node = Node(cls=core.namespace.S4BLDG.Schedule)
...     reverse_node = Node(cls=core.namespace.XSD.boolean)
...
...     sp = SignaturePattern(
...         semantic_model_=core.ontologies,
...     )
...
...     # All relationships are exact for precise control logic
...     sp.add_triple(
...         Exact(subject=controller_node, object=property_node,
...               predicate=core.namespace.SAREF.observes)
...     )
...     sp.add_triple(
...         Exact(subject=sensor_node, object=property_node,
...               predicate=core.namespace.SAREF.observes)
...     )
...     sp.add_triple(
...         Exact(subject=controller_node, object=schedule_node,
...               predicate=core.namespace.SAREF.hasProfile)
...     )
...     sp.add_triple(
...         Exact(subject=controller_node, object=reverse_node,
...               predicate=core.namespace.S4BLDG.isReverse)
...     )
...
...     # Configure controller inputs and parameters
...     sp.add_input("actualValue", sensor_node, "measuredValue")
...     sp.add_input("setpointValue", schedule_node, "scheduleValue")
...     sp.add_parameter("isReverse", reverse_node)
...     sp.add_modeled_node(controller_node)
...
...     return sp

Building space pattern with SinglePath for flexible connections (from building space system):

>>> def get_signature_pattern():
...     '''Create signature pattern for building space system'''
...     # Define nodes for building space components
...     supply_damper = Node(cls=core.namespace.S4BLDG.Damper)  # supply damper
...     return_damper = Node(cls=core.namespace.S4BLDG.Damper)  # return damper
...     building_space = Node(cls=core.namespace.S4BLDG.BuildingSpace)
...     space_heater = Node(cls=core.namespace.S4BLDG.SpaceHeater)
...     schedule = Node(cls=core.namespace.S4BLDG.Schedule)
...     outdoor_env = Node(cls=core.namespace.S4BLDG.OutdoorEnvironment)
...     supply_equipment = Node(cls=(
...         core.namespace.S4BLDG.Coil,
...         core.namespace.S4BLDG.AirToAirHeatRecovery,
...         core.namespace.S4BLDG.Fan,
...     ))
...
...     sp = SignaturePattern(
...         semantic_model_=core.ontologies,
...     )
...
...     # Exact relationships for system topology
...     sp.add_triple(
...         Exact(subject=supply_damper, object=building_space,
...               predicate=core.namespace.FSO.suppliesFluidTo)
...     )
...     sp.add_triple(
...         Exact(subject=return_damper, object=building_space,
...               predicate=core.namespace.FSO.hasFluidReturnedBy)
...     )
...     sp.add_triple(
...         Exact(subject=space_heater, object=building_space,
...               predicate=core.namespace.S4BLDG.isContainedIn)
...     )
...
...     # SinglePath allows flexible connection from damper to equipment
...     sp.add_triple(
...         SinglePath(subject=supply_damper, object=supply_equipment,
...                    predicate=core.namespace.FSO.hasFluidSuppliedBy)
...     )
...
...     # Configure inputs for the building space
...     sp.add_input("supplyAirFlowRate", supply_damper, "airFlowRate")
...     sp.add_input("exhaustAirFlowRate", return_damper, "airFlowRate")
...     sp.add_input("heatGain", space_heater, "Power")
...     sp.add_input("numberOfPeople", schedule, "scheduleValue")
...     sp.add_input("outdoorTemperature", outdoor_env, "outdoorTemperature")
...     sp.add_input("supplyAirTemperature", supply_equipment,
...                  ("outletAirTemperature", "primaryTemperatureOut"))
...
...     sp.add_modeled_node(building_space)
...     return sp

BRICK ontology pattern (from damper BRICK system):

>>> def get_signature_pattern_brick():
...     '''Create BRICK-specific signature pattern for damper'''
...     damper_node = Node(cls=core.namespace.BRICK.Damper)
...     position_setpoint = Node(cls=core.namespace.BRICK.Damper_Position_Setpoint)
...     position_sensor = Node(cls=core.namespace.BRICK.Damper_Position_Sensor)
...     flow_sensor = Node(cls=core.namespace.BRICK.Air_Flow_Sensor)
...     flow_setpoint = Node(cls=core.namespace.BRICK.Air_Flow_Setpoint)
...     float_value = Node(cls=core.namespace.XSD.float)
...
...     sp = SignaturePattern(
...         semantic_model_=core.ontologies,
...     )
...
...     # BRICK-specific relationships
...     sp.add_triple(
...         Exact(subject=position_setpoint, object=damper_node,
...               predicate=core.namespace.BRICK.isPointOf)
...     )
...     sp.add_triple(
...         Exact(subject=position_sensor, object=damper_node,
...               predicate=core.namespace.BRICK.isPointOf)
...     )
...     sp.add_triple(
...         Exact(subject=flow_sensor, object=damper_node,
...               predicate=core.namespace.BRICK.isPointOf)
...     )
...
...     # Optional flow rate parameter
...     sp.add_triple(
...         Optional_(subject=flow_setpoint, object=float_value,
...                   predicate=core.namespace.BRICK.hasValue)
...     )
...
...     sp.add_input("damperPosition", position_setpoint, "setpoint")
...     sp.add_parameter("nominalAirFlowRate", float_value)
...     sp.add_modeled_node(damper_node)
...
...     return sp

Using signature patterns in component classes (from actual system implementation):

>>> class DamperTorchSystem(core.System, nn.Module):
...     # Multiple signature patterns with different priorities
...     sp = [get_signature_pattern(), get_signature_pattern_brick()]
...
...     def __init__(self, a=1, nominalAirFlowRate=0.034, **kwargs):
...         super().__init__(**kwargs)
...         nn.Module.__init__(self)
...         # System implementation...

Sensor signature patterns for space properties (from sensor system):

>>> def get_space_temperature_signature_pattern():
...     '''Pattern for temperature sensors in building spaces'''
...     sensor_node = Node(cls=core.namespace.SAREF.Sensor)
...     temperature_node = Node(cls=core.namespace.SAREF.Temperature)
...     space_node = Node(cls=core.namespace.S4BLDG.BuildingSpace)
...
...     sp = SignaturePattern(
...         semantic_model_=core.ontologies,
...     )
...
...     sp.add_triple(
...         Exact(subject=sensor_node, object=temperature_node,
...               predicate=core.namespace.SAREF.observes)
...     )
...     sp.add_triple(
...         Exact(subject=temperature_node, object=space_node,
...               predicate=core.namespace.SAREF.isPropertyOf)
...     )
...
...     sp.add_modeled_node(sensor_node)
...     return sp
add_input(key, node, source_keys=None)[source]
add_modeled_node(node)[source]
add_namespace(namespace)[source]
add_parameter(key, node)[source]
add_triple(rule)[source]
get_node_by_id(id)[source]
remove_modeled_node(node)[source]
reset_ruleset()[source]
property inputs
property modeled_nodes
property nodes
property parameters
property required_nodes
property ruleset
class SinglePath(stop_early=True, **kwargs)[source]

Bases: Rule

Rule that allows traversal along a single path in the semantic model.

The SinglePath rule is more flexible than Exact, allowing the pattern matcher to traverse through intermediate nodes in the semantic model to find a path between the subject and object. This is useful when the semantic model has additional intermediate elements that aren’t part of the core pattern.

Priority: 1 (lower priority than Exact)

Behavior

  • Allows traversal through intermediate nodes in the semantic model

  • Finds a single path between subject and object

  • More flexible than Exact but still constrained to one path

  • Can stop early if stop_early=True (default)

Examples

Building space equipment connections (from building space system):

>>> # Define building space nodes using real ontology classes
>>> supply_damper = Node(cls=core.namespace.S4BLDG.Damper)
>>> supply_equipment = Node(cls=(
...     core.namespace.S4BLDG.Coil,
...     core.namespace.S4BLDG.AirToAirHeatRecovery,
...     core.namespace.S4BLDG.Fan,
... ))
>>>
>>> # SinglePath allows flexible connection from damper to upstream equipment
>>> # This can traverse through intermediate components like ducts or junctions
>>> equipment_connection = SinglePath(
...     subject=supply_damper,
...     object=supply_equipment,
...     predicate=core.namespace.FSO.hasFluidSuppliedBy
... )
>>>
>>> # This will match even if the semantic model has:
>>> # damper -> duct_section -> coil
>>> # damper -> junction -> fan
>>> # damper -> heat_recovery_unit

BRICK ontology flexible connections (from BRICK building space system):

>>> # Define BRICK nodes
>>> vav_node = Node(cls=core.namespace.BRICK.VAV)  # Variable Air Volume unit
>>> ahu_node = Node(cls=core.namespace.BRICK.AHU)  # Air Handling Unit
>>>
>>> # SinglePath allows traversal through BRICK equipment hierarchy
>>> ahu_connection = SinglePath(
...     subject=vav_node,
...     object=ahu_node,
...     predicate=core.namespace.BRICK.isFedBy
... )
>>>
>>> # This can match complex BRICK hierarchies:
>>> # VAV -> Terminal_Unit -> Zone_Equipment -> AHU
>>> # VAV -> Duct_System -> AHU

Sensor connections after equipment (from sensor system):

>>> # Define sensor nodes for temperature measurement after coil
>>> sensor_node = Node(cls=core.namespace.SAREF.Sensor)
>>> temperature_node = Node(cls=core.namespace.SAREF.Temperature)
>>> coil_air_side = Node(cls=core.namespace.S4BLDG.Coil)
>>> system_after = Node(cls=core.namespace.S4SYST.System)
>>>
>>> # Exact relationship for sensor observation
>>> sensor_observes = Exact(
...     subject=sensor_node,
...     object=temperature_node,
...     predicate=core.namespace.SAREF.observes
... )
>>>
>>> # SinglePath allows flexible connection from coil to sensor location
>>> coil_to_sensor = SinglePath(
...     subject=coil_air_side,
...     object=sensor_node,
...     predicate=core.namespace.FSO.suppliesFluidTo
... )
>>>
>>> # This matches various sensor placements:
>>> # coil -> duct_section -> sensor
>>> # coil -> mixing_box -> sensor
>>> # coil -> damper -> sensor

Multi-type node connections (common pattern):

>>> # Node that can match multiple equipment types
>>> equipment_node = Node(cls=(
...     core.namespace.S4BLDG.Pump,
...     core.namespace.S4BLDG.Fan,
...     core.namespace.S4BLDG.Compressor
... ))
>>> pipe_or_duct = Node(cls=(
...     core.namespace.S4BLDG.Pipe,
...     core.namespace.S4BLDG.Duct
... ))
>>>
>>> # SinglePath for flexible fluid/air distribution
>>> distribution_path = SinglePath(
...     subject=equipment_node,
...     object=pipe_or_duct,
...     predicate=core.namespace.FSO.suppliesFluidTo
... )
>>>
>>> # This allows matching:
>>> # pump -> valve -> pipe
>>> # fan -> damper -> duct
>>> # compressor -> expansion_valve -> pipe

Flexible system topology traversal:

>>> # Building space connections with intermediate zones
>>> building_space1 = Node(cls=core.namespace.S4BLDG.BuildingSpace)
>>> building_space2 = Node(cls=core.namespace.S4BLDG.BuildingSpace)
>>>
>>> # SinglePath for adjacent zone connections through shared systems
>>> adjacent_connection = SinglePath(
...     subject=building_space1,
...     object=building_space2,
...     predicate=core.namespace.S4SYST.connectedTo
... )
>>>
>>> # This can traverse:
>>> # space1 -> shared_duct_system -> space2
>>> # space1 -> common_equipment -> space2
>>> # space1 -> thermal_bridge -> space2
apply(sm_subject, sm_object, ruleset, sp_sm_map_list=None, master_rule=None)[source]
reset()[source]
PRIORITY = 1
class Translator[source]

Bases: object

Class for ontology-driven automated model generation and calibration in building energy systems.

Parameters:
  • sim2sem_map – Dictionary mapping simulation model components to semantic model instances

  • sem2sim_map – Dictionary mapping semantic model instances to simulation model components

  • instance_to_group_map – Dictionary mapping simulation model components to their corresponding signature pattern groups

This class implements a general methodology for translating semantic models of building systems into executable simulation models, as described in:

Jakob Bjørnskov, Muhyiddine Jradi, Michael Wetter, “Automated model generation and parameter estimation of building energy models using an ontology-based framework,” Energy and Buildings, Volume 329, 2025, 115228. https://doi.org/10.1016/j.enbuild.2024.115228

Overview

The Translator enables the automated generation and calibration of building energy simulation models by leveraging semantic models and a library of reusable component models. The approach is based on the following key concepts:

  • Semantic Models: Structured, machine-readable representations of building systems, including topology, equipment, and sensor placement, based on ontologies such as SAREF, SAREF4BLDG, SAREF4SYST, and FSO.

  • Component Model Library: Modular simulation components (e.g., fans, coils, controllers) each defined with a signature pattern that describes the semantic context in which the model applies.

  • Signature Patterns: Generalized graph patterns (subject-predicate-object triples) that specify how component models map to semantic model instances, including rules for optionality and traversal.

  • Automated Model Generation: The Translator searches the semantic model for matches to signature patterns, instantiates the corresponding component models, and connects them to form a complete simulation model.

Pattern Matching Process

The core of the Translator is the pattern matching process, which identifies how signature patterns map to semantic model instances. This process involves:

  1. Graph Representation: Both the semantic model and signature patterns are represented as directed graphs with labeled nodes and edges.

  2. Pattern Matching: The Translator searches for subgraph isomorphisms between signature patterns and the semantic model.

  3. Rule Application: Different types of rules (Exact, SinglePath, MultiPath, Optional) determine how pattern elements map to semantic model elements.

System overview showing components and their relationships

Example of a semantic model: This diagram shows the relationships between various components in a building system, including fans, coils, sensors, meters, valves, and pumps. The different line styles represent different types of relationships (suppliesFluidTo, observes, hasValue, etc.).

Signature patterns showing different component configurations

Example of signature patterns: This diagram illustrates five distinct patterns (p1-p5) of interconnected components, each representing different configurations or sub-systems within a larger model. The patterns show how generic component types (Fan, Sensor, Coil, etc.) can be arranged in different ways to match various system configurations.

Pattern matching process showing how signatures map to system components

Example of pattern matching: This diagram shows how signature patterns are matched against the semantic model. The central graph represents the actual system components, while the surrounding “Match of signature pX” blocks show how generic pattern elements (n₁, n₂, etc.) map to specific system components. The dotted lines connect pattern elements to their corresponding system instances.

Methodology

  1. Pattern Matching: Signature patterns are matched against the semantic model using a graph search algorithm, identifying all valid contexts for each component model.

  2. Model Instantiation: For each match, the corresponding component model is instantiated and mapped to the relevant semantic model instances.

  3. Model Assembly: Components are connected according to the relationships defined in the semantic model and signature patterns, resulting in an executable simulation model.

Mathematical Formulation

The task of searching for signature patterns in the semantic model is formulated as a subgraph isomorphism problem:

Given the pattern signature represented by the graph \(p = (V_p, E_p, L_p)\) and the semantic model represented by the graph \(G = (V_G, E_G, L_G)\), find the map \(f: V_p \rightarrow V_G\) such that:

\[L_G(f(u)) \subseteq L_p(u) \quad \forall u \in V_p\]
\[L_p(u, v) = L_G(f(u), f(v)) \quad \forall (u, v) \in E_p\]
\[(f(u), f(v)) \subseteq E_G \quad \forall (u, v) \in E_p\]
Where:
  • \(L_G(f(u)) \subseteq L_p(u)\) requires that the node label (ontology class) of the semantic model is a subset of the pattern node label

  • \(L_p(u, v) = L_G(f(u), f(v))\) ensures that the edge label (ontology predicate) of the semantic model matches the pattern edge label

  • \((f(u), f(v)) \subseteq E_G\) ensures that the mapped pattern edge also exists in the semantic model

For each match found, a map \(f_i\) is generated, and the corresponding component model is instantiated.

Rule Types

The Translator supports several types of rules for pattern matching:

  • Exact: Requires exact matches between pattern and semantic model elements

  • SinglePath: Allows traversal along a single path in the semantic model

  • MultiPath: Allows traversal along multiple paths in the semantic model

  • Optional: Makes pattern elements optional (may or may not be present)

These rules are combined to create flexible signature patterns that can match various system configurations while maintaining the integrity of the model structure.

Examples

>>> import twin4build as tb
>>> sem_model = tb.SemanticModel("path/to/semantic_model.ttl") # or web address
>>> translator = tb.Translator()
>>> sim_model = translator.translate(sem_model)
>>> sim_model.visualize()
translate(semantic_model, systems_=None)[source]

Translate semantic model to simulation model using pattern matching

Parameters:
  • systems – List of system types to match against

  • semantic_model (SemanticModel) – The semantic model to translate

Return type:

SimulationModel

Returns:

SimulationModel instance with matched components

property sem2sim_map
property sim2sem_map