Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion 3rdParty
Submodule 3rdParty updated 1 files
+1 −0 CMakeLists.txt
9 changes: 9 additions & 0 deletions src/OMSimulatorLib/Component.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#include "ResultWriter.h"
#include "SignalDerivative.h"
#include "Snapshot.h"
#include "Variable.h"
#include "OMSimulator/Types.h"
#include "Values.h"

Expand Down Expand Up @@ -79,6 +80,14 @@ namespace oms
virtual oms_status_enu_t terminate() = 0;
virtual oms_status_enu_t updateSignals(ResultWriter& resultWriter) = 0;
virtual Variable* getVariable(const ComRef& cref) = 0;
virtual oms_status_enu_t getVariableType(const ComRef& cref, oms_signal_type_enu_t& type)
{
Variable* var = getVariable(cref);
if (!var)
return oms_status_error;
type = var->getType();
return oms_status_ok;
}

virtual bool getCanGetAndSetState() { return false; }
virtual const FMUInfo* getFMUInfo() const { return nullptr; }
Expand Down
4 changes: 2 additions & 2 deletions src/OMSimulatorLib/ComponentFMU3CS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ oms_status_enu_t oms::ComponentFMU3CS::instantiate()
time = getModel().getStartTime();

double relativeTolerance = 0.0;
dynamic_cast<SystemWC*>(getParentSystem())->getTolerance(&relativeTolerance);
getParentSystem()->getTolerance(&relativeTolerance);

fmi3Status status_ = fmi3_enterInitializationMode(fmu, fmi3False, relativeTolerance, time, fmi3False, getModel().getStopTime());

Expand Down Expand Up @@ -882,7 +882,7 @@ oms_status_enu_t oms::ComponentFMU3CS::reset()
// enterInitialization
time = getModel().getStartTime();
double relativeTolerance = 0.0;
dynamic_cast<SystemWC*>(getParentSystem())->getTolerance(&relativeTolerance);
getParentSystem()->getTolerance(&relativeTolerance);

fmi3Status status_ = fmi3_enterInitializationMode(fmu, fmi3False, relativeTolerance, time, fmi3True, getModel().getStopTime());
if (fmi3OK != fmistatus) return logError_FMUCall("fmi3_enterInitializationMode", this);
Expand Down
4 changes: 2 additions & 2 deletions src/OMSimulatorLib/ComponentFMU3ME.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -675,7 +675,7 @@ oms_status_enu_t oms::ComponentFMU3ME::instantiate()
const double& startTime = getModel().getStartTime();

double relativeTolerance = 0.0;
dynamic_cast<SystemSC*>(getParentSystem())->getTolerance(&relativeTolerance);
getParentSystem()->getTolerance(&relativeTolerance);

fmi3Status status_ = fmi3_enterInitializationMode(fmu, fmi3False, relativeTolerance, startTime, fmi3False, getModel().getStopTime());

Expand Down Expand Up @@ -913,7 +913,7 @@ oms_status_enu_t oms::ComponentFMU3ME::reset()
// enterInitialization
const double& startTime = getModel().getStartTime();
double relativeTolerance = 0.0;
dynamic_cast<SystemSC3*>(getParentSystem())->getTolerance(&relativeTolerance);
getParentSystem()->getTolerance(&relativeTolerance);

fmi3Status status_ = fmi3_enterInitializationMode(fmu, fmi3False, relativeTolerance, startTime, fmi3True, getModel().getStopTime());
if (fmi3OK != fmistatus) return logError_FMUCall("fmi3_enterInitializationMode", this);
Expand Down
4 changes: 2 additions & 2 deletions src/OMSimulatorLib/ComponentFMUCS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,7 @@ oms_status_enu_t oms::ComponentFMUCS::instantiate()
// enterInitialization
time = getModel().getStartTime();
double relativeTolerance = 0.0;
dynamic_cast<SystemWC*>(getParentSystem())->getTolerance(&relativeTolerance);
getParentSystem()->getTolerance(&relativeTolerance);

fmi2Status status = fmi2_setupExperiment(fmu, fmi2True, relativeTolerance, time, fmi2False, 1.0);
if (fmi2OK != status) return logError_FMUCall("fmi2_setupExperiment", this);
Expand Down Expand Up @@ -868,7 +868,7 @@ oms_status_enu_t oms::ComponentFMUCS::reset()
// enterInitialization
time = getModel().getStartTime();
double relativeTolerance = 0.0;
dynamic_cast<SystemWC*>(getParentSystem())->getTolerance(&relativeTolerance);
getParentSystem()->getTolerance(&relativeTolerance);
fmistatus = fmi2_setupExperiment(fmu, fmi2True, relativeTolerance, time, fmi2False, 1.0);
if (fmi2OK != fmistatus) return logError_FMUCall("fmi2_setupExperiment", this);

