Skip to content

Commit f8eb172

Browse files
committed
[ModelicaSystem] update handling of outputs and continuous data
* store data as numpy.float64 - allows to define None values * split get*() function into Initial values and Final values
1 parent 93b7bac commit f8eb172

1 file changed

Lines changed: 193 additions & 79 deletions

File tree

OMPython/ModelicaSystem.py

Lines changed: 193 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -325,11 +325,8 @@ def __init__(
325325
self._quantities: list[dict[str, Any]] = []
326326
self._params: dict[str, str] = {} # even numerical values are stored as str
327327
self._inputs: dict[str, list[tuple[float, float]]] = {}
328-
# _outputs values are str before simulate(), but they can be
329-
# np.float64 after simulate().
330-
self._outputs: dict[str, Any] = {}
331-
# same for _continuous
332-
self._continuous: dict[str, Any] = {}
328+
self._outputs: dict[str, np.float64] = {} # numpy.float64 as it allows to define None values
329+
self._continuous: dict[str, np.float64] = {} # numpy.float64 as it allows to define None values
333330
self._simulate_options: dict[str, str] = {}
334331
self._override_variables: dict[str, str] = {}
335332
self._simulate_options_override: dict[str, str] = {}
@@ -630,11 +627,11 @@ def _xmlparse(self, xml_file: OMCPath):
630627
else:
631628
self._params[scalar["name"]] = scalar["start"]
632629
if scalar["variability"] == "continuous":
633-
self._continuous[scalar["name"]] = scalar["start"]
630+
self._continuous[scalar["name"]] = np.float64(scalar["start"])
634631
if scalar["causality"] == "input":
635632
self._inputs[scalar["name"]] = scalar["start"]
636633
if scalar["causality"] == "output":
637-
self._outputs[scalar["name"]] = scalar["start"]
634+
self._outputs[scalar["name"]] = np.float64(scalar["start"])
638635

639636
self._quantities.append(scalar)
640637

@@ -695,15 +692,104 @@ def getQuantities(self, names: Optional[str | list[str]] = None) -> list[dict]:
695692

696693
raise ModelicaSystemError("Unhandled input for getQuantities()")
697694

695+
def getContinuousInitial(
696+
self,
697+
names: Optional[str | list[str]] = None,
698+
) -> dict[str, np.float64] | list[np.float64]:
699+
"""
700+
Get (initial) values of continuous signals.
701+
702+
Args:
703+
names: Either None (default), a string with the continuous signal
704+
name, or a list of signal name strings.
705+
Returns:
706+
If `names` is None, a dict in the format
707+
{signal_name: signal_value} is returned.
708+
If `names` is a string, a single element list [signal_value] is
709+
returned.
710+
If `names` is a list, a list with one value for each signal name
711+
in names is returned: [signal1_value, signal2_value, ...].
712+
713+
Examples:
714+
>>> mod.getContinuousInitial()
715+
{'x': '1.0', 'der(x)': None, 'y': '-0.4'}
716+
>>> mod.getContinuousInitial("y")
717+
['-0.4']
718+
>>> mod.getContinuousInitial(["y","x"])
719+
['-0.4', '1.0']
720+
"""
721+
if names is None:
722+
return self._continuous
723+
if isinstance(names, str):
724+
return [self._continuous[names]]
725+
if isinstance(names, list):
726+
return [self._continuous[x] for x in names]
727+
728+
raise ModelicaSystemError("Unhandled input for getContinousInitial()")
729+
730+
def getContinuousFinal(
731+
self,
732+
names: Optional[str | list[str]] = None,
733+
) -> dict[str, np.float64] | list[np.float64]:
734+
"""
735+
Get (final) values of continuous signals (at stopTime).
736+
737+
Args:
738+
names: Either None (default), a string with the continuous signal
739+
name, or a list of signal name strings.
740+
Returns:
741+
If `names` is None, a dict in the format
742+
{signal_name: signal_value} is returned.
743+
If `names` is a string, a single element list [signal_value] is
744+
returned.
745+
If `names` is a list, a list with one value for each signal name
746+
in names is returned: [signal1_value, signal2_value, ...].
747+
748+
Examples:
749+
>>> mod.getContinuousFinal()
750+
{'x': np.float64(0.68), 'der(x)': np.float64(-0.24), 'y': np.float64(-0.24)}
751+
>>> mod.getContinuousFinal("x")
752+
[np.float64(0.68)]
753+
>>> mod.getContinuousFinal(["y","x"])
754+
[np.float64(-0.24), np.float64(0.68)]
755+
"""
756+
if not self._simulated:
757+
raise ModelicaSystemError("Please use getContinuousInitial() before the simulation was started!")
758+
759+
def get_continuous_solution(name_list: list[str]) -> None:
760+
for name in name_list:
761+
if name in self._continuous:
762+
value = self.getSolutions(name)
763+
self._continuous[name] = np.float64(value[0][-1])
764+
else:
765+
raise ModelicaSystemError(f"{names} is not continuous")
766+
767+
if names is None:
768+
get_continuous_solution(name_list=list(self._continuous.keys()))
769+
return self._continuous
770+
771+
if isinstance(names, str):
772+
get_continuous_solution(name_list=[names])
773+
return [self._continuous[names]]
774+
775+
if isinstance(names, list):
776+
get_continuous_solution(name_list=names)
777+
values = []
778+
for name in names:
779+
values.append(self._continuous[name])
780+
return values
781+
782+
raise ModelicaSystemError("Unhandled input for getContinousFinal()")
783+
698784
def getContinuous(
699785
self,
700786
names: Optional[str | list[str]] = None,
701-
) -> dict[str, str | numbers.Real] | list[str | numbers.Real]:
787+
) -> dict[str, np.float64] | list[np.float64]:
702788
"""Get values of continuous signals.
703789
704-
If called before simulate(), the initial values are returned as
705-
strings (or None). If called after simulate(), the final values (at
706-
stopTime) are returned as numpy.float64.
790+
If called before simulate(), the initial values are returned.
791+
If called after simulate(), the final values (at stopTime) are returned.
792+
The return format is always numpy.float64.
707793
708794
Args:
709795
names: Either None (default), a string with the continuous signal
@@ -734,41 +820,9 @@ def getContinuous(
734820
[np.float64(-0.24), np.float64(0.68)]
735821
"""
736822
if not self._simulated:
737-
if names is None:
738-
return self._continuous
739-
if isinstance(names, str):
740-
return [self._continuous[names]]
741-
if isinstance(names, list):
742-
return [self._continuous[x] for x in names]
743-
744-
if names is None:
745-
for name in self._continuous:
746-
try:
747-
value = self.getSolutions(name)
748-
self._continuous[name] = value[0][-1]
749-
except (OMCSessionException, ModelicaSystemError) as ex:
750-
raise ModelicaSystemError(f"{name} could not be computed") from ex
751-
return self._continuous
823+
return self.getContinuousInitial(names=names)
752824

