1111
1212from .logmsg import LogMsg
1313from .default_experiment import DefaultExperiment
14+ from .cosimulation import CoSimulation
15+ from .modelexchange import ModelExchange
1416from ._version import __version__ as VERSION
1517from .enums import Fmi3Type , Fmi3Status , Fmi3Causality , Fmi3Initial , Fmi3Variability
1618from .variables import Boolean , Enumeration , Int32 , Int64 , UInt64 , Float64 , ModelVariable , String
@@ -33,7 +35,8 @@ class Fmi3StepResult(NamedTuple):
3335 terminateSimulation : bool = False
3436 earlyReturn : bool = False
3537
36- class Fmi3Slave (ABC ):
38+
39+ class Fmi3SlaveBase (object ):
3740 """Abstract facade class to execute Python through FMI standard."""
3841
3942 # Dictionary of (category, description) entries
@@ -104,8 +107,18 @@ def to_xml(self, model_options: Dict[str, str] = dict()) -> Element:
104107 options [option .name ] = str (value ).lower ()
105108 options ["modelIdentifier" ] = self .modelName
106109 options ["canNotUseMemoryManagementFunctions" ] = "true"
107-
108- SubElement (root , "CoSimulation" , attrib = options )
110+
111+ options_me = dict ()
112+ options_me ["canGetAndSetFMUState" ] = "true"
113+ options_me ["modelIdentifier" ] = self .modelName
114+ options_me ["needsCompletedIntegratorStep" ] = "false"
115+
116+ # check if we have cosim mixin or model exchange mixin
117+ if isinstance (self , ModelExchange ):
118+ SubElement (root , "ModelExchange" , attrib = options_me )
119+
120+ if isinstance (self , CoSimulation ):
121+ SubElement (root , "CoSimulation" , attrib = options )
109122
110123 if self .units :
111124 unit_defs = SubElement (root , "UnitDefinitions" )
@@ -239,7 +252,6 @@ def enter_initialization_mode(self):
239252 def exit_initialization_mode (self ):
240253 pass
241254
242- @abstractmethod
243255 def do_step (self , current_time : float , step_size : float ) -> Fmi3StepResult :
244256 pass
245257
@@ -404,6 +416,50 @@ def _set_fmu_state(self, state: Dict[str, Any]):
404416 if v .setter is not None :
405417 v .setter (value )
406418
419+ def set_continuous_states (self , values : List [float ]):
420+ offset = 0
421+ continuous_state_derivatives = list (
422+ filter (lambda v : v .variability == Fmi3Variability .continuous and (isinstance (v , Float64 ) and v .derivative is not None ), self .vars .values ())
423+ )
424+
425+ vrs = [v .derivative for v in continuous_state_derivatives ]
426+
427+ for vr in vrs :
428+ var = self .vars [vr ]
429+ size = var .size (self .vars )
430+ if size > 1 :
431+ var .setter (values [offset :offset + size ])
432+ else :
433+ var .setter (values [offset ])
434+ offset += size
435+
436+ def get_continuous_states (self ) -> List [float ]:
437+ offset = 0
438+ continuous_state_derivatives = list (
439+ filter (lambda v : v .variability == Fmi3Variability .continuous and (isinstance (v , Float64 ) and v .derivative is not None ), self .vars .values ())
440+ )
441+
442+ vrs = [v .derivative for v in continuous_state_derivatives ]
443+
444+ refs = list ()
445+ for vr in vrs :
446+ var = self .vars [vr ]
447+ if len (var .dimensions ) == 0 :
448+ refs .append (float (var .getter ()))
449+ else :
450+ refs .extend (var .getter ())
451+
452+ return refs
453+
454+ def get_number_of_continuous_states (self ) -> int :
455+ continuous_state_derivatives = list (
456+ filter (lambda v : v .variability == Fmi3Variability .continuous and (isinstance (v , Float64 ) and v .derivative is not None ), self .vars .values ())
457+ )
458+ return len (continuous_state_derivatives )
459+
460+ def set_time (self , time : float ):
461+ self .time = time
462+
407463 @staticmethod
408464 def _fmu_state_to_bytes (state : Dict [str , Any ]) -> bytes :
409465 return json .dumps (state ).encode ("utf-8" )
@@ -436,3 +492,6 @@ def log(
436492 category = "logAll"
437493 log_msg = LogMsg (status , category , msg , debug )
438494 self .log_queue .append (log_msg )
495+
496+ class Fmi3Slave (Fmi3SlaveBase , CoSimulation ):
497+ pass
0 commit comments