Skip to content

Commit d6918e1

Browse files
event model excange
1 parent 28fe6d3 commit d6918e1

File tree

6 files changed

+131
-5
lines changed

6 files changed

+131
-5
lines changed

examples/bouncingBall_me.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
from pythonfmu3 import Fmi3Causality, Fmi3Variability, Fmi3SlaveBase, ModelExchange, Fmi3Status, Float64, Fmi3Initial, Unit, Float64Type, Fmi3StepResult
2+
3+
from typing import List
4+
5+
6+
EVENT_EPS = 1e-12
7+
8+
class BouncingBall(Fmi3SlaveBase, ModelExchange):
9+
10+
def __init__(self, **kwargs):
11+
super().__init__(**kwargs)
12+
13+
self.author = "..."
14+
self.description = "Bouncing Ball"
15+
16+
self.time = 0.0
17+
self.h = 1.0
18+
self.v = 0.0
19+
self.derh = 0.0
20+
self.derv = 0.0
21+
self.g = -9.81
22+
self.e = 0.7
23+
self.v_min = 0.1
24+
25+
# define units
26+
unit1 = Unit(name="m", m=1)
27+
unit2 = Unit(name="m/s", m=1, s=-1)
28+
unit3 = Unit(name="m/s2", m=1, s=-2)
29+
self.register_units([unit1, unit2, unit3])
30+
31+
32+
self.register_variable(Float64("time", causality=Fmi3Causality.independent, variability=Fmi3Variability.continuous))
33+
34+
self.register_variable(Float64("ball.h", causality=Fmi3Causality.output, start=1, variability=Fmi3Variability.continuous, initial=Fmi3Initial.exact, unit=unit1.name),
35+
nested=False, has_event_indicator=True)
36+
self.register_variable(Float64("ball.derh", causality=Fmi3Causality.local, variability=Fmi3Variability.continuous, derivative=1, unit=unit2.name),
37+
nested=False)
38+
self.register_variable(Float64("ball.v", causality=Fmi3Causality.output, start=0, variability=Fmi3Variability.continuous, initial=Fmi3Initial.exact, unit=unit2.name),
39+
nested=False)
40+
self.register_variable(Float64("ball.derv", causality=Fmi3Causality.local, variability=Fmi3Variability.continuous, derivative=3, unit=unit3.name),
41+
nested=False)
42+
43+
self.register_variable(Float64("g", causality=Fmi3Causality.parameter, variability=Fmi3Variability.fixed, unit=unit3.name))
44+
self.register_variable(Float64("e", causality=Fmi3Causality.parameter, variability=Fmi3Variability.tunable))
45+
self.register_variable(Float64("v_min", variability=Fmi3Variability.constant, start=0.1))
46+
47+
48+
49+
def get_continuous_state_derivatives(self) -> List[float]:
50+
self.derh = self.v
51+
self.derv = self.g
52+
53+
def get_event_indicators(self) -> List[float]:
54+
z = [self.h]
55+
if self.h > -EVENT_EPS and self.h <=0 and self.v > 0:
56+
z[0] = -EVENT_EPS
57+
58+
return [self.h - 0.0]
59+
60+
def update_discrete_states(self):
61+
discrete_states_need_update = False
62+
terminate_simulation = False
63+
nominals_of_continuous_states_changed = False
64+
values_of_continuous_states_changed = False
65+
next_event_time_defined = False
66+
next_event_time = 0.0
67+
68+
if self.h <= 0 and self.v < 0:
69+
self.v = -self.e * self.v
70+
if abs(self.v) < self.v_min:
71+
self.v = 0.0
72+
terminate_simulation = True
73+
discrete_states_need_update = True
74+
75+
return Fmi3Status.ok, discrete_states_need_update, terminate_simulation

pythonfmu3/fmi3slave.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class Fmi3SlaveBase(object):
5050

5151
def __init__(self, **kwargs):
5252
self.vars = OrderedDict()
53+
self.event_indicators: List[int] = []
5354
self.instance_name = kwargs["instance_name"]
5455
self.resources = kwargs.get("resources", None)
5556
self.visible = kwargs.get("visible", False)
@@ -184,7 +185,8 @@ def to_xml(self, model_options: Dict[str, str] = dict()) -> Element:
184185
for v in initial_unknown:
185186
SubElement(structure, "InitialUnknown", attrib=dict(valueReference=str(v.value_reference)))
186187

187-
188+
for v in self.event_indicators:
189+
SubElement(structure, "EventIndicator", attrib=dict(valueReference=str(v)))
188190

189191
return root
190192

@@ -206,7 +208,7 @@ def __apply_start_value(self, var: ModelVariable):
206208
raise Exception(f"Unsupported type {type(var)}!")
207209
var.start = refs if len(getattr(var, "dimensions", [])) > 0 else refs[0]
208210

