From 00ce771060ad396692f59bda611bd04a9ec40ffe Mon Sep 17 00:00:00 2001 From: arun3688 Date: Tue, 1 Sep 2026 16:14:10 +0200 Subject: [PATCH 1/9] check fmi requirements when setting value in different modes --- src/OMSimulatorPython/fmu.py | 56 ++++++++++++++++++++- src/OMSimulatorPython/instantiated_model.py | 34 +++++++++++++ src/OMSimulatorPython/variable.py | 3 ++ 3 files changed, 91 insertions(+), 2 deletions(-) diff --git a/src/OMSimulatorPython/fmu.py b/src/OMSimulatorPython/fmu.py index 9e8d7c041..120bc4b48 100644 --- a/src/OMSimulatorPython/fmu.py +++ b/src/OMSimulatorPython/fmu.py @@ -61,6 +61,49 @@ 'ma':6 } +## default initial attribute table for FMI 2.0, based on variability and causality +initialDefaultTableFmi2 = { + "fixed": { + "input": "unknown", + "output": "unknown", + "parameter": "exact", + "calculatedParameter": "calculated", + "local": "calculated", + "independent": "unknown", + }, + "tunable": { + "input": "unknown", + "output": "unknown", + "parameter": "exact", + "calculatedParameter": "calculated", + "local": "calculated", + "independent": "unknown", + }, + "constant": { + "input": "unknown", + "output": "exact", + "parameter": "unknown", + "calculatedParameter": "exact", + "local": "exact", + "independent": "unknown", + }, + "discrete": { + "input": "unknown", + "output": "calculated", + "parameter": "unknown", + "calculatedParameter": "calculated", + "local": "calculated", + "independent": "unknown", + }, + "continuous": { + "input": "unknown", + "output": "calculated", + "parameter": "unknown", + "calculatedParameter": "calculated", + "local": "calculated", + "independent": "unknown", + }, +} class FMU: def __init__(self, fmu_path: Union[str, Path], instanceName: str = None): '''Initialize the FMU by loading modelDescription.xml from the FMU archive.''' @@ -263,6 +306,9 @@ def _get(attr, default): 'stepSize': _get('stepSize', 1e-3), } + def _getInitialAttribute(self, variability, causality): + return initialDefaultTableFmi2.get(variability, {}).get(causality) + def _parse_variables_fmi2(self, model_description): '''Parses variables from the ModelVariables section of modelDescription.xml''' scalar_variables = model_description.xpath('//ModelVariables/ScalarVariable') @@ -272,7 +318,7 @@ def _parse_variables_fmi2(self, model_description): value_reference = scalar_var.get('valueReference') causality = scalar_var.get('causality', 'local') variability = scalar_var.get('variability', 'continuous') - + initial = scalar_var.get('initial') if scalar_var.get('initial') is not None else self._getInitialAttribute(variability, causality) var_type = None unit = None start = None @@ -327,7 +373,7 @@ def _parse_variables_fmi3(self, model_description): value_reference = element.get("valueReference") causality = element.get("causality", "local") variability = element.get("variability", "continuous") - initial = element.get("initial") + initial = element.get('initial') if element.get('initial') is not None else self._getInitialAttribute(variability, causality) start = element.get("start") derivative_index = int(element.get('derivative', '-1')) declaredType = element.get('declaredType') @@ -418,6 +464,12 @@ def makeConnectors(self): connectors.append(connector) return connectors + def getVariableByName(self, name: str) -> Variable | None: + for var in self.variables: + if str(var.name) == name: + return var + return None + def varExist(self, cref: str) -> bool: return any(var.name == cref for var in self.variables) diff --git a/src/OMSimulatorPython/instantiated_model.py b/src/OMSimulatorPython/instantiated_model.py index d9612309e..a650d49a5 100644 --- a/src/OMSimulatorPython/instantiated_model.py +++ b/src/OMSimulatorPython/instantiated_model.py @@ -41,6 +41,7 @@ import json import tempfile from enum import Enum +import warnings class SolverType(Enum): '''Enumeration for solver method to map with c api.''' @@ -74,6 +75,7 @@ def __init__(self, json_description, ssdName: str, system: System, resources: di self.ssdName = ssdName self.resources = resources self.fmuInstantitated = False + self.fmuInitialized = False Capi.setSuppressPath() # Set the temporary directory @@ -440,8 +442,39 @@ def dumpApiCalls(self): """Returns the generated API calls as a string.""" return "\n".join(self.apiCall) + def checkfmiRequirements(self, cref: CRef): + """Checks if a component variable meets the FMI requirements.""" + ## Last element is the variable name + variable_name = cref.names[-1] + ## The second last element is the component name + component_name = cref.names[-2] + component = self.system.elements.get(CRef(component_name)) + + # Top-level system variable: no FMI check required + if component is None: + return True + + variable = component.fmu.getVariableByName(variable_name) + + if variable and self.fmuInitialized: + if not variable.isInput() or not variable.isContinuous(): + warnings.warn( + f"Cannot set variable '{variable_name}' after the FMU " + f"is initialized. Only variables with " + f"causality='input' and variability='continuous' can " + f"be set at this stage. Variable '{variable_name}' has " + f"causality='{variable.causality.name}' and " + f"variability='{variable.variability}'",RuntimeWarning) + return False + + return True + def setValue(self, cref: CRef, value): """Sets a value for a specific CRef in the model.""" + + if not self.checkfmiRequirements(cref): + return + name = ".".join(cref.names) ## check for top level connectors with alias names first @@ -544,6 +577,7 @@ def initialize(self): status = Capi.initialize(self.modelName) if status != Status.ok: raise RuntimeError(f"Failed to initialize model: {status}") + self.fmuInitialized = True def reset(self): '''Reset the model to the state right after instantiation, discarding initialization/simulation progress.''' diff --git a/src/OMSimulatorPython/variable.py b/src/OMSimulatorPython/variable.py index 1a6b405b1..d969cedcd 100644 --- a/src/OMSimulatorPython/variable.py +++ b/src/OMSimulatorPython/variable.py @@ -134,3 +134,6 @@ def isParameter(self): def isCalculatedParameter(self): return self.causality == Causality.calculatedParameter + + def isContinuous(self): + return self.variability == "continuous" \ No newline at end of file From 454722c65529f2264f99929adf1920468b2a5937 Mon Sep 17 00:00:00 2001 From: arun3688 Date: Tue, 1 Sep 2026 16:24:04 +0200 Subject: [PATCH 2/9] store initial attribute in Variable class --- src/OMSimulatorPython/fmu.py | 4 ++-- src/OMSimulatorPython/variable.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/OMSimulatorPython/fmu.py b/src/OMSimulatorPython/fmu.py index 120bc4b48..50ca83799 100644 --- a/src/OMSimulatorPython/fmu.py +++ b/src/OMSimulatorPython/fmu.py @@ -339,7 +339,7 @@ def _parse_variables_fmi2(self, model_description): self._states.append(self._variables[derivative_index - 1]) # Create and store the variable - variable = Variable(name, description, value_reference, causality, variability, var_type, unit, start, declaredType) + variable = Variable(name, description, value_reference, causality, variability, initial, var_type, unit, start, declaredType) # Assign unit definitions if applicable if unit: @@ -392,7 +392,7 @@ def _parse_variables_fmi3(self, model_description): # print(f"var_name: {name}, var_type: {var_type}, causality: {causality}, variability: {variability}, initial: {initial}, start: {start}") # Create and store the variable - variable = Variable(name, description, value_reference, causality, variability, var_type, unit, start, declaredType) + variable = Variable(name, description, value_reference, causality, variability, initial, var_type, unit, start, declaredType) # Assign unit definitions if applicable if unit: diff --git a/src/OMSimulatorPython/variable.py b/src/OMSimulatorPython/variable.py index d969cedcd..03d79c776 100644 --- a/src/OMSimulatorPython/variable.py +++ b/src/OMSimulatorPython/variable.py @@ -110,12 +110,13 @@ class Binary(FMI3Type): pass class Variable: '''Class for storing variable information''' - def __init__(self, name: Union[str, CRef], description : str, valueReference: Union[str, int], causality, variability, signal_type, unit, start_value, declaredType): + def __init__(self, name: Union[str, CRef], description : str, valueReference: Union[str, int], causality, variability, initial, signal_type, unit, start_value, declaredType): self.name = CRef(name) self.description = description self.valueReference = int(valueReference) self.causality = causality if isinstance(causality, Causality) else Causality[causality] self.variability = variability + self.initial = initial self.signal_type = signal_type if isinstance(signal_type, SignalType) else SignalType[signal_type] self.unit = unit self.modelDescriptionStartValue = start_value From 1eb904d17710e469fadf0ce7eb41743902a788a2 Mon Sep 17 00:00:00 2001 From: arun3688 Date: Tue, 1 Sep 2026 21:12:41 +0200 Subject: [PATCH 3/9] check for component instance --- src/OMSimulatorPython/instantiated_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OMSimulatorPython/instantiated_model.py b/src/OMSimulatorPython/instantiated_model.py index a650d49a5..70b4b2be3 100644 --- a/src/OMSimulatorPython/instantiated_model.py +++ b/src/OMSimulatorPython/instantiated_model.py @@ -451,7 +451,7 @@ def checkfmiRequirements(self, cref: CRef): component = self.system.elements.get(CRef(component_name)) # Top-level system variable: no FMI check required - if component is None: + if not isinstance(component, Component): return True variable = component.fmu.getVariableByName(variable_name) From 460b453f2051c58e3c3fed4c978f09fa4e464265 Mon Sep 17 00:00:00 2001 From: arun3688 Date: Wed, 2 Sep 2026 00:36:17 +0200 Subject: [PATCH 4/9] check fmi requirement during initialization --- src/OMSimulatorPython/instantiated_model.py | 26 +++++++++++++++------ src/OMSimulatorPython/variable.py | 5 +++- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/OMSimulatorPython/instantiated_model.py b/src/OMSimulatorPython/instantiated_model.py index 70b4b2be3..bbdee34aa 100644 --- a/src/OMSimulatorPython/instantiated_model.py +++ b/src/OMSimulatorPython/instantiated_model.py @@ -74,7 +74,7 @@ def __init__(self, json_description, ssdName: str, system: System, resources: di self.system = system self.ssdName = ssdName self.resources = resources - self.fmuInstantitated = False + self.fmuInstantiated = False self.fmuInitialized = False Capi.setSuppressPath() @@ -255,7 +255,7 @@ def __init__(self, json_description, ssdName: str, system: System, resources: di status = Capi.instantiate(self.modelName) if status != Status.ok: raise RuntimeError(f"Failed to instantiate model: {status}") - self.fmuInstantitated = True + self.fmuInstantiated = True ## set start and stop time from ssp self.setStartTime(float(config["simulation settings"]['start time'])) self.setStopTime(float(config["simulation settings"]['stop time'])) @@ -456,6 +456,18 @@ def checkfmiRequirements(self, cref: CRef): variable = component.fmu.getVariableByName(variable_name) + if variable and self.fmuInstantiated and not self.fmuInitialized: + print(f"info: Checking FMI requirements for variable '{variable_name}' in component '{component_name} {variable.initial}' after FMU instantiation.", flush=True) + if not variable.isInput() and not variable.isExact(): + warnings.warn( + f"Cannot set variable '{variable_name}' after the FMU " + f"is instantiated. Only variables with " + f"causality='input' or initial='exact' can " + f"be set at this stage. Variable '{variable_name}' has " + f"causality='{variable.causality.name}' and " + f"initial='{variable.initial}'",RuntimeWarning) + return False + if variable and self.fmuInitialized: if not variable.isInput() or not variable.isContinuous(): warnings.warn( @@ -630,7 +642,7 @@ def doStep(self): raise RuntimeError(f"Failed to do step: {status}") def setStopTime(self, stopTime: float): - if self.fmuInstantitated is False: + if self.fmuInstantiated is False: raise RuntimeError("FMU must be instantiated before setting stop time") status = Capi.setStopTime(self.modelName, stopTime) @@ -638,7 +650,7 @@ def setStopTime(self, stopTime: float): raise RuntimeError(f"Failed to set stop time: {status}") def setTolerance(self, tolerance: float): - if self.fmuInstantitated is False: + if self.fmuInstantiated is False: raise RuntimeError("FMU must be instantiated before setting tolerance") status = Capi.setTolerance(f"{self.modelName}.root", tolerance) @@ -646,7 +658,7 @@ def setTolerance(self, tolerance: float): raise RuntimeError(f"Failed to set tolerance: {status}") def setFixedStepSize(self, stepSize: float): - if self.fmuInstantitated is False: + if self.fmuInstantiated is False: raise RuntimeError("FMU must be instantiated before setting variable step size") status = Capi.setFixedStepSize(f"{self.modelName}.root", stepSize) @@ -654,7 +666,7 @@ def setFixedStepSize(self, stepSize: float): raise RuntimeError(f"Failed to set fixed step size: {status}") def setDcpPorts(self, masterPort: int, slavePort: int): - if self.fmuInstantitated is False: + if self.fmuInstantiated is False: raise RunTimeError("FMU must be instantiated before setting DCP ports") status = Capi.setDcpPorts(f"{self.modelName}.root", masterPort, slavePort) @@ -662,7 +674,7 @@ def setDcpPorts(self, masterPort: int, slavePort: int): raise RuntimeError(f"Failed to set DCP ports: {status}") def setVariableStepSize(self, initialStepSize: float, minimumStepSize: float, maximumStepSize: float): - if self.fmuInstantitated is False: + if self.fmuInstantiated is False: raise RuntimeError("FMU must be instantiated before setting variable step size") status = Capi.setVariableStepSize(f"{self.modelName}.root", initialStepSize, minimumStepSize, maximumStepSize) diff --git a/src/OMSimulatorPython/variable.py b/src/OMSimulatorPython/variable.py index 03d79c776..49cf747f3 100644 --- a/src/OMSimulatorPython/variable.py +++ b/src/OMSimulatorPython/variable.py @@ -137,4 +137,7 @@ def isCalculatedParameter(self): return self.causality == Causality.calculatedParameter def isContinuous(self): - return self.variability == "continuous" \ No newline at end of file + return self.variability == "continuous" + + def isExact(self): + return self.initial == "exact" \ No newline at end of file From d8a05f172fb67c1061d8255f1b0697befb82ef82 Mon Sep 17 00:00:00 2001 From: arun3688 Date: Wed, 2 Sep 2026 00:47:49 +0200 Subject: [PATCH 5/9] remove debug print --- src/OMSimulatorPython/instantiated_model.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/OMSimulatorPython/instantiated_model.py b/src/OMSimulatorPython/instantiated_model.py index bbdee34aa..325ef0227 100644 --- a/src/OMSimulatorPython/instantiated_model.py +++ b/src/OMSimulatorPython/instantiated_model.py @@ -457,7 +457,6 @@ def checkfmiRequirements(self, cref: CRef): variable = component.fmu.getVariableByName(variable_name) if variable and self.fmuInstantiated and not self.fmuInitialized: - print(f"info: Checking FMI requirements for variable '{variable_name}' in component '{component_name} {variable.initial}' after FMU instantiation.", flush=True) if not variable.isInput() and not variable.isExact(): warnings.warn( f"Cannot set variable '{variable_name}' after the FMU " From e4821529b73b2ca0d2d08d60d5cb752d12ae70c8 Mon Sep 17 00:00:00 2001 From: arun3688 Date: Wed, 2 Sep 2026 11:30:12 +0200 Subject: [PATCH 6/9] fix fmi requirement condition --- src/OMSimulatorPython/instantiated_model.py | 9 ++++++--- src/OMSimulatorPython/variable.py | 3 +++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/OMSimulatorPython/instantiated_model.py b/src/OMSimulatorPython/instantiated_model.py index 325ef0227..29507fe31 100644 --- a/src/OMSimulatorPython/instantiated_model.py +++ b/src/OMSimulatorPython/instantiated_model.py @@ -457,7 +457,8 @@ def checkfmiRequirements(self, cref: CRef): variable = component.fmu.getVariableByName(variable_name) if variable and self.fmuInstantiated and not self.fmuInitialized: - if not variable.isInput() and not variable.isExact(): + allowed = (variable.isInput() or variable.isExact()) + if not allowed: warnings.warn( f"Cannot set variable '{variable_name}' after the FMU " f"is instantiated. Only variables with " @@ -468,11 +469,13 @@ def checkfmiRequirements(self, cref: CRef): return False if variable and self.fmuInitialized: - if not variable.isInput() or not variable.isContinuous(): + allowed = (variable.isInput()) or (variable.isParameter() and variable.isTunable()) + if not allowed: warnings.warn( f"Cannot set variable '{variable_name}' after the FMU " f"is initialized. Only variables with " - f"causality='input' and variability='continuous' can " + f"causality='input' or " + f"causality='parameter' and variability='tunable' can " f"be set at this stage. Variable '{variable_name}' has " f"causality='{variable.causality.name}' and " f"variability='{variable.variability}'",RuntimeWarning) diff --git a/src/OMSimulatorPython/variable.py b/src/OMSimulatorPython/variable.py index 49cf747f3..73909c709 100644 --- a/src/OMSimulatorPython/variable.py +++ b/src/OMSimulatorPython/variable.py @@ -139,5 +139,8 @@ def isCalculatedParameter(self): def isContinuous(self): return self.variability == "continuous" + def isTunable(self): + return self.variability == "tunable" + def isExact(self): return self.initial == "exact" \ No newline at end of file From 750cd703a6c0fa7064681c3610fdfec27024d188 Mon Sep 17 00:00:00 2001 From: arun3688 Date: Wed, 2 Sep 2026 12:05:10 +0200 Subject: [PATCH 7/9] check fmirequirement for single fmu simulation --- src/OMSimulatorPython/fmu.py | 62 ++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/src/OMSimulatorPython/fmu.py b/src/OMSimulatorPython/fmu.py index 50ca83799..da14bef42 100644 --- a/src/OMSimulatorPython/fmu.py +++ b/src/OMSimulatorPython/fmu.py @@ -44,6 +44,7 @@ from OMSimulator.capi import Capi, Status from OMSimulator.cref import CRef from OMSimulator.enumeration import Enumeration +import warnings logger = logging.getLogger(__name__) @@ -136,7 +137,8 @@ def __init__(self, fmu_path: Union[str, Path], instanceName: str = None): self.appliedExperiment = {} self.apiCall = [] self.instanceName = instanceName - self.fmuInstantitated = False + self.fmuInstantiated = False + self.fmuInitialized = False self.mode = None # override the FMU's declared defaultExperiment; must be set before instantiate() # since they need to reach the FMU's fmi2SetupExperiment call, not just the @@ -644,33 +646,33 @@ def instantiate(self): status = Capi.instantiate(self.instanceName) if status != Status.ok: raise RuntimeError(f"Failed to instantiate model: {status}") - self.fmuInstantitated = True + self.fmuInstantiated = True self.apiCall.append(f'oms.instantiate("{self.instanceName}")') def setStartTime(self, startTime: float): - if self.fmuInstantitated is False: + if self.fmuInstantiated is False: raise RuntimeError("FMU must be instantiated before setting start time") status = Capi.setStartTime(self.instanceName, startTime) if status != Status.ok: raise RuntimeError(f"Failed to set start time: {status}") def setStopTime(self, stopTime: float): - if self.fmuInstantitated is False: + if self.fmuInstantiated is False: raise RuntimeError("FMU must be instantiated before setting stop time") status = Capi.setStopTime(self.instanceName, stopTime) if status != Status.ok: raise RuntimeError(f"Failed to set stop time: {status}") def setTolerance(self, tolerance: float): - if self.fmuInstantitated is False: + if self.fmuInstantiated is False: raise RuntimeError("FMU must be instantiated before setting tolerance") status = Capi.setTolerance(self.instanceName, tolerance) if status != Status.ok: raise RuntimeError(f"Failed to set tolerance: {status}") def setStepSize(self, stepSize: float): - if self.fmuInstantitated is False: + if self.fmuInstantiated is False: raise RuntimeError("FMU must be instantiated before setting variable step size") status = Capi.setVariableStepSize(self.instanceName, 1e-6, 1e-12, stepSize) if status != Status.ok: @@ -678,7 +680,7 @@ def setStepSize(self, stepSize: float): def setSolver(self, method: str): '''Set the ODE solver ('euler' or 'cvode'). Only applies to model-exchange FMUs.''' - if self.fmuInstantitated is False: + if self.fmuInstantiated is False: raise RuntimeError("FMU must be instantiated before setting the solver") if method not in _FMU_SOLVER: raise ValueError(f"Invalid solver '{method}': expected one of {sorted(_FMU_SOLVER)}") @@ -687,7 +689,7 @@ def setSolver(self, method: str): raise RuntimeError(f"Failed to set solver: {status}") def getValue(self, cref: str): - if self.fmuInstantitated is False: + if self.fmuInstantiated is False: raise RuntimeError("FMU must be instantiated before getting values") '''Get the value of a variable by its name.''' @@ -739,15 +741,48 @@ def _getString(self, cref: CRef): raise RuntimeError(f"Failed to get value for {cref}: {status}") return value + def checkfmiRequirements(self, cref: CRef): + """Checks if a component variable meets the FMI requirements.""" + variable = self.getVariableByName(str(cref)) + + if variable and self.fmuInstantiated and not self.fmuInitialized: + allowed = (variable.isInput() or variable.isExact()) + if not allowed: + warnings.warn( + f"Cannot set variable '{cref}' after the FMU " + f"is instantiated. Only variables with " + f"causality='input' or initial='exact' can " + f"be set at this stage. Variable '{cref}' has " + f"causality='{variable.causality.name}' and " + f"initial='{variable.initial}'",RuntimeWarning) + return False + + if variable and self.fmuInitialized: + allowed = (variable.isInput()) or (variable.isParameter() and variable.isTunable()) + if not allowed: + warnings.warn( + f"Cannot set variable '{cref}' after the FMU " + f"is initialized. Only variables with " + f"causality='input' or " + f"causality='parameter' and variability='tunable' can " + f"be set at this stage. Variable '{cref}' has " + f"causality='{variable.causality.name}' and " + f"variability='{variable.variability}'",RuntimeWarning) + return False + + return True + def setValue(self, cref: str, value): """Sets a value for a specific CRef in the model.""" - - if self.fmuInstantitated is False: + if self.fmuInstantiated is False: raise RuntimeError("FMU must be instantiated before setting values") if not self.varExist(CRef(cref)): raise KeyError(f"Variable '{cref}' does not exist in the FMU") + if not self.checkfmiRequirements(CRef(cref)): + return # Skip setting the value if FMI requirements are not met + mappedCrefs = f"{self.instanceName}.root.{self.instanceName}.{cref}" # Determine the variable type @@ -794,7 +829,7 @@ def _setString(self, mapped_cref: str, value: str): raise RuntimeError(f"Failed to set value for {mapped_cref}: {status}") def setResultFile(self, filename: str): - if self.fmuInstantitated is False: + if self.fmuInstantiated is False: raise RuntimeError("FMU must be instantiated before setting result file") status = Capi.setResultFile(self.instanceName, filename) @@ -802,15 +837,16 @@ def setResultFile(self, filename: str): raise RuntimeError(f"Failed to setResultFile {filename}: {status}") def initialize(self): - if self.fmuInstantitated is False: + if self.fmuInstantiated is False: raise RuntimeError("FMU must be instantiated before initialization") status = Capi.initialize(self.instanceName) if status != Status.ok: raise RuntimeError(f"Failed to initialize model: {status}") + self.fmuInitialized = True def simulate(self): - if self.fmuInstantitated is False: + if self.fmuInstantiated is False: raise RuntimeError("FMU must be instantiated before simulation") status = Capi.simulate(self.instanceName) From cb1fe2cfe9c1d703c58769c2957dceba58eb3a2a Mon Sep 17 00:00:00 2001 From: arun3688 Date: Wed, 2 Sep 2026 13:23:02 +0200 Subject: [PATCH 8/9] add support to set value before FMU instantiate --- src/OMSimulatorPython/fmu.py | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/OMSimulatorPython/fmu.py b/src/OMSimulatorPython/fmu.py index da14bef42..55f194b66 100644 --- a/src/OMSimulatorPython/fmu.py +++ b/src/OMSimulatorPython/fmu.py @@ -40,6 +40,7 @@ from OMSimulator.connector import Connector from OMSimulator.unit import Unit from OMSimulator.variable import Variable, SignalType +from OMSimulator.values import Values from OMSimulator import namespace, utils from OMSimulator.capi import Capi, Status from OMSimulator.cref import CRef @@ -137,6 +138,7 @@ def __init__(self, fmu_path: Union[str, Path], instanceName: str = None): self.appliedExperiment = {} self.apiCall = [] self.instanceName = instanceName + self.value = Values() self.fmuInstantiated = False self.fmuInitialized = False self.mode = None @@ -560,6 +562,30 @@ def splitModelName(self): else: return parts[-1] + def applyStartValues(self): + """ + Apply start values to the FMU's variables. set before instantiation. This method should be called after the FMU is loaded and before it is instantiated. + """ + for key, (value, type, _, _) in self.value.start_values.items(): + mappedCrefs = f"{self.instanceName}.root.{self.instanceName}.{key}" + #Determine the variable type + type, status = Capi.getVariableType(mappedCrefs) + if status != Status.ok: + raise RuntimeError(f"Failed to get variable type for {key}: {status}") + + match SignalType(type): + case SignalType.Real: # oms_signal_type_real + return self._setReal(mappedCrefs, value) + case SignalType.Integer: # oms_signal_type_integer + return self._setInteger(mappedCrefs, value) + case SignalType.Boolean: # oms_signal_type_boolean + return self._setBoolean(mappedCrefs, value) + case SignalType.String: # oms_signal_type_string + return self._setString(mappedCrefs, value) + case SignalType.Enumeration: # oms_signal_type_enumeration + return self._setInteger(mappedCrefs, value) # Treat enumeration as integer + case _: + raise TypeError(f"Unsupported type: {type}") def instantiate(self): '''Instantiate the FMU for simulation. @@ -642,7 +668,8 @@ def instantiate(self): status = Capi.setVariableStepSize(self.instanceName, 1e-6, 1e-12, self.appliedExperiment['stepSize']) if status != Status.ok: raise RuntimeError(f"Failed to set variable step size: {status}") - + ## apply start values before instantiation, so that initialization can use them before entering fmi2_enterInitializationMode + self.applyStartValues() status = Capi.instantiate(self.instanceName) if status != Status.ok: raise RuntimeError(f"Failed to instantiate model: {status}") @@ -772,14 +799,15 @@ def checkfmiRequirements(self, cref: CRef): return True - def setValue(self, cref: str, value): + def setValue(self, cref: str, value, unit=None, description = None): """Sets a value for a specific CRef in the model.""" - if self.fmuInstantiated is False: - raise RuntimeError("FMU must be instantiated before setting values") if not self.varExist(CRef(cref)): raise KeyError(f"Variable '{cref}' does not exist in the FMU") + if self.fmuInstantiated is False: + return self.value.setValue(cref, value, unit, description) + if not self.checkfmiRequirements(CRef(cref)): return # Skip setting the value if FMI requirements are not met From 49f9eb2489870ac88b1dbbbb85e5e0e9cac5b2d9 Mon Sep 17 00:00:00 2001 From: arun3688 Date: Wed, 2 Sep 2026 13:50:07 +0200 Subject: [PATCH 9/9] Trigger build