Expand Down
7 changes: 4 additions & 3 deletions src/OMSimulatorLib/ComponentFMUME.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
#include <regex>
#include <unordered_set>
#include <cmath>

#include <iostream>
oms::ComponentFMUME::ComponentFMUME(const ComRef& cref, System* parentSystem, const std::string& fmuPath)
: oms::Component(cref, oms_component_fmu, parentSystem, fmuPath), fmuInfo(fmuPath)
{
Expand Down Expand Up @@ -704,7 +704,8 @@ oms_status_enu_t oms::ComponentFMUME::instantiate()
// enterInitialization
const double& startTime = getModel().getStartTime();
double relativeTolerance = 0.0;
dynamic_cast<SystemSC*>(getParentSystem())->getTolerance(&relativeTolerance);
getParentSystem()->getTolerance(&relativeTolerance);

fmi2Status status = fmi2_setupExperiment(fmu, fmi2True, relativeTolerance, startTime, fmi2False, 1.0);
if (fmi2OK != status) return logError_FMUCall("fmi2_setupExperiment", this);

Expand Down Expand Up @@ -929,7 +930,7 @@ oms_status_enu_t oms::ComponentFMUME::reset()
// enterInitialization
const double& startTime = getModel().getStartTime();
double relativeTolerance = 0.0;
dynamic_cast<SystemSC*>(getParentSystem())->getTolerance(&relativeTolerance);
getParentSystem()->getTolerance(&relativeTolerance);
fmistatus = fmi2_setupExperiment(fmu, fmi2True, relativeTolerance, startTime, fmi2False, 1.0);
if (fmi2OK != fmistatus) return logError_FMUCall("fmi2_setupExperiment", this);

Expand Down
2 changes: 2 additions & 0 deletions src/OMSimulatorLib/ComponentTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ namespace oms
oms_status_enu_t setTime(double time) {this->time = time; return oms_status_ok;}

Variable* getVariable(const ComRef& cref) {logError_NotImplemented; return NULL;}
// All ComponentTable signals are interpolated series values (see getReal/getInteger/getBoolean below).
oms_status_enu_t getVariableType(const ComRef& cref, oms_signal_type_enu_t& type) {type = oms_signal_type_real; return oms_status_ok;}
oms_status_enu_t getReal(const ComRef& cref, double& value);
oms_status_enu_t getRealOutputDerivative(const ComRef& cref, SignalDerivative& value);
oms_status_enu_t getInteger(const ComRef& cref, int& value);
Expand Down
6 changes: 1 addition & 5 deletions src/OMSimulatorLib/System.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,7 @@ oms_status_enu_t oms::System::getVariableType(const ComRef& cref, oms_signal_typ

auto component = components.find(head);
if (component != components.end())
{
oms::Variable * var = component->second->getVariable(tail);
value = var->getType();
return oms_status_ok;
}
return component->second->getVariableType(tail, value);

// check connectors
for (auto& connector : connectors)
Expand Down
43 changes: 39 additions & 4 deletions src/OMSimulatorLib/SystemSC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,9 @@ int oms::cvode_rhs(realtype t, N_Vector y, N_Vector ydot, void* user_data)
{
SystemSC* system = (SystemSC*)user_data;
oms_status_enu_t status;
fmi2Status fmistatus;

// update states in FMUs
for (size_t i=0, j_y=0, j_ydot=0; i < system->fmus.size(); ++i)
// set time and states in all FMUs for this residual evaluation
for (size_t i=0, j_y=0; i < system->fmus.size(); ++i)
{
system->fmus[i]->setTime(t);

Expand All @@ -68,8 +67,25 @@ int oms::cvode_rhs(realtype t, N_Vector y, N_Vector ydot, void* user_data)
// set states
status = system->fmus[i]->setContinuousStates(system->states[i]);
if (oms_status_ok != status) return status;
}

// Propagate connected outputs into inputs (e.g. algebraic feedthrough across
// FMU boundaries) so that the derivatives below see coupling inputs that are
// consistent with the current state guess, instead of the value left over
// from the last accepted step. Without this, tightly-coupled FMUs (e.g. two
// FMUs closing an algebraic loop through connected signals) only get their
// coupling updated once per accepted step, which is an explicit one-step-
// delayed (Jacobi-style) coupling hidden inside what should be a fully
// implicit combined ODE solve, and can destabilize the integration.
status = system->updateInputs(system->simulationGraph);
if (oms_status_ok != status) return status;

// get state derivatives
for (size_t i=0, j_ydot=0; i < system->fmus.size(); ++i)
{
if (0 == system->nStates[i])
continue;

// get state derivatives
status = system->fmus[i]->getDerivatives(system->states_der[i]);
if (oms_status_ok != status) return status;

Expand Down Expand Up @@ -537,6 +553,25 @@ oms_status_enu_t oms::SystemSC::reset()
solverData.cvode.mem = nullptr;
}

// Free arrays allocated by initialize() and clear the per-FMU tracking
// vectors it populated, so a subsequent initialize() call starts fresh
// instead of appending duplicate entries for every FMU (which corrupts
// solver setup and causes lifecycle calls like enterContinuousTimeMode()
// to be issued twice for the same FMU).
for (double* ptr : states) free(ptr);
for (double* ptr : states_der) free(ptr);
for (double* ptr : states_nominal) free(ptr);
for (double* ptr : event_indicators) free(ptr);
for (double* ptr : event_indicators_prev) free(ptr);
fmus.clear();
nStates.clear();
nEventIndicators.clear();
states.clear();
states_der.clear();
states_nominal.clear();
event_indicators.clear();
event_indicators_prev.clear();

return oms_status_ok;
}

Expand Down
42 changes: 38 additions & 4 deletions src/OMSimulatorLib/SystemSC3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,9 @@ int oms::cvode_rhs3(realtype t, N_Vector y, N_Vector ydot, void* user_data)
{
SystemSC3* system = (SystemSC3*)user_data;
oms_status_enu_t status;
fmi3Status fmistatus;

// update states in FMUs
for (size_t i=0, j_y=0, j_ydot=0; i < system->fmus.size(); ++i)
// set time and states in all FMUs for this residual evaluation
for (size_t i=0, j_y=0; i < system->fmus.size(); ++i)
{
system->fmus[i]->setTime(t);

Expand All @@ -67,8 +66,24 @@ int oms::cvode_rhs3(realtype t, N_Vector y, N_Vector ydot, void* user_data)
// set states
status = system->fmus[i]->setContinuousStates(system->states[i]);
if (oms_status_ok != status) return status;
}

// Propagate connected outputs into inputs (e.g. algebraic feedthrough across
// FMU boundaries) so that the derivatives below see coupling inputs that are
// consistent with the current state guess, instead of the value left over
// from the last accepted step. Without this, tightly-coupled FMUs only get
// their coupling updated once per accepted step, which is an explicit
// one-step-delayed (Jacobi-style) coupling hidden inside what should be a
// fully implicit combined ODE solve, and can destabilize the integration.
status = system->updateInputs(system->simulationGraph);
if (oms_status_ok != status) return status;

// get state derivatives
for (size_t i=0, j_ydot=0; i < system->fmus.size(); ++i)
{
if (0 == system->nStates[i])
continue;

// get state derivatives
status = system->fmus[i]->getDerivatives(system->states_der[i]);
if (oms_status_ok != status) return status;

Expand Down Expand Up @@ -531,6 +546,25 @@ oms_status_enu_t oms::SystemSC3::reset()
solverData.cvode.mem = nullptr;
}

// Free arrays allocated by initialize() and clear the per-FMU tracking
// vectors it populated, so a subsequent initialize() call starts fresh
// instead of appending duplicate entries for every FMU (which corrupts
// solver setup and causes lifecycle calls like enterContinuousTimeMode()
// to be issued twice for the same FMU).
for (double* ptr : states) free(ptr);
for (double* ptr : states_der) free(ptr);
for (double* ptr : states_nominal) free(ptr);
for (double* ptr : event_indicators) free(ptr);
for (double* ptr : event_indicators_prev) free(ptr);
fmus.clear();
nStates.clear();
nEventIndicators.clear();
states.clear();
states_der.clear();
states_nominal.clear();
event_indicators.clear();
event_indicators_prev.clear();

return oms_status_ok;
}

Expand Down
2 changes: 1 addition & 1 deletion src/OMSimulatorPython/OMSimulatorPython3.bat
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@echo off

set PYTHONPATH=%~dp0;%~dp0\..\lib;%PYTHONPATH%
set PYTHONPATH=%~dp0;%~dp0\..\lib;%~dp0\..\lib\omc;%PYTHONPATH%
set PATH=%~dp0;%~dp0\..\bin;%PATH%

python3 %*
2 changes: 1 addition & 1 deletion src/OMSimulatorPython/OMSimulatorPython3.in
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ SCRIPTPATH=$(dirname "$SCRIPT")
OMS_LIB_DIR="${SCRIPTPATH}/../@CMAKE_INSTALL_LIBDIR@"

# Add the oms install lib directory to PYTHONPATH.
if [ -z "$PYTHONPATH" ]; then export PYTHONPATH="${OMS_LIB_DIR}"; else export PYTHONPATH="${OMS_LIB_DIR}":$PYTHONPATH; fi
if [ -z "$PYTHONPATH" ]; then export PYTHONPATH="${OMS_LIB_DIR}:${OMS_LIB_DIR}/omc"; else export PYTHONPATH="${OMS_LIB_DIR}:${OMS_LIB_DIR}/omc":$PYTHONPATH; fi

# On Linux, add the oms install LIB dir to LD_LIBRARY_PATH. That is where the shard lib is installed to.
# On Windows, add the oms install BIN dir to PATH. That is where the dll is installed to.
Expand Down
7 changes: 7 additions & 0 deletions src/OMSimulatorPython/capi.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ def __init__(self):
self.obj.oms_newModel.restype = ctypes.c_int
self.obj.oms_removeSignalsFromResults.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
self.obj.oms_removeSignalsFromResults.restype = ctypes.c_int
self.obj.oms_reset.argtypes = [ctypes.c_char_p]
self.obj.oms_reset.restype = ctypes.c_int
self.obj.oms_setCommandLineOption.argtypes = [ctypes.c_char_p]
self.obj.oms_setCommandLineOption.restype = ctypes.c_int
self.obj.oms_setConnectionLinearTransformation.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_double, ctypes.c_double]
Expand Down Expand Up @@ -379,4 +381,9 @@ def removeSignalsFromResults(self, cref, regex):
status = self.obj.oms_removeSignalsFromResults(cref.encode(), regex.encode())
return Status(status)

def reset(self, cref) -> Status:
'''Reset the model to the state right after instantiation, discarding initialization/simulation progress.'''
status = self.obj.oms_reset(cref.encode())
return Status(status)

Capi = capi()
11 changes: 3 additions & 8 deletions src/OMSimulatorPython/componenttable.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,12 @@ def __init__(self, name: CRef, filePath: Path | str, connectors : list | None =
self.name = name
self.filePath = filePath
self.connectors = connectors
self._solver = None
self.solver = None
self.elementgeometry = None


@property
def solver(self):
return self._solver

@solver.setter
def solver(self, cref: str):
self._solver = cref
def setSolver(self, cref: str):
self.solver = cref

def list(self, prefix=""):
print(f"{prefix} Table: {self.name}")
Expand Down
6 changes: 6 additions & 0 deletions src/OMSimulatorPython/instantiated_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,12 @@ def initialize(self):
if status != Status.ok:
raise RuntimeError(f"Failed to initialize model: {status}")

def reset(self):
'''Reset the model to the state right after instantiation, discarding initialization/simulation progress.'''
status = Capi.reset(self.modelName)
if status != Status.ok:
raise RuntimeError(f"Failed to reset model: {status}")

def simulate(self):
status = Capi.simulate(self.modelName)
if status != Status.ok:
Expand Down
4 changes: 2 additions & 2 deletions testsuite/dcmotor/dcmotor.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,8 @@
## info: maximum step size for 'model.root': 0.001000
## info: Result file: dc_motor_res.mat (bufferSize=10)
## info: Final Statistics for 'model.root':
## NumSteps = 10663 NumRhsEvals = 10833 NumLinSolvSetups = 606
## NumNonlinSolvIters = 10832 NumNonlinSolvConvFails = 0 NumErrTestFails = 26
## NumSteps = 10030 NumRhsEvals = 10044 NumLinSolvSetups = 511
## NumNonlinSolvIters = 10043 NumNonlinSolvConvFails = 0 NumErrTestFails = 1
## info: 3 warnings
## info: 0 errors
## endResult
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
## status: correct
## teardown_command: rm -rf DualMassOscillator.ssp DualMassOscillator_res.mat
## teardown_command: rm -rf DualMassOscillator1.ssp DualMassOscillator1_res.mat
## linux: yes
## ucrt64: yes
## win: yes
Expand All @@ -24,12 +24,12 @@
model.addConnection(CRef('default', 'system1', 'v1'), CRef('default', 'system2', 'v1'))
model.addConnection(CRef('default', 'system1', 'a1'), CRef('default', 'system2', 'a1'))

model.export('DualMassOscillator.ssp')
model.export('DualMassOscillator1.ssp')

model2 = SSP('DualMassOscillator.ssp')
model2 = SSP('DualMassOscillator1.ssp')

instantiated_model = model2.instantiate() ## internally generate the json file and also set the model state like virgin,
instantiated_model.setResultFile("DualMassOscillator_res.mat")
instantiated_model.setResultFile("DualMassOscillator1_res.mat")
instantiated_model.setStopTime(10)

instantiated_model.setFixedStepSize(1e-4)
Expand All @@ -52,7 +52,7 @@
instantiated_model.delete()

## Result:
## info: Result file: DualMassOscillator_res.mat (bufferSize=10)
## info: Result file: DualMassOscillator1_res.mat (bufferSize=10)
## info: Initialization
## info: default.system1.x1: 0.0
## info: default.system2.x2: 0.5
Expand Down
Loading