209-
def register_variable(self, var: ModelVariable, nested: bool = True, var_type: Any = None):
211+
def register_variable(self, var: ModelVariable, nested: bool = True, var_type: Any = None, has_event_indicator: bool = False):
210212
"""Register a variable as FMU interface.
211213
212214
Args:
@@ -238,6 +240,12 @@ def register_variable(self, var: ModelVariable, nested: bool = True, var_type: A
238240
if var_type:
239241
self.type_definitions[var_type.name] = var_type
240242
var.declared_type = var_type.name
243+
244+
if has_event_indicator:
245+
self.register_event_indicator(var.value_reference)
246+
247+
def register_event_indicator(self, vr):
248+
self.event_indicators.append(vr)
241249

242250
def setup_experiment(self, start_time: float):
243251
pass
@@ -416,6 +424,9 @@ def _set_fmu_state(self, state: Dict[str, Any]):
416424
if v.setter is not None:
417425
v.setter(value)
418426

427+
def get_number_of_event_indicators(self) -> int:
428+
return len(self.event_indicators)
429+
419430
def set_continuous_states(self, values: List[float]):
420431
offset = 0
421432
continuous_state_derivatives = list(

pythonfmu3/modelexchange.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,8 @@ class ModelExchange(ABC, metaclass=RequireTimeMeta):
2020
@abstractmethod
2121
def get_continuous_state_derivatives(self):
2222
"""Return the continuous state derivatives of the model."""
23-
pass
23+
pass
24+
25+
def get_event_indicators(self):
26+
"""Return the event indicators of the model."""
27+
return []

pythonfmu3/pythonfmu-export/src/cppfmu/cppfmu_cs.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ class SlaveInstance
141141
virtual void SetTime(cppfmu::FMIFloat64 time) = 0;
142142
virtual void GetNumberOfContinuousStates(std::size_t& nStates) const = 0;
143143
virtual void GetNumberOfEventIndicators(std::size_t& nEventIndicators) const = 0;
144+
virtual void GetEventIndicators(cppfmu::FMIFloat64* eventIndicators, std::size_t nEventIndicators) const = 0;
144145
virtual void UpdateDiscreteStates(
145146
cppfmu::FMIBoolean* discreteStatesNeedUpdate,
146147
cppfmu::FMIBoolean* terminateSimulation,

pythonfmu3/pythonfmu-export/src/cppfmu/fmi_functions.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,17 @@ fmi3Status fmi3GetEventIndicators(
622622
fmi3Float64 eventIndicators[],
623623
size_t nEventIndicators)
624624
{
625-
return fmi3OK;
625+
const auto component = reinterpret_cast<Component*>(c);
626+
try {
627+
component->slave->GetEventIndicators(eventIndicators, nEventIndicators);
628+
return fmi3OK;
629+
} catch (const cppfmu::FatalError& e) {
630+
component->logger.Log(fmi3Fatal, "", e.what());
631+
return fmi3Fatal;
632+
} catch (const std::exception& e) {
633+
component->logger.Log(fmi3Error, "", e.what());
634+
return fmi3Error;
635+
}
626636
}
627637

628638
fmi3Status fmi3GetNominalsOfContinuousStates(

pythonfmu3/pythonfmu-export/src/pythonfmu/PySlaveInstance.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,9 +602,34 @@ void PySlaveInstance::GetNumberOfContinuousStates(std::size_t& nStates) const
602602

603603
void PySlaveInstance::GetNumberOfEventIndicators(std::size_t& nIndicators) const
604604
{
605-
nIndicators= 0u;
605+
py_safe_run([this, &nIndicators](PyGILState_STATE gilState) {
606+
auto f = PyObject_CallMethod(pInstance_, "get_number_of_event_indicators", nullptr);
607+
if (f == nullptr) {
608+
handle_py_exception("[getNumberOfEventIndicators] PyObject_CallMethod", gilState);
609+
}
610+
nIndicators = static_cast<std::size_t>(PyLong_AsLong(f));
611+
Py_DECREF(f);
612+
clearLogBuffer();
613+
});
614+
}
615+
616+
void PySlaveInstance::GetEventIndicators(cppfmu::FMIFloat64* eventIndicators, std::size_t nIndicators) const
617+
{
618+
py_safe_run([this, &eventIndicators, nIndicators](PyGILState_STATE gilState) {
619+
auto f = PyObject_CallMethod(pInstance_, "get_event_indicators", nullptr);
620+
if (f == nullptr) {
621+
handle_py_exception("[getEventIndicators] PyObject_CallMethod", gilState);
622+
}
623+
// Assuming f is a list of floats
624+
for (std::size_t i = 0; i < nIndicators; i++) {
625+
PyObject* item = PyList_GetItem(f, i);
626+
eventIndicators[i] = static_cast<cppfmu::FMIFloat64>(PyFloat_AsDouble(item));
627+
}
628+
clearLogBuffer();
629+
});
606630
}
607631

632+
608633
void PySlaveInstance::SetTime(cppfmu::FMIFloat64 time)
609634
{
610635
py_safe_run([this, time](PyGILState_STATE gilState) {

0 commit comments

Comments
 (0)