753-
if isinstance(names, str):
754-
if names in self._continuous:
755-
value = self.getSolutions(names)
756-
self._continuous[names] = value[0][-1]
757-
return [self._continuous[names]]
758-
raise ModelicaSystemError(f"{names} is not continuous")
759-
760-
if isinstance(names, list):
761-
valuelist = []
762-
for name in names:
763-
if name in self._continuous:
764-
value = self.getSolutions(name)
765-
self._continuous[name] = value[0][-1]
766-
valuelist.append(value[0][-1])
767-
else:
768-
raise ModelicaSystemError(f"{name} is not continuous")
769-
return valuelist
770-
771-
raise ModelicaSystemError("Unhandled input for getContinous()")
825+
return self.getContinuousFinal(names=names)
772826

773827
def getParameters(
774828
self,
@@ -841,15 +895,103 @@ def getInputs(
841895

842896
raise ModelicaSystemError("Unhandled input for getInputs()")
843897

898+
def getOutputsInitial(
899+
self,
900+
names: Optional[str | list[str]] = None,
901+
) -> dict[str, np.float64] | list[np.float64]:
902+
"""
903+
Get (initial) values of output signals.
904+
905+
Args:
906+
names: Either None (default), a string with the output name,
907+
or a list of output name strings.
908+
Returns:
909+
If `names` is None, a dict in the format
910+
{output_name: output_value} is returned.
911+
If `names` is a string, a single element list [output_value] is
912+
returned.
913+
If `names` is a list, a list with one value for each output name
914+
in names is returned: [output1_value, output2_value, ...].
915+
916+
Examples:
917+
>>> mod.getOutputsInitial()
918+
{'out1': '-0.4', 'out2': '1.2'}
919+
>>> mod.getOutputsInitial("out1")
920+
['-0.4']
921+
>>> mod.getOutputsInitial(["out1","out2"])
922+
['-0.4', '1.2']
923+
"""
924+
if names is None:
925+
return self._outputs
926+
if isinstance(names, str):
927+
return [self._outputs[names]]
928+
if isinstance(names, list):
929+
return [self._outputs[x] for x in names]
930+
931+
raise ModelicaSystemError("Unhandled input for getOutputsInitial()")
932+
933+
def getOutputsFinal(
934+
self,
935+
names: Optional[str | list[str]] = None,
936+
) -> dict[str, np.float64] | list[np.float64]:
937+
"""Get (final) values of output signals (at stopTime).
938+
939+
Args:
940+
names: Either None (default), a string with the output name,
941+
or a list of output name strings.
942+
Returns:
943+
If `names` is None, a dict in the format
944+
{output_name: output_value} is returned.
945+
If `names` is a string, a single element list [output_value] is
946+
returned.
947+
If `names` is a list, a list with one value for each output name
948+
in names is returned: [output1_value, output2_value, ...].
949+
950+
Examples:
951+
>>> mod.getOutputsFinal()
952+
{'out1': np.float64(-0.1234), 'out2': np.float64(2.1)}
953+
>>> mod.getOutputsFinal("out1")
954+
[np.float64(-0.1234)]
955+
>>> mod.getOutputsFinal(["out1","out2"])
956+
[np.float64(-0.1234), np.float64(2.1)]
957+
"""
958+
if not self._simulated:
959+
raise ModelicaSystemError("Please use getOuputsInitial() before the simulation was started!")
960+
961+
def get_outputs_solution(name_list: list[str]) -> None:
962+
for name in name_list:
963+
if name in self._outputs:
964+
value = self.getSolutions(name)
965+
self._outputs[name] = np.float64(value[0][-1])
966+
else:
967+
raise ModelicaSystemError(f"{names} is not a valid output")
968+
969+
if names is None:
970+
get_outputs_solution(name_list=list(self._outputs.keys()))
971+
return self._outputs
972+
973+
if isinstance(names, str):
974+
get_outputs_solution(name_list=[names])
975+
return [self._outputs[names]]
976+
977+
if isinstance(names, list):
978+
get_outputs_solution(name_list=names)
979+
values = []
980+
for name in names:
981+
values.append(self._outputs[name])
982+
return values
983+
984+
raise ModelicaSystemError("Unhandled input for getOutputs()")
985+
844986
def getOutputs(
845987
self,
846988
names: Optional[str | list[str]] = None,
847-
) -> dict[str, str | numbers.Real] | list[str | numbers.Real]:
989+
) -> dict[str, np.float64] | list[np.float64]:
848990
"""Get values of output signals.
849991
850-
If called before simulate(), the initial values are returned as
851-
strings. If called after simulate(), the final values (at stopTime)
852-
are returned as numpy.float64.
992+
If called before simulate(), the initial values are returned.
993+
If called after simulate(), the final values (at stopTime) are returned.
994+
The return format is always numpy.float64.
853995
854996
Args:
855997
names: Either None (default), a string with the output name,
@@ -880,37 +1022,9 @@ def getOutputs(
8801022
[np.float64(-0.1234), np.float64(2.1)]
8811023
"""
8821024
if not self._simulated:
883-
if names is None:
884-
return self._outputs
885-
if isinstance(names, str):
886-
return [self._outputs[names]]
887-
return [self._outputs[x] for x in names]
888-
889-
if names is None:
890-
for name in self._outputs:
891-
value = self.getSolutions(name)
892-
self._outputs[name] = value[0][-1]
893-
return self._outputs
1025+
return self.getOutputsInitial(names=names)
8941026

895-
if isinstance(names, str):
896-
if names in self._outputs:
897-
value = self.getSolutions(names)
898-
self._outputs[names] = value[0][-1]
899-
return [self._outputs[names]]
900-
raise KeyError(names)
901-
902-
if isinstance(names, list):
903-
valuelist = []
904-
for name in names:
905-
if name in self._outputs:
906-
value = self.getSolutions(name)
907-
self._outputs[name] = value[0][-1]
908-
valuelist.append(value[0][-1])
909-
else:
910-
raise KeyError(name)
911-
return valuelist
912-
913-
raise ModelicaSystemError("Unhandled input for getOutputs()")
1027+
return self.getOutputsFinal(names=names)
9141028

9151029
def getSimulationOptions(
9161030
self,

0 commit comments

Comments
 (0)