From bffb6263e31824b283b48bf0e746e377cf276611 Mon Sep 17 00:00:00 2001 From: Eisinger Date: Wed, 30 Apr 2025 13:38:22 +0200 Subject: [PATCH 01/29] One of the tests had a return value, which pytest will not allow in the future and which is now removed. Added plotly as test dependency to pyproject.toml --- pyproject.toml | 2 ++ tests/test_make_time_table.py | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d2bc819..9ab0a63 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,12 +65,14 @@ dependencies = [ "pdfminer>=20191125", "pypdf2>=3.0.1", "scipy>=1.15.1", + "plotly>=6.0.1", ] [project.optional-dependencies] modelTest = [ "fmpy==0.3.21", # version 0.3.22 does so far (25.3.25) not workwhen installing through pip "matplotlib>=3.9.1", + "plotly>=6.0.1", ] rest = [ "docutils>=0.21", diff --git a/tests/test_make_time_table.py b/tests/test_make_time_table.py index ce9aab4..ec1100e 100644 --- a/tests/test_make_time_table.py +++ b/tests/test_make_time_table.py @@ -241,13 +241,11 @@ def test_make_with_new_data(): "default_experiment": {"startTime": 0, "stopTime": 2 * np.pi, "stepSize": 0.1, "tolerance": 1e-5}, }, ) - (build_path / "TimeTableFMU.fmu").replace(build_path / "NewDataFMU.fmu") - return fmu_path @pytest.mark.skip(reason="Does so far not work within pytest, only stand-alone") def test_use_with_new_data(show): - fmu_path = Path(__file__).parent / "test_working_directory" / "NewDataFMU.fmu" + fmu_path = Path(__file__).parent / "test_working_directory" / "TimeTableFMU.fmu" result = simulate_fmu( # type: ignore[reportArgumentType] fmu_path, stop_time=2 * np.pi, From 78f564624a87843c83622a1f6193c3f01fd94290 Mon Sep 17 00:00:00 2001 From: Eisinger Date: Thu, 15 May 2025 13:18:52 +0200 Subject: [PATCH 02/29] Added a separate object 'Unit' to variable, so that units are handled in a cleaner way. There is an issue with libcosimpy so that not all tests are running --- docs/source/component-development-process.rst | 82 ++++ examples/new_pythonfmu_features.py | 62 --- examples/new_pythonfmu_features2.py | 61 --- examples/new_pythonfmu_features3.py | 65 --- examples/new_pythonfmu_features4.py | 2 - pyproject.toml | 12 +- src/component_model/__init__.py | 2 - src/component_model/model.py | 129 ++--- src/component_model/variable.py | 464 +++++++++++------- src/component_model/variable_naming.py | 28 +- tests/test_make_time_table.py | 2 +- tests/test_model.py | 4 +- tests/test_unit.py | 105 ++++ tests/test_variable.py | 35 +- tests/test_variable_naming.py | 27 +- 15 files changed, 608 insertions(+), 472 deletions(-) create mode 100644 docs/source/component-development-process.rst delete mode 100644 examples/new_pythonfmu_features.py delete mode 100644 examples/new_pythonfmu_features2.py delete mode 100644 examples/new_pythonfmu_features3.py delete mode 100644 examples/new_pythonfmu_features4.py create mode 100644 tests/test_unit.py diff --git a/docs/source/component-development-process.rst b/docs/source/component-development-process.rst new file mode 100644 index 0000000..3826f74 --- /dev/null +++ b/docs/source/component-development-process.rst @@ -0,0 +1,82 @@ +******************************************************* +Guideline on how to develop a FMU using component-model +******************************************************* + +The development process follows the steps + +#. Develop a functional model using Python (>=3.10.) as a Python class. Called *basic model* here. +#. Thoroughly test the basic model. +#. Define the FMU interface using component-model functions +#. Build the FMU calling `Model.build()`, optionally overwriting optional argument of the model class. +#. Test the FMU standalone, e.g. using the `FMPy` package or in conjunction with other FMUs using the `sim-explorer` package. + +Develop a basic model +===================== +Define a functional model as a Python class. We refer to this model as the *basic model*. +At this stage the emerging model class does not need to refer to the `component-model` package. +In fact it is **not recommended** to derive the basic model from `Model`class of component-model. +The basic model might import any Python package (e.g. numpy), as needed to satisfy the functionality. + +Testing the basic model +======================= +The basic model should be thoroughly tested. +This cannot be emphasised too much, as test possibilities and feadback is limited in the FMU domain, +while Python offers proper test and debugging facilities. + + +Defining the FMU interface +========================== +A FMU interface must be added to the model prior to package the model as FMU. This concerns basically + +* component model parameters, i.e. settings which can be changed prior to a simulation run, but are typically constant during the run +* component model input variables, i.e. variables which can be changed based on the output from other component models +* component model output variables, i.e. results which are provided to any connected component model, or the system. + +Defining the interface is done like + +.. code-block:: Python + + class _FMU(Model, ): + def __init__(self, .__init__() + + +Virtual derivatives +------------------- +Running component models in scenarios it is often necessary to change variables during the simulation run. +As in the reality it is often not a good idea to step values in huge steps, as this resembles a 'hammer stroke', +which the system might not tolerate. The simulation dynamics does often not handle such a situation properly either. +It is therefore often necessary to ramp up or down values to the desired final values, +i.e. changing the derivative of the input variable to a non-zero value +until the desired value of the parent variable is reached and then setting the derivative back to zero. + +It is cumbersome to introduce derivative variables for every parent variable which might be changed during the simulation. +Therefore. component-model introduces the concept of *virtual derivatives*, +which are derivative interface variables which are not linked to variables in the basic model. +When defining such variables and setting them to non-zero values, +the parent variable is changed with the given slope at every time step, +i.e. + +` += d/dt * ` + +where `d/dt` is the non-zero derivative value (slope). + +In practical terms, virtual derivatives are defined using FMI structured variables syntax: + +.. code-block:: Python + + Variable( name='der()', causality='input', variability='continuous', ...) + +Explicit specification of the arguments `owner` and `local_name` should be avoided. +Specification of `local_name` changes the virtual derivative into a non-virtual derivative, +i.e. the variable is expected to exist in the basic model. +The `on_step` argument is automatically set to change the parent variable at every time step if the derivative is non-zero. +Explicitly overwriting the automatic `on_step` function is allowed at one's own expense. + + +Building the FMU +================ + +Testing the FMU +=============== \ No newline at end of file diff --git a/examples/new_pythonfmu_features.py b/examples/new_pythonfmu_features.py deleted file mode 100644 index b554982..0000000 --- a/examples/new_pythonfmu_features.py +++ /dev/null @@ -1,62 +0,0 @@ -from pythonfmu.enums import Fmi2Status - -from component_model import Model, Variable - - -class NewFeatures(Model): - """Dummy model to test new features of component-model and pythonfmu. - - * logger messages to the user - + handle __init__ parameters - + translate assert statements to logger messages - + translate Exceptions to logger messages - + allow class as argument to .build, instead of the source code file - """ - - def __init__(self, i: int = 1, f: float = 9.9, s: str = "Hello", **kwargs): - super().__init__( - "NewFeatures", - "Dummy model for testing new features in PythonFMU", - "Siegfried Eisinger", - default_experiment={"startTime": 0, "stopTime": 9, "stepSize": 1}, - **kwargs, - ) - print("NAME", self, self.name) - self._i = Variable( - self, - "i", - "My integer", - typ=int, - causality="output", - variability="discrete", - initial="exact", - start=i, - rng=(0, 10), - ) - - self._f = Variable(self, "f", "My float", causality="input", variability="continuous", start=f) - - self._s = Variable(self, "s", "My string", typ=str, causality="parameter", variability="fixed", start=s) - - self.log("This is a __init__ debug message", debug=True) - # self.log("This is a FATAL __init__ message", status=Fmi2Status.fatal, category="logStatusFatal", debug=False) - - def do_step(self, time: int | float, dt: int | float): - super().do_step(time, dt) - self.i += 1 - self.f = time - assert self.f == time # assert without message, but comment - # assert self.i < 8, "The range check would detect that with the next get message" - # send log messages of all types. OSP makes them visible according to log_output_level setting - self.log(f"do_step@{time}. logAll", status=Fmi2Status.ok, category="logAll", debug=True) - self.log(f"do_step@{time}. logStatusWarning", Fmi2Status.warning, "logStatusWarning", True) - self.log(f"do_step@{time}. logStatusDiscard", Fmi2Status.discard, "logStatusDiscard", True) - self.log(f"do_step@{time}. logStatusError", Fmi2Status.error, "logStatusError", True) - # self.log(f"do_step@{time}. logStatusFatal", Fmi2Status.fatal, "logStatusFatal", True) - if time > 8: - self.log(f"@{time}. Trying to terminate simulation", Fmi2Status.error, "logStatusError", True) - return False - return True - - def exit_initialization_mode(self): - print(f"My initial variables: i:{self.i}, f:{self.f}, s:{self.s}") diff --git a/examples/new_pythonfmu_features2.py b/examples/new_pythonfmu_features2.py deleted file mode 100644 index b49ad30..0000000 --- a/examples/new_pythonfmu_features2.py +++ /dev/null @@ -1,61 +0,0 @@ -from pythonfmu.enums import Fmi2Status - -from component_model import Model, Variable - - -class NewFeatures(Model): - """Dummy model to test new features of component-model and pythonfmu. - - * logger messages to the user - + handle __init__ parameters - + translate assert statements to logger messages - + translate Exceptions to logger messages - + allow class as argument to .build, instead of the source code file - """ - - def __init__(self, i: int = 1, f: float = 9.9, s: str = "Hello", **kwargs): - super().__init__( - "NewFeatures", - "Dummy model for testing new features in PythonFMU", - "Siegfried Eisinger", - default_experiment={"startTime": 0, "stopTime": 9, "stepSize": 1}, - **kwargs, - ) - self._i = Variable( - self, - "i", - "My integer", - typ=int, - causality="output", - variability="discrete", - initial="exact", - start=i, - rng=(0, 10), - ) - - self._f = Variable(self, "f", "My float", causality="input", variability="continuous", start=f) - - self._s = Variable(self, "s", "My string", typ=str, causality="parameter", variability="fixed", start=s) - - self.log("This is a __init__ debug message", debug=True) - # self.log("This is a FATAL __init__ message", status=Fmi2Status.fatal, category="logStatusFatal", debug=False) - - def do_step(self, time, dt): - super().do_step(time, dt) - self.i += 1 - self.f = time - assert self.f == time # assert without message, but comment - # assert self.i < 8, "The range check would detect that with the next get message" - # send log messages of all types. OSP makes them visible according to log_output_level setting - self.log(f"do_step@{time}. logAll", status=Fmi2Status.ok, category="logAll", debug=True) - self.log(f"do_step@{time}. logStatusWarning", Fmi2Status.warning, "logStatusWarning", True) - self.log(f"do_step@{time}. logStatusDiscard", Fmi2Status.discard, "logStatusDiscard", True) - self.log(f"do_step@{time}. logStatusError", Fmi2Status.error, "logStatusError", True) - self.log(f"do_step@{time}. logStatusFatal", Fmi2Status.fatal, "logStatusFatal", True) - if time > 8: - self.log(f"@{time}. Trying to terminate simulation", Fmi2Status.error, "logStatusError", True) - return False - return True - - def exit_initialization_mode(self): - print(f"My initial variables: i:{self.i}, f:{self.f}, s:{self.s}") diff --git a/examples/new_pythonfmu_features3.py b/examples/new_pythonfmu_features3.py deleted file mode 100644 index 6b7e254..0000000 --- a/examples/new_pythonfmu_features3.py +++ /dev/null @@ -1,65 +0,0 @@ -from pythonfmu.enums import Fmi2Status - -from component_model import Model, Variable - - -class NewFeatures(Model): - """Dummy model to test new features of component-model and pythonfmu. - - * logger messages to the user - + handle __init__ parameters - + translate assert statements to logger messages - + translate Exceptions to logger messages - + allow class as argument to .build, instead of the source code file - """ - - def __init__(self, i: int = 1, f: float = 9.9, s: str = "Hello", **kwargs): - super().__init__( - "NewFeatures", - "Dummy model for testing new features in PythonFMU", - "Siegfried Eisinger", - default_experiment={"startTime": 0, "stopTime": 9, "stepSize": 1}, - ) - self._i = Variable( - self, - "i", - "My integer", - typ=int, - causality="output", - variability="discrete", - initial="exact", - start=i, - rng=(0, 10), - ) - - self._f = Variable(self, "f", "My float", causality="input", variability="continuous", start=f) - - self._s = Variable(self, "s", "My string", typ=str, causality="parameter", variability="fixed", start=s) - - self.log("This is a __init__ debug message", debug=True) - # self.log("This is a FATAL __init__ message", status=Fmi2Status.fatal, category="logStatusFatal", debug=False) - - def do_step(self, time, dt): - super().do_step(time, dt) - self.i += 1 - self.f = time - assert self.f == time # assert without message, but comment - # assert self.i < 8, "The range check would detect that with the next get message" - # send log messages of all types. OSP makes them visible according to log_output_level setting - self.log(f"do_step@{time}. logAll", status=Fmi2Status.ok, category="logAll", debug=True) - self.log(f"do_step@{time}. logStatusWarning", Fmi2Status.warning, "logStatusWarning", True) - self.log(f"do_step@{time}. logStatusDiscard", Fmi2Status.discard, "logStatusDiscard", True) - self.log(f"do_step@{time}. logStatusError", Fmi2Status.error, "logStatusError", True) - self.log(f"do_step@{time}. logStatusFatal", Fmi2Status.fatal, "logStatusFatal", True) - if time > 8: - self.log(f"@{time}. Trying to terminate simulation", Fmi2Status.error, "logStatusError", True) - return False - return True - - def exit_initialization_mode(self): - print(f"My initial variables: i:{self.i}, f:{self.f}, s:{self.s}") - - -class NewFeatures2(Model): - def do_step(self, time, dt): - return True diff --git a/examples/new_pythonfmu_features4.py b/examples/new_pythonfmu_features4.py deleted file mode 100644 index 68a57b1..0000000 --- a/examples/new_pythonfmu_features4.py +++ /dev/null @@ -1,2 +0,0 @@ -class NewFeatures(object): - pass diff --git a/pyproject.toml b/pyproject.toml index 9ab0a63..17091e1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ build-backend = "hatchling.build" only-include = [ "src/component_model", "tests", - "tests/examples", + "examples", ".coveragerc", ".editorconfig", "pytest.ini", @@ -19,7 +19,6 @@ only-include = [ [tool.hatch.build.targets.wheel] packages = [ "src/component_model", - "examples", ] [project] @@ -59,13 +58,13 @@ dependencies = [ "pint>=0.24", "sympy>=1.13.3", "jsonpath-ng>=1.7.0", - "libcosimpy>=0.0.2", "pythonfmu>=0.6.7", "flexparser<0.4", "pdfminer>=20191125", "pypdf2>=3.0.1", "scipy>=1.15.1", "plotly>=6.0.1", + "libcosimpy>=0.0.3.post1", ] [project.optional-dependencies] @@ -115,11 +114,11 @@ plugins = [ mypy_path = "stubs" files = [ "src", - # "tests", - # "demos", + "tests", + "examples", ] exclude = [ - "^src/folder_to_be_excluded/", + "tests/test_working_directory", ] check_untyped_defs = true disable_error_code = [ @@ -136,5 +135,4 @@ include = [ ] exclude = [ "tests/test_working_directory", - "examples/new_pythonfmu_features*", ] diff --git a/src/component_model/__init__.py b/src/component_model/__init__.py index 46ce159..e69de29 100644 --- a/src/component_model/__init__.py +++ b/src/component_model/__init__.py @@ -1,2 +0,0 @@ -from component_model.model import Model # noqa: F401 -from component_model.variable import Variable # noqa: F401 diff --git a/src/component_model/model.py b/src/component_model/model.py index 3a88052..3cae8d8 100644 --- a/src/component_model/model.py +++ b/src/component_model/model.py @@ -20,7 +20,7 @@ from pythonfmu.fmi2slave import FMI2_MODEL_OPTIONS # type: ignore from component_model.enums import ensure_enum -from component_model.variable import Variable +from component_model.variable import Unit, Variable from component_model.variable_naming import ParsedVariable, VariableNamingConvention logger = logging.getLogger(__name__) @@ -87,6 +87,11 @@ class Model(Fmi2Slave): Valid keys: startTime,stopTime,stepSize,tolerance guid (str)=None: Unique identifier of the model (supplied or automatically generated) flags (dict)=None: Any of the defined FMI flags with a non-default value (see FMI 2.0.4, Section 4.3.1) + + .. todo:: + + Include support for model units with respect to time, degrees/radians,... + Make sure that such base units are consistently used in the model. """ instances: list[str] = [] @@ -144,11 +149,11 @@ def __init__( # print("FLAGS", flags) variable_naming = kwargs.pop("variable_naming", "structured") self.variable_naming = ensure_enum(variable_naming, VariableNamingConvention.flat) - self._units: dict[str, list] = {} # def units and display units (unitName:conversionFactor). => UnitDefinitions + self._units: list[Unit] = [] # list of all Unit objects defined in the model => UnitDefinitions self.flags = self.check_flags(flags) self._dirty: list = [] # dirty compound variables. Used by (set) during do_step() self.time = self.default_experiment.start_time # keeping track of time when dynamic calculations are performed - self.derivatives: list = [] # list of non-explicit derivatives + self.derivatives: dict = {} # dict of non-explicit derivatives {dername : basevar, ...} def setup_experiment(self, start_time: float = 0.0): """Minimum version of setup_experiment, just setting the start_time. Derived models may need to extend this.""" @@ -169,7 +174,6 @@ def do_step(self, current_time: float, step_size: float) -> bool: """ self.time = current_time self.dirty_do() # run on_set on all dirty variables - for var in self.vars.values(): if var is not None and var.on_step is not None: var.on_step(current_time, step_size) @@ -180,21 +184,12 @@ def _unit_ensure_registered(self, candidate: Variable): To register the units of a compound variable, the whole variable is entered and a recursive call to the underlying display units is made. """ - unit_display = [] for i in range(len(candidate)): - if candidate.display[i] is None: - unit_display.append((candidate.unit[i], None)) - else: - unit_display.append((candidate.unit[i], candidate.display[i])) - # here the actual work is done - for u, du in unit_display: - if u not in self._units: # the main unit is not yet registered - self._units[u] = [] # main unit has no factor - if du is not None: # displays are defined - if not len(self._units[u]) or all( - du[0] not in self._units[u][i][0] for i in range(len(self._units[u])) - ): - self._units[u].append(du) + cu = candidate.unit[i] + for u in self._units: + if cu.u == u.u and cu.du == u.du: # already registered + return + self._units.append(cu) def owner_hierarchy(self, parent: str | None) -> list: """Analyse the parent of a variable down to the Model and return the owners as list.""" @@ -292,15 +287,20 @@ def add_variable(self, *args, **kwargs): """ return Variable(self, *args, **kwargs) - def add_derivative(self, var: Variable, order: int): - """Add the derivative of var to the exposed Variables as virtual variable. + def add_derivative(self, dername: str, basename: str) -> Variable: + """Add the derivative of basename to the dict of virtual derivatives. This is convenient as many physical systems do not tolerate to abruptly change variable values, but require to ramp up/down values by setting the derivative to suitable values. This can be achieved without adding an internal variable to the model. The model will in this case do the ramping when the derivative is set != 0.0. + + Args: + basename (str): the full name of the base variable name, i.e. d basename / dt = dername(t) """ - self.derivatives.append(var) + basevar = self.variable_by_name(basename) + self.derivatives.update({dername: basevar}) + return basevar def variable_by_name(self, name: str) -> Variable: """Return Variable object related to name, or None, if not found. @@ -523,46 +523,55 @@ def to_xml(self, model_options: dict | None = None) -> ET.Element: def xml_unit_definitions(self): """Make the xml element for the unit definitions used in the model. See FMI 2.0.4 specification 2.2.2.""" defs = ET.Element("UnitDefinitions") - for u in self._units: - ubase = self.ureg(u).to_base_units() - dim = ubase.dimensionality - exponents = {} - for key, value in { - "mass": "kg", - "length": "m", - "time": "s", - "current": "A", - "temperature": "K", - "substance": "mol", - "luminosity": "cd", - }.items(): - if "[" + key + "]" in dim: - exponents.update({value: str(int(dim["[" + key + "]"]))}) - if ( - "radian" in str(ubase.units) - ): # radians are formally a dimensionless quantity. To include 'rad' as specified in FMI standard this dirty trick is used - # udeg = str(ubase.units).replace("radian", "degree") - # print("EXPONENT", ubase.units, udeg, log(ubase.magnitude), log(self.ureg('degree').to_base_units().magnitude)) - exponents.update( - {"rad": str(int(log(ubase.magnitude) / log(self.ureg("degree").to_base_units().magnitude)))} - ) - - unit = ET.Element("Unit", {"name": u}) - base = ET.Element("BaseUnit", exponents) - base.attrib.update({"factor": str(self.ureg(u).to_base_units().magnitude)}) - unit.append(base) - for du in self._units[u]: # list also the displays (if defined) - unit.append( - ET.Element( - "DisplayUnit", - { - "name": du[0], - "factor": str(du[1](1.0)), - "offset": str(du[1](0.0)), - }, + u_done: list[str] = [] + for u in self._units: # all registered unit objects + unit = ET.Element("NoUnit") # dummy element + if u.u not in u_done: # multiple entries are possible if there are multiple display units + ubase = self.ureg(u.u).to_base_units() + dim = ubase.dimensionality + exponents = {} + for key, value in { + "mass": "kg", + "length": "m", + "time": "s", + "current": "A", + "temperature": "K", + "substance": "mol", + "luminosity": "cd", + }.items(): + if "[" + key + "]" in dim: + exponents.update({value: str(int(dim["[" + key + "]"]))}) + if ( + "radian" in str(ubase.units) + ): # radians are formally a dimensionless quantity. To include 'rad' as specified in FMI standard this dirty trick is used + # udeg = str(ubase.units).replace("radian", "degree") + # print("EXPONENT", ubase.units, udeg, log(ubase.magnitude), log(self.ureg('degree').to_base_units().magnitude)) + exponents.update( + {"rad": str(int(log(ubase.magnitude) / log(self.ureg("degree").to_base_units().magnitude)))} ) - ) - defs.append(unit) + + unit = ET.Element("Unit", {"name": u.u}) + base = ET.Element("BaseUnit", exponents) + base.attrib.update({"factor": str(self.ureg(u.u).to_base_units().magnitude)}) + unit.append(base) + du_done: list[str] = [] + for _u in self._units: # list also the displays (if defined) + if _u.u not in u_done and u.u == _u.u and _u.du is not None and _u.du not in du_done: + unit.append( + ET.Element( + "DisplayUnit", + { + "name": _u.du, + "factor": str(_u.to_base(1.0) - _u.to_base(0.0)), + "offset": str(_u.to_base(0.0)), + }, + ) + ) + if isinstance( _u.du, str): + du_done.append(_u.du) + u_done.append(u.u) + if unit.tag != "NoUnit": + defs.append(unit) return defs def _xml_default_experiment(self): diff --git a/src/component_model/variable.py b/src/component_model/variable.py index dbe5dd5..0ff9643 100644 --- a/src/component_model/variable.py +++ b/src/component_model/variable.py @@ -8,7 +8,7 @@ from typing import Any, Callable, Sequence, TypeAlias import numpy as np -from pint import Quantity # management of units +from pint import Quantity, UnitRegistry # management of units from pythonfmu.enums import Fmi2Causality as Causality # type: ignore from pythonfmu.enums import Fmi2Initial as Initial # type: ignore from pythonfmu.enums import Fmi2Variability as Variability # type: ignore @@ -48,8 +48,167 @@ class Check(IntFlag): all = 3 -def linear(x: float, b: float, a: float = 0.0): - return a + b * x +class Unit: + """Helper class to store and manage units and display units, + i.e. base unit of variable and unit differences 'outside' and 'inside' the model. + + One Unit object represents one scalar variable. + """ + + def __init__(self): + self.u = "" # unit as string (placeholder) + self.du = None # display unit (default: same as u, no transformation) + self.to_base = partial(Unit.identity) # ensure a definition + self.from_base = partial(Unit.identity) # ensure a definition + + + def __str__(self): + txt = f"Unit {self.u}, display:{self.du}" + if self.du is not None: + txt += f". Offset:{self.to_base(0)}, factor:{self.to_base(1.0) - self.to_base(0.0)}" + return txt + + def parse_quantity(self, quantity: PyType, ureg: UnitRegistry, typ: type | None = None) -> PyType: + """Disect the provided quantity in terms of magnitude and unit, if provided as string. + If another type is provided, dimensionless units are assumed. + + Args: + quantity (PyType): the quantity to disect. Should be provided as string, but also the trivial cases (int,float,Enum) are allowed. + A free string should not be used and leads to a warning + Returns: + the magnitude in base units, the base unit and the unit as given (display units), + together with the conversion functions between the units. + """ + if typ is str: + self.u = "dimensionless" + self.du = None + val = quantity + elif isinstance(quantity, str): # only string variable make sense to disect + assert ureg is not None, f"UnitRegistry not found, while providing units: {quantity}" + try: + q = ureg(quantity) # parse the quantity-unit and return a Pint Quantity object + if isinstance(q, (int, float)): + self.u = "" + self.du = None + return q # integer or float variable with no units provided + elif isinstance(q, Quantity): # pint.Quantity object + # transform to base units ('SI' units). All internal calculations will be performed with these + val = self.val_unit_display(q, ureg) + else: + raise VariableInitError(f"Unknown quantity {quantity} to disect") from None + # no recognized units. Assume a free string. ??Maybe we should be more selective about the exact error type: + except Exception as warn: + logger.warning(f"Unhandled quantity {quantity}: {warn}. A str? Set explicit 'typ=str'.") + self.u = "" + self.du = None + val = str(quantity) + else: + self.u = "dimensionless" + self.du = None + val = quantity + if typ is not None and type(val) is not typ: # check variable type + try: # try to convert the magnitude to the correct type. + val = typ(val) + except Exception as err: + raise VariableInitError(f"Value {val} is not of the correct type {typ}") from err + return val + + @classmethod + def linear(cls, x: float, b: float, a: float = 0.0): + return a + b * x + + @classmethod + def identity(cls, x: float): + return x + + def val_unit_display(self, q: Quantity, ureg: UnitRegistry) -> float: + """Identify base units and calculate the transformations between display and base units. + + Returns + ------- + The numerical value of q. As side effect + + * the unit `u` is set. Might be `dimensionless` + * the display unit `du` is set to None if same as unit, else + + - it is set to the display unit name and + - the transformations `to_base` and `from_base` are set. + """ + qb = q.to_base_units() + self.u = str(qb.units) + val = qb.magnitude # Note: numeric types are not converted, e.g. int to float + if qb.units == q.units: # no conversion + self.du = None + else: # calculate the conversion functions + # we generate a second value and calculate the straight line conversion function + # did not find a better way in pint + self.du = str(q.units) + q2 = ureg.Quantity(10.0 * (q.magnitude + 10.0), q.units) + qb2 = q2.to_base_units() + a = (qb.magnitude * q2.magnitude - qb2.magnitude * q.magnitude) / (q2.magnitude - q.magnitude) + b = (qb2.magnitude - qb.magnitude) / (q2.magnitude - q.magnitude) + if abs(a) < 1e-9: # multiplicative conversion + if abs(b - 1.0) < 1e-9: # unit and display unit are compatible. No transformation + self.du = None + self.to_base = partial(Unit.linear, b=b) + self.from_base = partial(Unit.linear, b=1.0 / b) + else: # there is a constant (e.g. Celsius to Fahrenheit) + self.to_base = partial(Unit.linear, b=b, a=a) + self.from_base = partial(Unit.linear, b=1.0 / b, a=-a / b) + return val + + @classmethod + def make(cls, quantity: PyType, ureg: UnitRegistry, typ: type | None = None) -> tuple[tuple[PyType], tuple[Unit]]: + u = Unit() + val = u.parse_quantity(quantity, ureg, typ) + return ((val,), (u,)) + + @classmethod + def make_tuple( + cls, quantities: tuple | list | np.ndarray, ureg: UnitRegistry, typ: type | None = None + ) -> tuple[tuple[PyType, ...], tuple[Unit, ...]]: + """Make a tuple of Unit objects from the tuple of quantities.""" + values: list[PyType] = [] + units: list[Unit] = [] + for q in quantities: + val, u = cls.make(q, ureg, typ) + values.extend(val) + units.extend(u) + return (tuple(values), tuple(units)) + + @classmethod + def derivative(cls, baseunits: tuple[Unit, ...], tu: str = "s") -> tuple[tuple[float, ...], tuple[Unit, ...]]: + """Construct units for a derivative variable of basevars. tu is the time unit.""" + units: list[Unit] = [] + for bu in baseunits: + u = Unit() + u.u = f"{bu.u}/{tu}" + u.du = None if bu.du is None else f"{bu.du}/{tu}" + if bu.du is not None: + u.to_base = bu.to_base + u.from_base = bu.from_base + units.append(u) + values = [0.0] * len(baseunits) + return (tuple(values), tuple(units)) + + def compatible( + self, quantity: PyType, ureg: UnitRegistry, typ: type | None = None, strict: bool = True + ) -> tuple[bool, PyType]: + """Check whether the supplied quantity 'q' is compatible with this unit. + If strict==True, the supplied quantity shall be in display units. + """ + _q, _unit = Unit.make(quantity, ureg, typ) + q = _q[0] + unit = _unit[0] + # no explicit unit needed when the quantity is 0 or inf (anything compatible) + if ( + ((q == 0 or q == float("inf") or q == float("-inf")) and unit.u == "dimensionless") # 0, +/-inf without unit + or (strict and self.u == unit.u and self.du == unit.du) + or (not strict and self.u == unit.u) + ): + return (True, q) + else: + return (False, q) # Some special error classes @@ -181,53 +340,64 @@ def __init__( super().__init__(name=name, description=description, getter=self.getter, setter=self.setter) parsed = ParsedVariable(name, self.model.variable_naming) + + self._annotations = annotations + self._check = value_check # unique for all elements in compound variables + self._typ: type | None = typ # preliminary. Will be adapted if not explicitly provided (None) + + self.on_step = on_step # hook to define a function of currentTime and time step dT, + # to be performed during Model.do_step for input variables + self.on_set = on_set + # Note: the _len is a central property, distinguishing scalar and compound variables. + self._unit: tuple[Unit, ...] + self._start: tuple[PyType, ...] = tuple() + if owner is None: oh = self.model.owner_hierarchy(parsed.parent) if owner is None: self.owner = oh[-1] else: self.owner = owner + basevar: Variable | None = None if local_name is None: if parsed.der > 0: # is a derivative of 'var' self.local_name = f"der{parsed.der}_{parsed.var}" - if not hasattr(self.owner, self.local_name): # the derivative is not explicitly defined in the model - if parsed.der == 1: # first derivative - self.local_name = f"der{parsed.der}_{parsed.var}" - setattr(self.owner, self.local_name, 0.0) - if on_step is None: - on_step = self.der1 - else: - raise NotImplementedError("None-explicit higher order derivatives not implemented") from None + if not hasattr(self.owner, self.local_name): # a virtual derivative + if parsed.der != 1: + raise NotImplementedError( + "Virtual higher order derivatives should be implemented iteratively." + ) from None + + basevar = self.model.add_derivative(self.name, parsed.as_string(("parent", "var"))) + assert isinstance(basevar, Variable), "The primitive of a derivative must be a Variable object" + assert basevar.typ is float, ( + f"The primitive the derivative {self.name} shall be float. Found {basevar.typ}" + ) + self._typ = float + if start is None: + self._start, self._unit = Unit.derivative(basevar.unit) + if self.on_step is None: + self.on_step = self.der1 else: self.local_name = parsed.var else: - self.local_name = local_name + self.local_name = local_name # use explicitly provided local name - self._annotations = annotations - self._check = value_check # unique for all elements in compound variables - self._typ: type | None = typ # preliminary. Will be adapted if not explicitly provided (None) - - self.on_step = on_step # hook to define a function of currentTime and time step dT, - # to be performed during Model.do_step for input variables - self.on_set = on_set - # Note: the _len is a central property, distinguishing scalar and compound variables. - - self._start: tuple - # First we check for str (since these are also iterable), then we can check for the presence of __getitem__ - # Determine the (element) type (unique for all elements in compound variables) if self._typ is str: # explicit free string + assert isinstance( start, str) self._len = 1 - self.unit = "dimensionless" - self.display = None + self._start, self._unit = Unit.make(start, self.model.ureg, typ=str) self.range = ("", "") # just a placeholder. Strings are not range checked - self.start = ("",) if start is None else (str(start),) else: # if type is provided and no (initial) value. We set a default value of the correct type as 'example' value - assert start is not None, f"{self.name}: start value is mandatory, at least for type and unit determination" - _start, _unit, _display = self._disect_unit(start) # do that first. units included as str! - self.start = _start - self.unit = _unit - self.display = _display + if not len(self._start): # not yet set + assert start is not None, ( + f"{self.name}: start value is mandatory, at least for type and unit determination" + ) + if isinstance(start, (tuple | list | np.ndarray)): + self._start, self._unit = Unit.make_tuple(start, self.model.ureg, self._typ) + else: + self._start, self._unit = Unit.make(start, self.model.ureg, self._typ) self._len = len(self._start) if self._typ is None: # try to adapt using start self._typ = self.auto_type(self._start) @@ -240,23 +410,27 @@ def __init__( raise VariableInitError(f"The provided value {self._start} is not in the valid range {self._range}") self.model.register_variable(self) try: - setattr(self.owner, self.local_name, np.array(self.start, self.typ) if self._len > 1 else self.start[0]) + setattr(self.owner, self.local_name, np.array(self._start, self.typ) if self._len > 1 else self._start[0]) except AttributeError as _: # can happen if a @property is defined for local_name, but no @local_name.setter pass def der1(self, current_time: float, step_size: float): - der = self.getter() # the current slope value - if any(x != 0.0 for x in der): # the value is (currently) set to > or < 0 - varname = self.local_name[5:] - if len(der) == 1: - assert isinstance(der[0], float) - setattr(self.owner, varname, getattr(self.owner, varname) + der[0] * step_size) + """Ramp the base variable value up or down within step_size.""" + der = getattr(self.owner, self.local_name) # the current slope value + if (isinstance(der, float) and der != 0.0) or ( + isinstance(der, (Sequence, np.ndarray)) and any(x != 0.0 for x in der) + ): # there is a slope + if not isinstance(der, (Sequence, np.ndarray)): + der = [der] + varname = self.local_name[5:] # local name of the base variable + val = getattr(self.owner, varname) # previous value of base variable + is_nparray = isinstance(val, np.ndarray) + basevar = self.model.derivatives[self.name] # base variable object + if is_nparray: + basevar.setter_internal(val + step_size * np.array(der, float), -1, True) else: - setattr( - self.owner, - varname, - np.array(getattr(self.owner, varname), float) + np.array(der, float) * step_size, - ) + newval = [val[i] + step_size * der[i] for i in range(len(der))] + basevar.setter_internal(newval, -1, False) # disable super() functions and properties which are not in use here def to_xml(self) -> ET.Element: @@ -271,40 +445,17 @@ def start(self): return self._start @start.setter - def start(self, val): - if isinstance(val, (str, int, float, bool, Enum)): - self._start = (val,) - elif isinstance(val, (tuple, list, np.ndarray)): + def start(self, val: PyType | Compound): + if isinstance(val, (Sequence, np.ndarray)): self._start = tuple(val) else: - raise VariableInitError(f"Unallowed start value setting {val} for variable {self.name}") from None + self._start = (val,) @property def unit(self): + """Get the unit object.""" return self._unit - @unit.setter - def unit(self, val): - if isinstance(val, (tuple, list)): - self._unit = tuple(val) - elif isinstance(val, str): - self._unit = (val,) - else: - raise VariableInitError(f"Unallowed unit setting {val} for variable {self.name}") from None - - @property - def display(self): - return self._display - - @display.setter - def display(self, val): - if val is None or (isinstance(val, tuple) and isinstance(val[0], str)): # single variable - self._display = (val,) - elif isinstance(val, tuple) and (val[0] is None or isinstance(val[0], (tuple))): # compound variable - self._display = tuple(val) - else: - raise VariableInitError(f"Unallowed display setting {val} for variable {self.name}") from None - @property def range(self): return self._range @@ -361,19 +512,21 @@ def setter(self, values: Sequence[int | float | bool | str | Enum] | np.ndarray, if self._check & Check.units: #'values' expected as displayUnit. Convert to unit if idx >= 0: # explicit index of single values - if self._display[idx] is not None: - dvals = [self.display[idx][1](values[0])] - else: + if self._unit[idx].du is None: dvals = list(values) + else: + assert isinstance(values[0], float) + dvals = [self._unit[idx].to_base(values[0])] # type: ignore ## values[0] is float! else: # the whole array dvals = [] for i in range(self._len): if values[i] is None: # keep the value dvals.append(getattr(self.owner, self.local_name)[i]) - elif self._display[i] is None: + elif self._unit[i].du is None: dvals.append(values[i]) else: - dvals.append(self.display[i][1](values[i])) + assert isinstance(values[i], float) + dvals.append(self._unit[i].to_base(values[i])) # type: ignore ## it is a float! else: # no unit issues if self._len == 1: dvals = [values[0] if values[0] is not None else getattr(self.owner, self.local_name)] @@ -382,20 +535,30 @@ def setter(self, values: Sequence[int | float | bool | str | Enum] | np.ndarray, values[i] if values[i] is not None else getattr(self.owner, self.local_name)[i] for i in range(self._len) ] + self.setter_internal(dvals, idx, is_ndarray) # do the setting, or flag as dirty + def setter_internal( + self, + values: Sequence[int | float | bool | str | Enum | None] | np.ndarray, + idx: int = -1, + is_ndarray: bool = False, + ): + """Do internal setting of values (no range checking and units expected internal), including dirty flags.""" if self._len == 1: - setattr(self.owner, self.local_name, dvals[0] if self.on_set is None else self.on_set(dvals[0])) # type: ignore + setattr(self.owner, self.local_name, values[0] if self.on_set is None else self.on_set(values[0])) # type: ignore elif idx >= 0: - if dvals[0] is not None: - getattr(self.owner, self.local_name)[idx] = dvals[0] + if values[0] is not None: # Note: only the indexed value is provided, as list! + val = getattr(self.owner, self.local_name) + val[idx] = values[0] + setattr(self.owner, self.local_name, val) if self.on_set is not None: self.model.dirty_ensure(self) else: # the whole array if is_ndarray: # Note: on_set might contain array operations - arr: np.ndarray = np.array(dvals, self._typ) + arr: np.ndarray = np.array(values, self._typ) setattr(self.owner, self.local_name, arr if self.on_set is None else self.on_set(arr)) else: - setattr(self.owner, self.local_name, dvals if self.on_set is None else self.on_set(dvals)) + setattr(self.owner, self.local_name, values if self.on_set is None else self.on_set(values)) if self.on_set is None: logger.debug(f"SETTER {self.name}, {values}[{idx}] => {getattr(self.owner, self.local_name)}") @@ -412,8 +575,8 @@ def getter(self) -> Sequence[PyType]: elif not isinstance(value, self._typ): # other type conversion value = self._typ(value) # type: ignore[call-arg] if self._check & Check.units: # Convert 'value' display.u -> base unit - if self._display[0] is not None: - value = self.display[0][2](value) + if self._unit[0].du is not None: + value = self._unit[0].from_base(value) values = [value] else: # compound variable @@ -427,8 +590,8 @@ def getter(self) -> Sequence[PyType]: values[i] = self._typ(values[i]) # type: ignore[call-arg] if self._check & Check.units: # Convert 'value' display.u -> base unit for i in range(self._len): - if self._display[i] is not None: - values[i] = self.display[i][2](values[i]) + if self._unit[i].du is not None: + values[i] = self._unit[i].from_base(values[i]) if self._check & Check.ranges and not self.check_range(values, -1): raise VariableRangeError(f"getter(): Value {values} outside range.") from None @@ -443,12 +606,10 @@ def _init_range(self, rng: tuple | None) -> tuple: Always for the whole variable with scalar variables packed in a singleton """ - def ensure_display_limits(val: PyType, idx: int, right: bool): + def ensure_display_limits(val: float, idx: int, right: bool): """Ensure that value is provided as display unit and that limits are included in range.""" - if self._display[idx] is not None: # Range in display units! - _val = self._display[idx] - assert isinstance(_val, tuple) - val = _val[2](val) + if self._unit[idx].du is not None: # Range in display units! + val = self._unit[idx].from_base(val) if isinstance(val, float) and abs(val) != float("inf") and int(val) != val: if right: val += 1e-15 @@ -466,10 +627,11 @@ def ensure_display_limits(val: PyType, idx: int, right: bool): for idx in range(self._len): # go through all elements _rng = rng[idx] if _rng is None: # => no range. Used for compound variables if not all elements have a range + assert isinstance( self._start[idx], float) _range.append( ( - ensure_display_limits(self._start[idx], idx, right=False), - ensure_display_limits(self._start[idx], idx, right=True), + ensure_display_limits(self._start[idx], idx, right=False), # type: ignore ## it is a float! + ensure_display_limits(self._start[idx], idx, right=True), # type: ignore ## it is a float! ) ) # no range elif isinstance(_rng, tuple) and not len(_rng): # empty tuple => try automatic range @@ -480,23 +642,23 @@ def ensure_display_limits(val: PyType, idx: int, right: bool): if r is None: # no range => fixed to initial value q = self._start[idx] else: - q, u, du = self._disect_unit(r) - # no explicit unit needed when the quantity is 0 or inf - if (q == 0 or q == float("inf") or q == float("-inf")) and u == "dimensionless": - u = self._unit[idx] - elif self._unit[idx] != u: - raise VariableInitError( - f"The supplied range value {str(r)} does not conform to the unit type {self._unit[idx]}" - ) - elif du is not None and self._display[idx] is not None and du[0] != self._display[idx][0]: # type: ignore[index] - raise VariableInitError(f"Range unit {du[0]} != start {self._display[idx][0]}!") # type: ignore[index] + check, q = self._unit[idx].compatible(r, self.model.ureg, self._typ, strict=True) + if not check: + check, q = self._unit[idx].compatible(r, self.model.ureg, self._typ, strict=False) + if check: + logger.warn(f"{self.name}[{idx}] range {r}: Use display units {self._unit[idx].du}!") + else: + raise VariableInitError( + f"{self.name}[{idx}]: range {r} does not conform to the unit type {self._unit[idx]}" + ) + assert isinstance( q, float) q = ensure_display_limits(q, idx, len(i_range) > 0) i_range.append(q) try: # check variable type i_range = [self._typ(x) for x in i_range] except Exception as err: - raise VariableRangeError(f"Incompatible types range {rng} - {self.start}") from err + raise VariableRangeError(f"Incompatible types range {rng} - {self._start}") from err assert all(isinstance(x, self._typ) for x in i_range) _range.append(tuple(i_range)) # type: ignore else: @@ -524,10 +686,8 @@ def check_range_single(self, value: PyType | None, idx: int = 0, disp: bool = Tr return isinstance(value, self._typ) elif isinstance(value, (int, float)) and all(isinstance(x, (int, float)) for x in self._range[idx]): - if not disp and self._display[idx] is not None: # check an internal unit values - _val = self._display[idx] - assert isinstance(_val, tuple) - value = _val[2](value) + if not disp and self._unit[idx].du is not None: # check an internal unit values + value = self._unit[idx].from_base(value) return self._range[idx] is None or self._range[idx][0] <= value <= self._range[idx][1] # type: ignore else: raise VariableUseError(f"check_range(): value={value}, type={self.typ}, range={self.range}") from None @@ -628,73 +788,6 @@ def _auto_extreme(cls, var: PyType) -> tuple: else: return tuple() # return an empty tuple (no range specified, e.g. for str) - def _disect_unit(self, quantity: PyType | Compound) -> tuple: - """Disect the provided quantity in terms of magnitude and unit, if provided as string. - If another type is provided, dimensionless units are assumed. - - Args: - quantity (PyType): the quantity to disect. Should be provided as string, but also the trivial cases (int,float,Enum) are allowed. - A free string should not be used and leads to a warning - Returns: - the magnitude in base units, the base unit and the unit as given (display units), - together with the conversion functions between the units. - """ - if isinstance(quantity, (tuple, list, np.ndarray)): # handle composit values - _val, _ub, _disp = [], [], [] - for q in quantity: # disect components and collect results - v, u, d = self._disect_unit(q) - _val.append(v) - _ub.append(u) - _disp.append(d) - return (tuple(_val), tuple(_ub), None if _disp is None else tuple(_disp)) - - elif isinstance(quantity, str): # only string variable make sense to disect - assert self.model.ureg is not None, f"UnitRegistry not found, while providing units: {quantity}" - try: - q = self.model.ureg(quantity) # parse the quantity-unit and return a Pint Quantity object - if isinstance(q, (int, float)): - return q, "", None # integer or float variable with no units provided - elif isinstance(q, Quantity): # pint.Quantity object - # transform to base units ('SI' units). All internal calculations will be performed with these - val, ub, display = self._get_transformation(q) - else: - raise VariableInitError(f"Unknown quantity {quantity} to disect") from None - # no recognized units. Assume a free string. ??Maybe we should be more selective about the exact error type: - except Exception as warn: - logger.warning(f"Unhandled quantity {quantity}: {warn}. A str? Set explicit 'typ=str'.") - val, ub, display = (str(quantity), "", None) # type: ignore - else: - val, ub, display = (quantity, "dimensionless", None) # type: ignore - if self._typ is not None and type(val) is not self._typ: # check variable type - try: # try to convert the magnitude to the correct type. - val = self._typ(val) - except Exception as err: - raise VariableInitError(f"Value {val} is not of the correct type {self._typ}") from err - return (val, ub, display) - - def _get_transformation(self, q: Quantity) -> tuple[float, str, tuple | None]: - """Identity base units and calculate the transformations between display and base units.""" - qb = q.to_base_units() - val = qb.magnitude # Note: numeric types are not converted, e.g. int to float - if qb.units == q.units: # no conversion - return (val, str(qb.units), None) - else: # calculate the conversion functions - # we generate a second value and calculate the straight line conversion function - # did not find a better way in pint - q2 = self.model.ureg.Quantity(10.0 * (q.magnitude + 10.0), q.units) - qb2 = q2.to_base_units() - a = (qb.magnitude * q2.magnitude - qb2.magnitude * q.magnitude) / (q2.magnitude - q.magnitude) - b = (qb2.magnitude - qb.magnitude) / (q2.magnitude - q.magnitude) - if abs(a) < 1e-9: # multiplicative conversion - if abs(b - 1.0) < 1e-9: # unit and display unit are compatible. No transformation - return (val, str(qb.units), None) - to_base = partial(linear, b=b) - from_base = partial(linear, b=1.0 / b) - else: # there is a constant (e.g. Celsius to Fahrenheit) - to_base = partial(linear, b, a) - from_base = partial(linear, b=1.0 / b, a=-a / b) - return (val, str(qb.units), (str(q.units), to_base, from_base)) - def xml_scalarvariables(self): """Generate XML code with respect to this variable and return xml element. For compound variables, all elements are included. @@ -708,8 +801,15 @@ def xml_scalarvariables(self): List of ScalarVariable xml elements """ - def substr(alt1: str, alti: str): - return alt1 if self._len == 1 else alti + def indexed_name(fullname: str, idx: int, length: int = 1): + """Provide a proper xml name conformant with structured variables.""" + if length == 1: + return fullname + elif fullname.startswith("der("): # the index cannot be outside ) + assert fullname.endswith(")"), f"Closing parantehesis expected. Found {fullname}" + return f"{fullname[:-1]}[{idx}])" + else: + return f"{fullname}[{idx}]" _type = {"int": "Integer", "bool": "Boolean", "float": "Real", "str": "String", "Enum": "Enumeration"}[ self.typ.__qualname__ @@ -721,7 +821,7 @@ def substr(alt1: str, alti: str): sv = ET.Element( "ScalarVariable", { - "name": self.name + substr("", f"[{i}]"), + "name": indexed_name(self.name, i, self._len), "valueReference": str(self.value_reference + i), "description": "" if self.description is None else self.description, "causality": self.causality.name, @@ -732,13 +832,13 @@ def substr(alt1: str, alti: str): sv.attrib.update({"initial": self._initial.name}) if self._annotations is not None and i == 0: sv.append(ET.Element("annotations", self._annotations)) - # if self.display is None or (self._len>1 and self.display[i] is None): + # if self._unit[ is None or (self._len>1 and self._unit[i].du is None): # "display" = (self.unit, 1.0) # detailed variable definition info = ET.Element(_type) if do_use_start: # a start value is to be used - info.attrib.update({"start": self.fmi_type_str(self.start[i])}) + info.attrib.update({"start": self.fmi_type_str(self._start[i])}) if _type in ("Real", "Integer", "Enumeration"): # range to be specified xMin = self.range[i][0] if _type != "Real" or xMin > float("-inf"): @@ -751,9 +851,9 @@ def substr(alt1: str, alti: str): else: info.attrib.update({"unbounded": "true"}) if _type == "Real": # other attributes apply only to Real variables - info.attrib.update({"unit": self.unit[i]}) - if self.display is not None and self.display[i] is not None and self.unit[i] != self.display[i][0]: - info.attrib.update({"displayUnit": self.display[i][0]}) + info.attrib.update({"unit": self.unit[i].u}) + if isinstance(self._unit[i].du, str) and self.unit[i].du != self._unit[i].du: + info.attrib.update({"displayUnit": self._unit[i].du}) # type: ignore ## it is a str! if a_der is not None: info.attrib.update({"derivative": str(a_der.value_reference + i + 1)}) diff --git a/src/component_model/variable_naming.py b/src/component_model/variable_naming.py index 048592e..4849362 100644 --- a/src/component_model/variable_naming.py +++ b/src/component_model/variable_naming.py @@ -27,7 +27,7 @@ class ParsedVariable: * der: unsigned integer, defining the derivation order. 0 for no derivation """ - def __init__(self, varname: str, convention: VariableNamingConvention): + def __init__(self, varname: str, convention: VariableNamingConvention = VariableNamingConvention.structured): self.parent: str | None # None indicates no parent self.var: str self.indices: list[int] = [] # empty list indicates no indices @@ -43,7 +43,7 @@ def __init__(self, varname: str, convention: VariableNamingConvention): m = re.match(r"der\((.+)\)", varname) if m is not None: vo = m.group(1) - m = re.match(r"(.+),(\d+)$", vo) + m = re.match(r"(.+),\s*(\d+)$", vo) if m is not None: var = m.group(1) self.der = int(m.group(2)) @@ -67,9 +67,31 @@ def as_tuple(self): """Return all fields as tuple.""" return (self.parent, self.var, self.indices, self.der) + def as_string(self, include: tuple[str, ...] = ("parent", "var", "indices", "der"), simplified: bool = True): + """Re-construct the variable name, including what is requested. + + This is convenient to e.g. leave out indices (vector variables) or finding parent names of derivatives. + """ + if "parent" in include: + name = "" if self.parent is None else self.parent + else: + name = "" + if "var" in include: + name = name + "." + self.var if len(name) else self.var + if "indices" in include and len(self.indices): # never empty parantheses + name += str(self.indices) + if "der" in include and self.der > 0: + if not simplified or self.der > 1: + name = f"der({name}, {self.der})" + elif simplified and self.der == 1: + name = f"der({name})" + else: + raise NotImplementedError(f"Unknown combination simplified={simplified}, der={self.der}") from None + return name + @staticmethod def disect_indices(txt: str) -> tuple[str, list[int]]: - m = re.match(r"(.+)\[([\d,]+)\]", txt) + m = re.match(r"(.+)\[([\d,\s*]+)\]", txt) if m is None: return (txt, []) else: diff --git a/tests/test_make_time_table.py b/tests/test_make_time_table.py index ec1100e..c37c590 100644 --- a/tests/test_make_time_table.py +++ b/tests/test_make_time_table.py @@ -230,7 +230,7 @@ def test_make_with_new_data(): data = list(zip(times, np.cos(times), np.sin(times), strict=False)) build_path = Path.cwd() build_path.mkdir(exist_ok=True) - fmu_path = Model.build( + _ = Model.build( script=str(Path(__file__).parent.parent / "examples" / "time_table_fmu.py"), project_files=[Path(__file__).parent.parent / "examples" / "time_table.py"], dest=build_path, diff --git a/tests/test_model.py b/tests/test_model.py index b44eb27..64736c1 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -147,9 +147,9 @@ def test_from_fmu(bouncing_ball_fmu): if __name__ == "__main__": - retcode = 0 # pytest.main(["-rA", "-v", __file__]) + retcode = pytest.main(["-rA", "-v", __file__]) assert retcode == 0, f"Non-zero return code {retcode}" # test_license() # test_xml() - test_from_fmu(_bouncing_ball_fmu()) + # test_from_fmu(_bouncing_ball_fmu()) # test_variable_naming() diff --git a/tests/test_unit.py b/tests/test_unit.py new file mode 100644 index 0000000..4ed85ce --- /dev/null +++ b/tests/test_unit.py @@ -0,0 +1,105 @@ +from math import degrees, radians + +import pytest +from pint import UnitRegistry + +from component_model.variable import Unit + + +@pytest.fixture +def ureg(scope="module", autouse=True): + return _ureg() + + +def _ureg(): + return UnitRegistry(system="SI", autoconvert_offset_to_baseunit=True) + + +def test_parsing(ureg): + u1 = Unit() + # default values: + assert u1.u == "" + assert u1.du is None + val = u1.parse_quantity("9.9m", ureg) + assert val == 9.9 + assert u1.u == "meter" + assert u1.du is None + val = u1.parse_quantity("9.9inch", ureg) + assert val == u1.to_base(9.9), f"Found val={val}" + assert u1.u == "meter" + assert u1.du == "inch" + assert abs(123.456 - u1.to_base(u1.from_base(123.456))) < 1e-13, f"Found {u1.to_base(u1.from_base(123.456))}" + val = u1.parse_quantity("99.0%", ureg) + assert val == 0.99 + assert u1.u == "dimensionless" + assert u1.du == "percent" + assert str(u1) == "Unit dimensionless, display:percent. Offset:0.0, factor:0.01" + + +def test_make(ureg): + val, unit = Unit.make("2m", ureg) + assert val[0] == 2 + assert unit[0].u == "meter", f"Found {unit[0].u}" + assert unit[0].du is None + val, unit = Unit.make("Hello World", ureg=ureg, typ=str) + assert val[0] == "Hello World" + assert unit[0].u == "dimensionless" + assert unit[0].du is None + val, unit = Unit.make("99.0%", ureg=ureg) + assert val[0] == 0.99 + assert unit[0].u == "dimensionless" + assert unit[0].du == "percent" + + +def test_make_tuple(ureg): + vals, units = Unit.make_tuple(("2m", "3deg", "0.0 degF"), ureg) + k2degc = 273.15 + assert units[0].u == "meter" + assert units[0].du is None + assert vals[0] == 2 + assert units[1].u == "radian", f"Found {units[1].u}" + assert units[1].du == "degree" + assert units[1].to_base(1.0) == radians(1.0) + assert units[1].from_base(1.0) == degrees(1.0) + assert vals[1] == radians(3) + assert units[2].u == "kelvin", f"Found {units[2].u}" + assert units[2].du == "degree_Fahrenheit", f"Found {units[2].du}" + assert abs(units[2].from_base(k2degc) - (k2degc * 9 / 5 - 459.67)) < 1e-10 + assert abs(units[2].to_base(0.0) - (0.0 + 459.67) * 5 / 9) < 1e-10, ( + f"Found {units[2].to_base(0.0)}, {(0.0 + 459.67) * 5 / 9}" + ) + + +def test_derivative(ureg): + bv, bu = Unit.make_tuple(("2m", "3deg"), ureg) + vals, units = Unit.derivative(bu) + assert vals == (0.0, 0.0) + assert units[0].u == "meter/s" + assert units[0].du is None + assert units[1].u == "radian/s", f"Found {units[1].u}" + assert units[1].du == "degree/s" + assert units[1].to_base == bu[1].to_base + assert units[1].from_base == bu[1].from_base + + +def test_compatible(ureg): + v, u = Unit.make_tuple(("2m", "3deg"), ureg) + ck, q = u[0].compatible("4m", ureg, strict=True) + assert ck + assert q == 4 + ck, q = u[1].compatible("5 radian", ureg, strict=True) + assert not ck, "Not compatible for 'strict'" + ck, q = u[1].compatible("5 radian", ureg, strict=False) + assert ck, "Ok for non-strict" + ck, q = u[0].compatible("5 radian", ureg, strict=False) + assert not ck, "Totally wrong units" + + +if __name__ == "__main__": + retcode = 0 # pytest.main(["-rP -s -v", __file__]) + assert retcode == 0, f"Return code {retcode}" + # test_parsing(_ureg()) + # test_make(_ureg()) + # test_make_tuple( _ureg()) + # test_derivative( _ureg()) + test_compatible( _ureg()) diff --git a/tests/test_variable.py b/tests/test_variable.py index 9e56c43..250395e 100644 --- a/tests/test_variable.py +++ b/tests/test_variable.py @@ -239,8 +239,8 @@ def test_init(): # internally packed into tuple: assert int1.start == (99,) assert int1.range == ((0, 100),) - assert int1.unit == ("dimensionless",) - assert int1.display == (None,) + assert int1.unit[0].u == "dimensionless" + assert int1.unit[0].du is None assert int1.check_range([50]) assert not int1.check_range([110]) assert mod.int1 == 99, "Value directly accessible as model variable" @@ -263,10 +263,10 @@ def test_init(): # internally packed into tuple: assert float1.start == (0.99,) assert float1.range == ((0, 99.0),), f"Range: {float1.range} in display units." - assert float1.unit == ("dimensionless",) - assert float1.display[0][0] == "percent", f"Display: {float1.display[0][0]}" - assert float1.display[0][1](99) == 0.99, "Transform from dimensionless to percent" - assert float1.display[0][2](0.99) == 99, "... and back." + assert float1.unit[0].u == "dimensionless" + assert float1.unit[0].du == "percent", f"Display: {float1.unit[0].du}" + assert float1.unit[0].to_base(99) == 0.99, "Transform to dimensionless" + assert float1.unit[0].from_base(0.99) == 99, "... and back." assert float1.check_range([0.5]) assert not float1.check_range([1.0], disp=False), "Check as internal units" assert not float1.check_range([100.0]), "Check as display units" @@ -291,8 +291,8 @@ def test_init(): # internally packed into tuple: assert enum1.start == (Causality.parameter,) assert enum1.range == ((0, 4),), f"Range: {enum1.range}" - assert enum1.unit == ("dimensionless",) - assert enum1.display[0] is None, f"Display: {enum1.display[0]}" + assert enum1.unit[0].u == "dimensionless" + assert enum1.unit[0].du is None, f"Display: {enum1.unit[0].du}" assert enum1.check_range([1]) assert not enum1.check_range([7]) assert mod.enum1 == Causality.parameter, f"Value {mod.enum1} directly accessible as model variable" @@ -311,8 +311,8 @@ def test_init(): # internally packed into tuple: assert bool1.start == (True,) assert bool1.range == ((False, True),) - assert bool1.unit == ("dimensionless",) - assert bool1.display == (None,) + assert bool1.unit[0].u == "dimensionless" + assert bool1.unit[0].du is None assert bool1.check_range([True]) assert bool1.check_range([100.5]), "Any number will work" assert not bool1.check_range("Hei"), "But non-numbers are rejected" @@ -333,8 +333,8 @@ def test_init(): # internally packed into tuple: assert str1.start == ("Hello World!",) assert str1.range == (("", ""),), f"Range: {str1.range}. Basically irrelevant" - assert str1.unit == ("dimensionless",), f"Unit {str1.unit}" - assert str1.display[0] is None, f"Display: {str1.display[0]}" + assert str1.unit[0].u == "dimensionless", f"Unit {str1.unit}" + assert str1.unit[0].du is None, f"Display: {str1.unit[0].du}" assert str1.check_range([0.5]), "Everything is ok" assert mod.str1 == "Hello World!", f"Value {mod.str1} directly accessible as model variable" mod.str1 = 1.0 # type: ignore # intentional misuse @@ -358,11 +358,12 @@ def test_init(): tuples_nearly_equal(np1.range, ((0, 3), (1, 5), (float("-inf"), 5))) assert not np1.check_range([5.1], idx=1), "Checks performed on display units!" assert not np1.check_range([0.9], idx=1), "Checks performed on display units!" - assert np1.unit == ("meter", "radian", "radian"), f"Units: {np1.unit}" - assert isinstance(np1.display, tuple) and len(np1.display) == 3, "Tuple of length 3 expected" - assert np1.display[0] is None - assert np1.display[1][0] == "degree" - assert np1.display[2] is None + assert tuple(x.u for x in np1.unit) == ("meter", "radian", "radian"), f"Units: {np1.unit}" + disp = tuple(x.du for x in np1.unit) + assert isinstance(disp, tuple) and len(disp) == 3, "Tuple of length 3 expected" + assert disp[0] is None + assert disp[1] == "degree" + assert disp[2] is None assert np1.check_range((2, 3.5, 4.5)) assert not np1.check_range((2, 3.5, 6.3), -1), f"Range is {np1.range}" assert mod.np1[1] == math.radians(2), "Value directly accessible as model variable" diff --git a/tests/test_variable_naming.py b/tests/test_variable_naming.py index 96515c2..8f93bda 100644 --- a/tests/test_variable_naming.py +++ b/tests/test_variable_naming.py @@ -13,11 +13,11 @@ ("resistor12.u", ("resistor12", "u", [], 0)), ("v_min", (None, "v_min", [], 0)), ("robot.axis.'motor #234'", ("robot.axis", "'motor #234'", [], 0)), - ("der(pipe[3,4].T[14],2)", ("pipe[3,4]", "T", [14], 2)), + ("der(pipe[3,4].T[14], 2)", ("pipe[3,4]", "T", [14], 2)), ("der(pipe[3,4].T[14])", ("pipe[3,4]", "T", [14], 1)), ("pipe[3,4].T[14]", ("pipe[3,4]", "T", [14], 0)), - ("T[14].pipe[3,4]", ("T[14]", "pipe", [3, 4], 0)), - ("der(pos,1)", (None, "pos", [], 1)), + ("T[14].pipe[3, 4]", ("T[14]", "pipe", [3, 4], 0)), + ("der(pos)", (None, "pos", [], 1)), ("der(wheels[0].motor.rpm)", ("wheels[0].motor", "rpm", [], 1)), ] @@ -29,12 +29,23 @@ def test_basic_re_expressions(txt, expected): tpl = (parsed.parent, parsed.var, parsed.indices, parsed.der) for i in range(4): assert tpl[i] == expected[i], f"Test:{txt}. Variable element {i} {tpl[i]} != {expected[i]}" - # print(tpl) + assert parsed.as_string() == txt, f"as_string {parsed.as_tuple()}: {parsed.as_string()}. Expected:{txt}" + + +def test_as_string(): + parsed = ParsedVariable("der(pipe[3,4].T[14], 2)", VariableNamingConvention.structured) + assert parsed.as_string(("parent", "var")) == "pipe[3,4].T" + assert parsed.as_string(("parent", "var", "indices")) == "pipe[3,4].T[14]" + parsed = ParsedVariable("der(wheels[0].motor.rpm)") + assert parsed.as_string(("parent", "var")) == "wheels[0].motor.rpm" + assert parsed.as_string(("parent", "var", "indices")) == "wheels[0].motor.rpm" + assert parsed.as_string(("parent", "var", "indices", "der"), simplified=False) == "der(wheels[0].motor.rpm, 1)" if __name__ == "__main__": - retcode = 0 # pytest.main(["-rA", "-v", __file__]) + retcode = pytest.main(["-rA", "-v", __file__]) assert retcode == 0, f"Non-zero return code {retcode}" - for c, e in test_cases: - print(c, e) - test_basic_re_expressions(c, e) + # for c, e in test_cases: + # print(c, e) + # test_basic_re_expressions(c, e) + # test_as_string() From d90e6714ec891967d6c0c5cba964221a434abaa5 Mon Sep 17 00:00:00 2001 From: Eisinger Date: Sun, 18 May 2025 19:04:44 +0200 Subject: [PATCH 03/29] Fully tested the changes related to the separate Unit class. So far libcosimpy< 0.0.3.post1 in pyproject.toml --- pyproject.toml | 9 +++----- src/component_model/model.py | 6 ++--- src/component_model/variable.py | 39 ++++++++++++++++++--------------- tests/test_bouncing_ball_3d.py | 7 +++--- tests/test_time_table.py | 2 ++ tests/test_unit.py | 2 +- 6 files changed, 34 insertions(+), 31 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 17091e1..259ec83 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,7 @@ only-include = [ [tool.hatch.build.targets.wheel] packages = [ "src/component_model", + "examples", ] [project] @@ -60,11 +61,10 @@ dependencies = [ "jsonpath-ng>=1.7.0", "pythonfmu>=0.6.7", "flexparser<0.4", - "pdfminer>=20191125", - "pypdf2>=3.0.1", "scipy>=1.15.1", "plotly>=6.0.1", - "libcosimpy>=0.0.3.post1", + "libcosimpy < 0.0.3.post1", + "thonny>=4.1.7", ] [project.optional-dependencies] @@ -76,9 +76,6 @@ modelTest = [ rest = [ "docutils>=0.21", ] -editor = [ - "thonny>=4.1", -] [project.urls] Homepage = "https://github.com/dnv-opensource/component-model" diff --git a/src/component_model/model.py b/src/component_model/model.py index 3cae8d8..5bb60c7 100644 --- a/src/component_model/model.py +++ b/src/component_model/model.py @@ -188,7 +188,7 @@ def _unit_ensure_registered(self, candidate: Variable): cu = candidate.unit[i] for u in self._units: if cu.u == u.u and cu.du == u.du: # already registered - return + break self._units.append(cu) def owner_hierarchy(self, parent: str | None) -> list: @@ -525,7 +525,7 @@ def xml_unit_definitions(self): defs = ET.Element("UnitDefinitions") u_done: list[str] = [] for u in self._units: # all registered unit objects - unit = ET.Element("NoUnit") # dummy element + unit = ET.Element("NoUnit") # dummy element if u.u not in u_done: # multiple entries are possible if there are multiple display units ubase = self.ureg(u.u).to_base_units() dim = ubase.dimensionality @@ -567,7 +567,7 @@ def xml_unit_definitions(self): }, ) ) - if isinstance( _u.du, str): + if isinstance(_u.du, str): du_done.append(_u.du) u_done.append(u.u) if unit.tag != "NoUnit": diff --git a/src/component_model/variable.py b/src/component_model/variable.py index 0ff9643..0c365ff 100644 --- a/src/component_model/variable.py +++ b/src/component_model/variable.py @@ -58,9 +58,8 @@ class Unit: def __init__(self): self.u = "" # unit as string (placeholder) self.du = None # display unit (default: same as u, no transformation) - self.to_base = partial(Unit.identity) # ensure a definition - self.from_base = partial(Unit.identity) # ensure a definition - + self.to_base = partial(Unit.identity) # ensure a definition + self.from_base = partial(Unit.identity) # ensure a definition def __str__(self): txt = f"Unit {self.u}, display:{self.du}" @@ -116,7 +115,7 @@ def parse_quantity(self, quantity: PyType, ureg: UnitRegistry, typ: type | None @classmethod def linear(cls, x: float, b: float, a: float = 0.0): return a + b * x - + @classmethod def identity(cls, x: float): return x @@ -202,7 +201,9 @@ def compatible( unit = _unit[0] # no explicit unit needed when the quantity is 0 or inf (anything compatible) if ( - ((q == 0 or q == float("inf") or q == float("-inf")) and unit.u == "dimensionless") # 0, +/-inf without unit + ( + (q == 0 or q == float("inf") or q == float("-inf")) and unit.u == "dimensionless" + ) # 0, +/-inf without unit or (strict and self.u == unit.u and self.du == unit.du) or (not strict and self.u == unit.u) ): @@ -384,7 +385,7 @@ def __init__( self.local_name = local_name # use explicitly provided local name if self._typ is str: # explicit free string - assert isinstance( start, str) + assert isinstance(start, str) self._len = 1 self._start, self._unit = Unit.make(start, self.model.ureg, typ=str) self.range = ("", "") # just a placeholder. Strings are not range checked @@ -420,10 +421,12 @@ def der1(self, current_time: float, step_size: float): if (isinstance(der, float) and der != 0.0) or ( isinstance(der, (Sequence, np.ndarray)) and any(x != 0.0 for x in der) ): # there is a slope - if not isinstance(der, (Sequence, np.ndarray)): - der = [der] varname = self.local_name[5:] # local name of the base variable val = getattr(self.owner, varname) # previous value of base variable + if not isinstance(der, (Sequence, np.ndarray)): + der = [der] + assert not isinstance(val, (Sequence, np.ndarray)), "Should be the same as der" + val = [val] is_nparray = isinstance(val, np.ndarray) basevar = self.model.derivatives[self.name] # base variable object if is_nparray: @@ -515,8 +518,8 @@ def setter(self, values: Sequence[int | float | bool | str | Enum] | np.ndarray, if self._unit[idx].du is None: dvals = list(values) else: - assert isinstance(values[0], float) - dvals = [self._unit[idx].to_base(values[0])] # type: ignore ## values[0] is float! + # assert isinstance(values[0], float) + dvals = [self._unit[idx].to_base(values[0])] # type: ignore ## values[0] is float! else: # the whole array dvals = [] for i in range(self._len): @@ -525,8 +528,8 @@ def setter(self, values: Sequence[int | float | bool | str | Enum] | np.ndarray, elif self._unit[i].du is None: dvals.append(values[i]) else: - assert isinstance(values[i], float) - dvals.append(self._unit[i].to_base(values[i])) # type: ignore ## it is a float! + # assert isinstance(values[i], float) or (self._typ is int and isinstance(values[i], int)) + dvals.append(self._unit[i].to_base(values[i])) # type: ignore ## it is a float! else: # no unit issues if self._len == 1: dvals = [values[0] if values[0] is not None else getattr(self.owner, self.local_name)] @@ -627,11 +630,11 @@ def ensure_display_limits(val: float, idx: int, right: bool): for idx in range(self._len): # go through all elements _rng = rng[idx] if _rng is None: # => no range. Used for compound variables if not all elements have a range - assert isinstance( self._start[idx], float) + assert isinstance(self._start[idx], float) _range.append( ( - ensure_display_limits(self._start[idx], idx, right=False), # type: ignore ## it is a float! - ensure_display_limits(self._start[idx], idx, right=True), # type: ignore ## it is a float! + ensure_display_limits(self._start[idx], idx, right=False), # type: ignore ## it is a float! + ensure_display_limits(self._start[idx], idx, right=True), # type: ignore ## it is a float! ) ) # no range elif isinstance(_rng, tuple) and not len(_rng): # empty tuple => try automatic range @@ -651,7 +654,7 @@ def ensure_display_limits(val: float, idx: int, right: bool): raise VariableInitError( f"{self.name}[{idx}]: range {r} does not conform to the unit type {self._unit[idx]}" ) - assert isinstance( q, float) + assert isinstance(q, float) or (self._typ is int and isinstance(q, int)) q = ensure_display_limits(q, idx, len(i_range) > 0) i_range.append(q) @@ -852,8 +855,8 @@ def indexed_name(fullname: str, idx: int, length: int = 1): info.attrib.update({"unbounded": "true"}) if _type == "Real": # other attributes apply only to Real variables info.attrib.update({"unit": self.unit[i].u}) - if isinstance(self._unit[i].du, str) and self.unit[i].du != self._unit[i].du: - info.attrib.update({"displayUnit": self._unit[i].du}) # type: ignore ## it is a str! + if isinstance(self._unit[i].du, str) and self.unit[i].du != self._unit[i].u: + info.attrib.update({"displayUnit": self._unit[i].du}) # type: ignore ## it is a str! if a_der is not None: info.attrib.update({"derivative": str(a_der.value_reference + i + 1)}) diff --git a/tests/test_bouncing_ball_3d.py b/tests/test_bouncing_ball_3d.py index cf58a67..cc7314e 100644 --- a/tests/test_bouncing_ball_3d.py +++ b/tests/test_bouncing_ball_3d.py @@ -75,7 +75,8 @@ def test_bouncing_ball_class(show): from examples.bouncing_ball_3d import BouncingBall3D bb = BouncingBall3D() - assert bb._pos.display is not None + assert bb._pos.unit[0].u == "meter" + assert bb._pos.unit[2].du == "inch" assert bb._pos.setter is not None assert bb._pos.getter is not None assert bb._speed.getter is not None @@ -94,7 +95,7 @@ def get_result(): result.append((bb.time, *_pos, *_speed, *_p_bounce)) h_fac = 1.0 - if len(bb._pos.display) > 1 and bb._pos.display[2] is not None: # the main test settings + if len(bb._pos) > 1 and bb._pos.unit[2].du is not None: # the main test settings arrays_equal(bb.pos, (0, 0, 10 * 0.0254)) # was provided as inch arrays_equal(bb.speed, (1, 0, 0)) assert bb.g == 9.81 @@ -109,7 +110,7 @@ def get_result(): dt = bb.default_experiment.step_size assert dt == 0.01 # set start values (in display units. Are translated to internal units - if len(bb._pos.display) > 1 and bb._pos.display[2] is not None: + if len(bb._pos) > 1 and bb._pos.unit[2].du is not None: bb._pos.setter((0, 0, 10)) t_b, p_b = bb.next_bounce() assert t_bounce == t_b diff --git a/tests/test_time_table.py b/tests/test_time_table.py index f108462..a12e40a 100644 --- a/tests/test_time_table.py +++ b/tests/test_time_table.py @@ -28,6 +28,8 @@ def arrays_equal( def test_time_table(show: bool = False): + from examples.time_table import TimeTable + tbl = TimeTable( data=((0.0, 1, 0, 0), (1.0, 1, 1, 1), (3.0, 1, 3, 9), (7.0, 1, 7, 49)), header=("x", "y", "z"), diff --git a/tests/test_unit.py b/tests/test_unit.py index 4ed85ce..7e59be4 100644 --- a/tests/test_unit.py +++ b/tests/test_unit.py @@ -102,4 +102,4 @@ def test_compatible(ureg): # test_make(_ureg()) # test_make_tuple( _ureg()) # test_derivative( _ureg()) - test_compatible( _ureg()) + test_compatible(_ureg()) From fb5d0777f17db815edc41318f80255bf7d51b2e7 Mon Sep 17 00:00:00 2001 From: Eisinger Date: Thu, 22 May 2025 10:37:21 +0200 Subject: [PATCH 04/29] With structured variable derivatives der(,int), no space is allowed after ','. Adapted. Still on libcosimpy<0.03.post1. --- src/component_model/model.py | 4 +- src/component_model/variable.py | 41 +++++++---------- src/component_model/variable_naming.py | 51 ++++++++++++++------- tests/test_variable_naming.py | 62 +++++++++++++++++++------- 4 files changed, 98 insertions(+), 60 deletions(-) diff --git a/src/component_model/model.py b/src/component_model/model.py index 5bb60c7..5446aea 100644 --- a/src/component_model/model.py +++ b/src/component_model/model.py @@ -624,10 +624,10 @@ def _xml_structure_derivatives(self): ders = ET.Element("Derivatives") for v in filter( - lambda v: v is not None and v.antiderivative() is not None, + lambda v: v is not None and v.primitive() is not None, self.vars.values(), ): - i_a_der = v.antiderivative().value_reference + i_a_der = v.primitive().value_reference for i in range(len(v)): # works for vectors and scalars ders.append( ET.Element( diff --git a/src/component_model/variable.py b/src/component_model/variable.py index 0c365ff..e38bad8 100644 --- a/src/component_model/variable.py +++ b/src/component_model/variable.py @@ -364,16 +364,11 @@ def __init__( if parsed.der > 0: # is a derivative of 'var' self.local_name = f"der{parsed.der}_{parsed.var}" if not hasattr(self.owner, self.local_name): # a virtual derivative - if parsed.der != 1: - raise NotImplementedError( - "Virtual higher order derivatives should be implemented iteratively." - ) from None - - basevar = self.model.add_derivative(self.name, parsed.as_string(("parent", "var"))) - assert isinstance(basevar, Variable), "The primitive of a derivative must be a Variable object" - assert basevar.typ is float, ( - f"The primitive the derivative {self.name} shall be float. Found {basevar.typ}" + basevar = self.model.add_derivative( + self.name, parsed.as_string(("parent", "var", "der"), primitive=True) ) + assert isinstance(basevar, Variable), f"The primitive of {self.name} must be a Variable object" + assert basevar.typ is float, f"The primitive of {self.name} shall be float. Found {basevar.typ}" self._typ = float if start is None: self._start, self._unit = Unit.derivative(basevar.unit) @@ -804,27 +799,21 @@ def xml_scalarvariables(self): List of ScalarVariable xml elements """ - def indexed_name(fullname: str, idx: int, length: int = 1): - """Provide a proper xml name conformant with structured variables.""" - if length == 1: - return fullname - elif fullname.startswith("der("): # the index cannot be outside ) - assert fullname.endswith(")"), f"Closing parantehesis expected. Found {fullname}" - return f"{fullname[:-1]}[{idx}])" - else: - return f"{fullname}[{idx}]" - _type = {"int": "Integer", "bool": "Boolean", "float": "Real", "str": "String", "Enum": "Enumeration"}[ self.typ.__qualname__ ] # translation of python to FMI primitives. Same for all components do_use_start = use_start(causality=self._causality, variability=self._variability, initial=self._initial) svars = [] - a_der = self.antiderivative() # d a_der /dt = self, or None + a_der = self.primitive() # d a_der /dt = self, or None for i in range(self._len): + if self._len > 1: + varname = ParsedVariable(self.name).as_string(index=str(i)) + else: + varname = ParsedVariable(self.name).as_string(include=("parent", "var", "der")) sv = ET.Element( "ScalarVariable", { - "name": indexed_name(self.name, i, self._len), + "name": varname, "valueReference": str(self.value_reference + i), "description": "" if self.description is None else self.description, "causality": self.causality.name, @@ -864,14 +853,16 @@ def indexed_name(fullname: str, idx: int, length: int = 1): svars.append(sv) return svars - def antiderivative(self) -> Variable | None: + def primitive(self) -> Variable | None: """Determine the variable which self is the derivative of. Return None if self is not a derivative. """ - if self.name.startswith("der(") and self.name.endswith(")"): - return self.model.variable_by_name(self.name[4:-1]) - else: + parsed = ParsedVariable(self.name) + if parsed.der == 0: return None + else: + name = parsed.as_string(("parent", "var", "der"), simplified=True, primitive=True) + return self.model.variable_by_name(name) # Utility functions for handling special variable types diff --git a/src/component_model/variable_naming.py b/src/component_model/variable_naming.py index 4849362..207c4b8 100644 --- a/src/component_model/variable_naming.py +++ b/src/component_model/variable_naming.py @@ -40,19 +40,20 @@ def __init__(self, varname: str, convention: VariableNamingConvention = Variable self.indices = indices self.der = 0 else: # structured variable naming (only these two are defined) - m = re.match(r"der\((.+)\)", varname) - if m is not None: + self.der = 0 # default and count start + var = varname + while True: + m = re.match(r"der\((.+)\)", var) # der(*) + if m is None: + break vo = m.group(1) - m = re.match(r"(.+),\s*(\d+)$", vo) + m = re.match(r"(.+),\s*(\d+)$", vo) # check order of der if m is not None: var = m.group(1) - self.der = int(m.group(2)) + self.der += int(m.group(2)) else: var = vo - self.der = 1 - else: - var = varname - self.der = 0 + self.der += 1 varlist = var.split(".") if len(varlist) > 1: self.parent = varlist[0] + "".join("." + varlist[i] for i in range(1, len(varlist) - 1)) @@ -67,8 +68,20 @@ def as_tuple(self): """Return all fields as tuple.""" return (self.parent, self.var, self.indices, self.der) - def as_string(self, include: tuple[str, ...] = ("parent", "var", "indices", "der"), simplified: bool = True): - """Re-construct the variable name, including what is requested. + def as_string( + self, + include: tuple[str, ...] = ("parent", "var", "indices", "der"), + simplified: bool = True, + primitive: bool = False, + index: str = "", + ): + """Re-construct the variable name, including what is requested and optionally adapting. + + Args: + include (tuple): list of strings of what to incdule of "parent", "var", "indices", "der" + simplified (bool)= True: Optionally change the style to also including superfluous info + primitive (bool)= False: If true, 'der' included and >0, determines the name of the primitive. + index (str)="": If !="" and 'indices' included, specifies the index to use This is convenient to e.g. leave out indices (vector variables) or finding parent names of derivatives. """ @@ -78,15 +91,21 @@ def as_string(self, include: tuple[str, ...] = ("parent", "var", "indices", "der name = "" if "var" in include: name = name + "." + self.var if len(name) else self.var - if "indices" in include and len(self.indices): # never empty parantheses - name += str(self.indices) + if "indices" in include and (len(self.indices) or index != ""): # never empty parantheses + if index != "": + name += f"[{index}]" + else: + name += str(self.indices) if "der" in include and self.der > 0: - if not simplified or self.der > 1: - name = f"der({name}, {self.der})" - elif simplified and self.der == 1: + der = self.der if not primitive else self.der - 1 + if der == 0: + pass # just the name + elif not simplified or der > 1: + name = f"der({name},{der})" + elif simplified and der == 1: name = f"der({name})" else: - raise NotImplementedError(f"Unknown combination simplified={simplified}, der={self.der}") from None + raise NotImplementedError(f"Unknown combination simplified={simplified}, der={der}") from None return name @staticmethod diff --git a/tests/test_variable_naming.py b/tests/test_variable_naming.py index 8f93bda..6aec782 100644 --- a/tests/test_variable_naming.py +++ b/tests/test_variable_naming.py @@ -8,28 +8,57 @@ logging.basicConfig(level=logging.INFO) +def test_single(): + which = -1 + if which == -1 or which == 1: + parsed = ParsedVariable("der(pos,1)", VariableNamingConvention.structured) + assert parsed.as_string() == "der(pos)" + assert parsed.as_tuple() == (None, "pos", [], 1) + assert parsed.var == "pos" + if which == -1 or which == 2: + parsed = ParsedVariable("der(pos,2)", VariableNamingConvention.structured) + assert parsed.as_string() == "der(pos,2)" + if which == -1 or which == 3: + parsed = ParsedVariable("der(der(pos,1))", VariableNamingConvention.structured) + assert parsed.as_tuple() == (None, "pos", [], 2), f"Found {parsed.as_tuple()}" + assert parsed.as_string(simplified=True, primitive=True) == "der(pos)" + if which == -1 or which == 4: + parsed = ParsedVariable("der(pipe[3,4].T[14])", VariableNamingConvention.structured) + assert parsed.as_string(primitive=True, index="13") == "pipe[3,4].T[13]" + + test_cases = [ - ("vehicle.engine.speed", ("vehicle.engine", "speed", [], 0)), - ("resistor12.u", ("resistor12", "u", [], 0)), - ("v_min", (None, "v_min", [], 0)), - ("robot.axis.'motor #234'", ("robot.axis", "'motor #234'", [], 0)), - ("der(pipe[3,4].T[14], 2)", ("pipe[3,4]", "T", [14], 2)), - ("der(pipe[3,4].T[14])", ("pipe[3,4]", "T", [14], 1)), - ("pipe[3,4].T[14]", ("pipe[3,4]", "T", [14], 0)), - ("T[14].pipe[3, 4]", ("T[14]", "pipe", [3, 4], 0)), - ("der(pos)", (None, "pos", [], 1)), - ("der(wheels[0].motor.rpm)", ("wheels[0].motor", "rpm", [], 1)), + ("vehicle.engine.speed", ("vehicle.engine", "speed", [], 0), ""), + ("resistor12.u", ("resistor12", "u", [], 0), ""), + ("v_min", (None, "v_min", [], 0), ""), + ("robot.axis.'motor #234'", ("robot.axis", "'motor #234'", [], 0), ""), + ("der(pipe[3,4].T[14],2)", ("pipe[3,4]", "T", [14], 2), ""), + ("der(pipe[3,4].T[14])", ("pipe[3,4]", "T", [14], 1), ""), + ("pipe[3,4].T[14]", ("pipe[3,4]", "T", [14], 0), ""), + ("T[14].pipe[3, 4]", ("T[14]", "pipe", [3, 4], 0), ""), + ("der(pos)", (None, "pos", [], 1), ""), + ("der(pos,1)", (None, "pos", [], 1), "der(pos)"), + ("der(wheels[0].motor.rpm)", ("wheels[0].motor", "rpm", [], 1), ""), + ("der(der(wheels[0].motor.rpm))", ("wheels[0].motor", "rpm", [], 2), "der(wheels[0].motor.rpm,2)"), + ("der(wheels[0].motor.rpm,2)", ("wheels[0].motor", "rpm", [], 2), ""), ] -@pytest.mark.parametrize("txt, expected", test_cases) -def test_basic_re_expressions(txt, expected): +@pytest.mark.parametrize("txt, expected, text", test_cases) +def test_basic_re_expressions(txt, expected, text): """Test the expressions used in variable_naming.""" parsed = ParsedVariable(txt, VariableNamingConvention.structured) tpl = (parsed.parent, parsed.var, parsed.indices, parsed.der) for i in range(4): assert tpl[i] == expected[i], f"Test:{txt}. Variable element {i} {tpl[i]} != {expected[i]}" - assert parsed.as_string() == txt, f"as_string {parsed.as_tuple()}: {parsed.as_string()}. Expected:{txt}" + expected_txt = txt if text == "" else text + assert parsed.as_string() == expected_txt, f"as_string {parsed.as_tuple()}: {parsed.as_string()}. Expected:{txt}" + + +def _test_basic_re_expressions(): + for c, e, t in test_cases: + print(c, e, t) + test_basic_re_expressions(c, e, t) def test_as_string(): @@ -39,13 +68,12 @@ def test_as_string(): parsed = ParsedVariable("der(wheels[0].motor.rpm)") assert parsed.as_string(("parent", "var")) == "wheels[0].motor.rpm" assert parsed.as_string(("parent", "var", "indices")) == "wheels[0].motor.rpm" - assert parsed.as_string(("parent", "var", "indices", "der"), simplified=False) == "der(wheels[0].motor.rpm, 1)" + assert parsed.as_string(("parent", "var", "indices", "der"), simplified=False) == "der(wheels[0].motor.rpm,1)" if __name__ == "__main__": retcode = pytest.main(["-rA", "-v", __file__]) assert retcode == 0, f"Non-zero return code {retcode}" - # for c, e in test_cases: - # print(c, e) - # test_basic_re_expressions(c, e) + # test_single() + # _test_basic_re_expressions() # test_as_string() From 653a65a2c77d780682c0d75dfe438bec480f39c9 Mon Sep 17 00:00:00 2001 From: Eisinger Date: Thu, 22 May 2025 14:37:30 +0200 Subject: [PATCH 05/29] Defined return of Variable.getter() uniquely as list (not Sequence) --- src/component_model/variable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/component_model/variable.py b/src/component_model/variable.py index e38bad8..07d308a 100644 --- a/src/component_model/variable.py +++ b/src/component_model/variable.py @@ -560,7 +560,7 @@ def setter_internal( if self.on_set is None: logger.debug(f"SETTER {self.name}, {values}[{idx}] => {getattr(self.owner, self.local_name)}") - def getter(self) -> Sequence[PyType]: + def getter(self) -> list[PyType]: """Get the value (output a value from the model), including range checking and unit conversion. The whole variable value is returned. The return a list of values. Can later be indexed/sliced to get elements of compound variables. From f582a47f8392c987c0da4930cb61a7afeae39bec Mon Sep 17 00:00:00 2001 From: Eisinger Date: Thu, 5 Jun 2025 15:27:58 +0200 Subject: [PATCH 06/29] Draft changes with respect to using the ECCO algorithm within libcosimpy --- docs/source/component-model.pptx | Bin 1328268 -> 1335685 bytes examples/ForcedOscillator.xml | 14 +- examples/TimeTableStructure.xml | 1 + examples/driving_force_fmu.py | 24 ++- examples/oscillator.py | 13 +- pyproject.toml | 21 ++- src/component_model/model.py | 2 +- tests/test_bouncing_ball_3d.py | 1 - tests/test_make_oscillator_fmu.py | 258 -------------------------- tests/test_make_time_table.py | 283 ----------------------------- tests/test_oscillator.py | 110 ++++++++++- tests/test_structured_variables.py | 156 ---------------- 12 files changed, 159 insertions(+), 724 deletions(-) delete mode 100644 tests/test_make_oscillator_fmu.py delete mode 100644 tests/test_make_time_table.py delete mode 100644 tests/test_structured_variables.py diff --git a/docs/source/component-model.pptx b/docs/source/component-model.pptx index bda9710c09aaef86c0ad657c8ec458c219b3ed49..cca11cf856f0b74519b8425007fbc6e74bc6cde5 100644 GIT binary patch delta 36722 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%0ACXdG1Ef@0zsTNldQYq@E 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 0.0 0.01 + fixedStep - + + 0.99 + 0.0001 + 0.00001 + 0.01 + 0.2 + 1.5 + 0.2 + 0.15 + 1e-6 + 1e-6 + \ No newline at end of file diff --git a/examples/TimeTableStructure.xml b/examples/TimeTableStructure.xml index b1617ef..32e8cfd 100644 --- a/examples/TimeTableStructure.xml +++ b/examples/TimeTableStructure.xml @@ -1,6 +1,7 @@ 0.0 0.1 + fixedStep diff --git a/examples/driving_force_fmu.py b/examples/driving_force_fmu.py index 41a9c19..bfef884 100644 --- a/examples/driving_force_fmu.py +++ b/examples/driving_force_fmu.py @@ -15,8 +15,14 @@ # Note: PythonFMU (which component-model is built on) works on files and thus only one Model class allowed per file -def func(time: float, ampl: float = 1.0, omega: float = 0.1): - return np.array((0, 0, ampl * sin(omega * time)), float) +def func(time: float, ampl: float = 1.0, omega: float = 0.1, d_omega: float = 0.0): + """Generate a harmonic oscillating force function. + Optionally it is possible to linearly change the angular frequency as omega + d_omega*time. + """ + if d_omega == 0.0: + return np.array((0, 0, ampl * sin(omega * time)), float) + else: + return np.array((0, 0, ampl * sin((omega + d_omega*time) * time)), float) class DrivingForce(Model): @@ -33,9 +39,10 @@ class DrivingForce(Model): def __init__( self, - func: Callable[..., Any] = func, + function: Callable[..., Any] = func, ampl: float = 1.0, freq: float = 1.0, + d_freq: float = 0.0, **kwargs: Any, ): super().__init__( @@ -47,6 +54,8 @@ def __init__( # interface Variables self._ampl = Variable(self, "ampl", "The amplitude of the force in N", start=ampl) self._freq = Variable(self, "freq", "The frequency of the force in 1/s", start=freq) + self._d_freq = Variable(self, "d_freq", "Change of frequency of the force in 1/s**2", start=d_freq) + self.function = function self._f = Variable( self, "f", @@ -57,10 +66,13 @@ def __init__( ) def do_step(self, current_time: float, step_size: float): - self.f = self.func(current_time) + self.f = self.func(current_time+step_size) return True # very important! def exit_initialization_mode(self): """Set internal state after initial variables are set.""" - self.func = partial(func, ampl=self.ampl, omega=2 * pi * self.freq) # type: ignore[reportAttributeAccessIssue] - logger.info(f"Initial settings: ampl={self.ampl}, freq={self.freq}") # type: ignore[reportAttributeAccessIssue] + self.func = partial(self.function, + ampl=self.ampl, + omega=2 * pi * self.freq, + d_omega= 0.0 if self.d_freq == 0.0 else 2 * pi * self.d_freq) + logger.info(f"Initial settings: ampl={self.ampl}, freq={self.freq}, d_freq={self.d_freq}") diff --git a/examples/oscillator.py b/examples/oscillator.py index 6e9fd6c..6ed2b11 100644 --- a/examples/oscillator.py +++ b/examples/oscillator.py @@ -33,6 +33,7 @@ def __init__( c: tuple[float, float, float] | tuple[str, str, str] = (0.0, 0.0, 0.0), m: float = 1.0, tolerance: float = 1e-5, + f_func: Callable|None = None ): self.k = np.array(k, float) self.c = np.array(c, float) @@ -43,11 +44,15 @@ def __init__( self.f = np.array((0, 0, 0), float) # standard ODE matrix (homogeneous system): self.ode = [np.array(((-self.c[i] / self.m, -self.k[i] / self.m), (1, 0)), float) for i in range(3)] + self.f_func = f_func def ode_func(self, t: float, y: np.ndarray, i: int, f: float) -> np.ndarray: res = self.ode[i].dot(y) - if f != 0: - res += np.array((f, 0), float) + if self.f_func is None: + if f != 0: + res += np.array((f, 0), float) + elif i==2: # only implemented for z + res += np.array((self.f_func(t)[i], 0), float) return res def do_step(self, current_time: float, step_size: int | float) -> bool: @@ -56,13 +61,13 @@ def do_step(self, current_time: float, step_size: int | float) -> bool: We implement a very simplistic algoritm based on difference calculus. """ for i in range(3): # this is a 3D oscillator - if self.x[i] != 0 or self.v[i] != 0 or self.f[i] != 0: + if self.x[i] != 0 or self.v[i] != 0 or self.f[i] != 0 or self.f_func is not None: y0 = np.array([self.v[i], self.x[i]], float) sol = integrate.solve_ivp( fun=self.ode_func, t_span=[current_time, current_time + step_size], y0=y0, - args=(i, self.f[i]), # dimension and force as extra arguments to fun + args=(i, self.f[i]), # axis and force as extra arguments to fun atol=self.tolerance, ) self.x[i] = sol.y[1][-1] diff --git a/pyproject.toml b/pyproject.toml index 259ec83..f3b969f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,9 @@ packages = [ "examples", ] +[tool.hatch.metadata] +allow-direct-references = true + [project] name = "component-model" version = "0.1.0" @@ -63,19 +66,17 @@ dependencies = [ "flexparser<0.4", "scipy>=1.15.1", "plotly>=6.0.1", - "libcosimpy < 0.0.3.post1", - "thonny>=4.1.7", + "libcosimpy@git+https://github.com/open-simulation-platform/libcosimpy@cosimc-loading-fix", ] [project.optional-dependencies] -modelTest = [ - "fmpy==0.3.21", # version 0.3.22 does so far (25.3.25) not workwhen installing through pip - "matplotlib>=3.9.1", - "plotly>=6.0.1", -] -rest = [ - "docutils>=0.21", -] + modelTest = [ + "fmpy==0.3.21", # version 0.3.22 does so far (25.3.25) not workwhen installing through pip + "matplotlib>=3.9.1", + "plotly>=6.0.1", + ] + rest = ["docutils>=0.21"] + editor = [] [project.urls] Homepage = "https://github.com/dnv-opensource/component-model" diff --git a/src/component_model/model.py b/src/component_model/model.py index 5446aea..bf591a1 100644 --- a/src/component_model/model.py +++ b/src/component_model/model.py @@ -411,7 +411,7 @@ def build( project_files = [] project_files.append(Path(__file__).parents[0]) - # Make sure the dest path is of type Patch + # Make sure the dest path is of type Path dest = dest if isinstance(dest, Path) else Path(dest) with ( diff --git a/tests/test_bouncing_ball_3d.py b/tests/test_bouncing_ball_3d.py index cc7314e..a579df5 100644 --- a/tests/test_bouncing_ball_3d.py +++ b/tests/test_bouncing_ball_3d.py @@ -428,7 +428,6 @@ def get_status(sim): # sim.simulate_until(target_time=3e9) - def test_from_fmu(bouncing_ball_fmu): assert bouncing_ball_fmu.exists(), "FMU not found" model = model_from_fmu(bouncing_ball_fmu) diff --git a/tests/test_make_oscillator_fmu.py b/tests/test_make_oscillator_fmu.py deleted file mode 100644 index 5930423..0000000 --- a/tests/test_make_oscillator_fmu.py +++ /dev/null @@ -1,258 +0,0 @@ -import shutil -import sys -from collections.abc import Iterable -from pathlib import Path -from typing import Any - -import matplotlib.pyplot as plt -import numpy as np -import pytest -from fmpy.simulation import simulate_fmu -from fmpy.util import fmu_info, plot_result -from fmpy.validation import validate_fmu -from libcosimpy.CosimEnums import CosimExecutionState -from libcosimpy.CosimExecution import CosimExecution -from libcosimpy.CosimLogging import CosimLogLevel, log_output_level -from libcosimpy.CosimManipulator import CosimManipulator -from libcosimpy.CosimObserver import CosimObserver -from libcosimpy.CosimSlave import CosimLocalSlave - -from component_model.model import Model - - -def arrays_equal( - res: Iterable[Any], - expected: Iterable[Any], - eps: float = 1e-7, -): - len_res = len(list(res)) - len_exp = len(list(expected)) - if len_res != len_exp: - raise ValueError(f"Arrays of different lengths cannot be equal. Found {len_res} != {len_exp}") - for i, (x, y) in enumerate(zip(res, expected, strict=False)): - assert abs(x - y) < eps, f"Element {i} not nearly equal in {x}, {y}" - - -def do_show(time: list[float], z: list[float], v: list[float]): - fig, ax = plt.subplots() - _ = ax.plot(time, z, label="z-position") - _ = ax.plot(time, v, label="z-speed") - _ = ax.legend() - plt.show() - - -def force(t: float, ampl: float = 1.0, omega: float = 0.1): - return np.array((0, 0, ampl * np.sin(omega * t)), dtype=float) - - -@pytest.fixture(scope="session") -def oscillator_fmu(): - return _oscillator_fmu() - - -def _oscillator_fmu(): - """Make FMU and return .fmu file with path.""" - build_path = Path.cwd() - build_path.mkdir(exist_ok=True) - src = Path(__file__).parent.parent / "examples" / "oscillator_fmu.py" - fmu_path = Model.build( - script=str(src), - project_files=[src], - dest=build_path, - ) - return fmu_path - - -@pytest.fixture(scope="session") -def driver_fmu(): - return _driver_fmu() - - -def _driver_fmu(): - """Make FMU and return .fmu file with path.""" - build_path = Path.cwd() - build_path.mkdir(exist_ok=True) - src = Path(__file__).parent.parent / "examples" / "driving_force_fmu.py" - fmu_path = Model.build( - script=str(src), - project_files=[src], - dest=build_path, - ) - return fmu_path - - -@pytest.fixture(scope="session") -def system_structure(): - return _system_structure() - - -def _system_structure(): - """Make a OSP structure file and return the path""" - # path = make_osp_system_structure( - # name="ForcedOscillator", - # simulators={ - # "osc": {"source": "HarmonicOscillator.fmu", "stepSize": 0.01}, - # "drv": {"source": "DrivingForce.fmu", "stepSize": 0.01}, - # }, - # connections_variable=(("drv", "f[2]", "osc", "f[2]"),), - # version="0.1", - # start=0.0, - # base_step=0.01, - # algorithm="fixedStep", - # path=Path.cwd(), - # ) - shutil.copy(Path(__file__).parent.parent / "examples" / "ForcedOscillator.xml", Path.cwd()) - return Path.cwd() / "ForcedOscillator.xml" - - -def test_make_fmus( - oscillator_fmu: Path, - driver_fmu: Path, -): - info = fmu_info(filename=str(oscillator_fmu)) # this is a formatted string. Not easy to check - print(f"Info Oscillator: {info}") - val = validate_fmu(filename=str(oscillator_fmu)) - assert not len(val), f"Validation of of {oscillator_fmu.name} was not successful. Errors: {val}" - - info = fmu_info(filename=str(driver_fmu)) # this is a formatted string. Not easy to check - print(f"Info Driver: {info}") - val = validate_fmu(filename=str(driver_fmu)) - assert not len(val), f"Validation of of {oscillator_fmu.name} was not successful. Errors: {val}" - - -# def test_make_system_structure(system_structure: Path): -# assert Path(system_structure).exists(), "System structure not created" -# el = read_xml(Path(system_structure)) -# assert isinstance(el, ET.Element), f"ElementTree element expected. Found {el}" -# ns = el.tag.split("{")[1].split("}")[0] -# print("NS", ns, system_structure) -# for s in el.findall(".//{*}Simulator"): -# assert (Path(system_structure).parent / s.get("source", "??")).exists(), f"Component {s.get('name')} not found" -# for _con in el.findall(".//{*}VariableConnection"): -# for c in _con: -# assert c.attrib in ({"simulator": "drv", "name": "f[2]"}, {"simulator": "osc", "name": "f[2]"}) -# - - -def test_use_fmu(oscillator_fmu: Path, driver_fmu: Path, show: bool): - """Test single FMUs.""" - # sourcery skip: move-assign - result = simulate_fmu( - oscillator_fmu, - stop_time=50, - step_size=0.01, - validate=True, - solver="Euler", - debug_logging=True, - logger=print, # fmi_call_logger=print, - start_values={"x[2]": 1.0, "c[2]": 0.1}, - step_finished=None, # pyright: ignore[reportArgumentType] # (typing incorrect in fmpy) - fmu_instance=None, # pyright: ignore[reportArgumentType] # (typing incorrect in fmpy) - ) - if show: - plot_result(result) - - -def test_run_osp(oscillator_fmu: Path, driver_fmu: Path): - # sourcery skip: extract-duplicate-method - sim = CosimExecution.from_step_size(step_size=1e8) # empty execution object with fixed time step in nanos - osc = CosimLocalSlave(fmu_path=str(oscillator_fmu), instance_name="osc") - _osc = sim.add_local_slave(osc) - assert _osc == 0, f"local slave number {_osc}" - reference_dict = {var_ref.name.decode(): var_ref.reference for var_ref in sim.slave_variables(_osc)} - - dri = CosimLocalSlave(fmu_path=str(driver_fmu), instance_name="dri") - _dri = sim.add_local_slave(dri) - assert _dri == 1, f"local slave number {_dri}" - - # Set initial values - sim.real_initial_value(slave_index=_osc, variable_reference=reference_dict["x[2]"], value=1.0) - sim.real_initial_value(slave_index=_osc, variable_reference=reference_dict["c[2]"], value=0.1) - - sim_status = sim.status() - assert sim_status.current_time == 0 - assert CosimExecutionState(sim_status.state) == CosimExecutionState.STOPPED - - # Simulate for 1 second - _ = sim.simulate_until(target_time=15e9) - - -@pytest.mark.skipif(sys.platform.startswith("linux"), reason="HarmonicOsciallatorFMU.fmu throws an error on Linux") -def test_run_osp_system_structure(system_structure: Path, show: bool): - "Run an OSP simulation in the same way as the SimulatorInterface of sim-explorer is implemented" - log_output_level(CosimLogLevel.TRACE) - print("STRUCTURE", system_structure) - simulator = CosimExecution.from_osp_config_file(str(system_structure)) - sim_status = simulator.status() - assert sim_status.current_time == 0 - assert CosimExecutionState(sim_status.state) == CosimExecutionState.STOPPED - comps = [] - for comp in list(simulator.slave_infos()): - name = comp.name.decode() - comps.append(name) - assert comps == ["osc", "drv"] - variables = {} - for idx in range(simulator.num_slave_variables(0)): - struct = simulator.slave_variables(0)[idx] - variables[struct.name.decode()] = { - "reference": struct.reference, - "type": struct.type, - "causality": struct.causality, - "variability": struct.variability, - } - - for idx in range(simulator.num_slave_variables(1)): - struct = simulator.slave_variables(1)[idx] - variables |= { - struct.name.decode(): { - "reference": struct.reference, - "type": struct.type, - "causality": struct.causality, - "variability": struct.variability, - } - } - assert variables["c[2]"]["type"] == 0 - assert variables["c[2]"]["causality"] == 1 - assert variables["c[2]"]["variability"] == 1 - - assert variables["x[2]"]["type"] == 0 - assert variables["x[2]"]["causality"] == 2 - assert variables["x[2]"]["variability"] == 4 - - assert variables["v[2]"]["type"] == 0 - assert variables["v[2]"]["causality"] == 2 - assert variables["v[2]"]["variability"] == 4 - - # Instantiate a suitable observer for collecting results. - # Instantiate a suitable manipulator for changing variables. - manipulator = CosimManipulator.create_override() - simulator.add_manipulator(manipulator=manipulator) - simulator.real_initial_value(slave_index=0, variable_reference=5, value=0.5) # c[2] - simulator.real_initial_value(slave_index=0, variable_reference=9, value=1.0) # x[2] - observer = CosimObserver.create_last_value() - simulator.add_observer(observer=observer) - times = [] - pos = [] - speed = [] - for step in range(1, 1000): - time = step * 0.01 - _ = simulator.simulate_until(step * 1e8) - values = observer.last_real_values(slave_index=0, variable_references=[9, 12]) # x[2], v[2] - times.append(time) - pos.append(values[0]) - speed.append(values[1]) - if show: - do_show(time=times, z=pos, v=speed) - - -if __name__ == "__main__": - retcode = 0 # pytest.main(args=["-rA", "-v", __file__, "--show", "True"]) - assert retcode == 0, f"Non-zero return code {retcode}" - # import os - - # os.chdir(Path(__file__).parent.absolute() / "test_working_directory") - # test_make_fmus(_oscillator_fmu(), _driver_fmu()) - # test_make_system_structure( _system_structure()) - # test_use_fmu(_oscillator_fmu(), _driver_fmu(), show=True) - # test_run_osp(_oscillator_fmu(), _driver_fmu()) - test_run_osp_system_structure(_system_structure(), show=True) diff --git a/tests/test_make_time_table.py b/tests/test_make_time_table.py deleted file mode 100644 index c37c590..0000000 --- a/tests/test_make_time_table.py +++ /dev/null @@ -1,283 +0,0 @@ -import shutil -import xml.etree.ElementTree as ET # noqa: N817 -from pathlib import Path -from typing import Any, Iterable -from zipfile import ZipFile - -import numpy as np -import pytest -from fmpy.simulation import simulate_fmu # type: ignore -from fmpy.util import fmu_info, plot_result # type: ignore -from fmpy.validation import validate_fmu # type: ignore -from libcosimpy.CosimEnums import CosimExecutionState -from libcosimpy.CosimExecution import CosimExecution -from libcosimpy.CosimLogging import CosimLogLevel, log_output_level -from libcosimpy.CosimManipulator import CosimManipulator # type: ignore -from libcosimpy.CosimObserver import CosimObserver # type: ignore -from libcosimpy.CosimSlave import CosimLocalSlave -from pythonfmu.enums import Fmi2Causality as Causality -from pythonfmu.enums import Fmi2Variability as Variability - -from component_model.model import Model - - -def arrays_equal( - res: Iterable[Any], - expected: Iterable[Any], - eps: float = 1e-7, -): - len_res = len(list(res)) - len_exp = len(list(expected)) - if len_res != len_exp: - raise ValueError(f"Arrays of different lengths cannot be equal. Found {len_res} != {len_exp}") - for i, (x, y) in enumerate(zip(res, expected, strict=False)): - assert abs(x - y) < eps, f"Element {i} not nearly equal in {x}, {y}" - - -@pytest.fixture(scope="session") -def time_table_fmu(): - return _time_table_fmu() - - -def _time_table_fmu(): - """Make FMU and return .fmu file with path.""" - build_path = Path.cwd() - build_path.mkdir(exist_ok=True) - fmu_path = Model.build( - script=str(Path(__file__).parent.parent / "examples" / "time_table_fmu.py"), - project_files=[Path(__file__).parent.parent / "examples" / "time_table.py"], - dest=build_path, - ) - return fmu_path - - -@pytest.fixture(scope="session") -def time_table_system_structure(time_table_fmu): - return _time_table_system_structure(time_table_fmu) - - -def _time_table_system_structure(time_table_fmu): - """Make a structure file and return the path""" - # path = make_osp_system_structure( - # name="TimeTableStructure", - # simulators={"tab": {"source": "TimeTableFMU.fmu", "stepSize": 0.1}}, # , "interpolate": 1}}, - # version="0.1", - # start=0.0, - # base_step=0.1, - # algorithm="fixedStep", - # path=Path.cwd(), - # ) - shutil.copy(Path(__file__).parent.parent / "examples" / "TimeTableStructure.xml", Path.cwd()) - return Path.cwd() / "TimeTableStructure.xml" - - -def test_time_table_fmu(): - from examples.time_table_fmu import TimeTableFMU - - tbl = TimeTableFMU(data=[[0, 0, 0], [1, 1, 1], [2, 2, 4], [3, 3, 9]], header=["exp1", "exp2"], interpolate=1) - assert tbl._cols == 2 - assert tbl._rows == 4 - assert tbl.header == ("exp1", "exp2") - assert tbl.interpolate == 1 - arrays_equal(tbl.times, (0, 1, 2, 3)) - arrays_equal(tbl.data[1], [1, 1]) - assert tbl._outs.causality == Causality.output - assert tbl._outs.variability == Variability.continuous - arrays_equal(tbl._outs.start, tbl.outs) - assert tbl._interpolate.causality == Causality.parameter - assert tbl._interpolate.variability == Variability.fixed - assert tbl._interpolate.start == (tbl.interpolate,) - - -def _in_interval(x: float, x0: float, x1: float): - return x0 <= x <= x1 or x1 <= x <= x0 - - -def _linear(t: float, tt: tuple | list, xx: tuple | list): - if t <= tt[-1]: - return np.interp([t], tt, xx)[0] - else: - return xx[-1] + (t - tt[-1]) * (xx[-1] - xx[-2]) / (tt[-1] - tt[-2]) - - -def _to_et(file: str, sub: str = "modelDescription.xml"): - with ZipFile(file) as zp: - xml = zp.read(sub) - return ET.fromstring(xml) - - -def test_make_time_table(time_table_fmu): - info = fmu_info(time_table_fmu) # this is a formatted string. Not easy to check - print(f"Info: {info}") - et = _to_et(str(time_table_fmu)) - assert et.attrib["fmiVersion"] == "2.0", "FMI Version" - # similarly other critical issues of the modelDescription can be checked - assert et.attrib["variableNamingConvention"] == "structured", "Variable naming convention. => use [i] for arrays" - # print(et.attrib) - val = validate_fmu(str(time_table_fmu)) - assert not len(val), ( - f"Validation of the modelDescription of {time_table_fmu.name} was not successful. Errors: {val}" - ) - - -def test_use_fmu(time_table_fmu, show: bool = False): - """Use the FMU running it on fmpy using various interpolate settings.""" - print(fmu_info(time_table_fmu)) - _t = np.linspace(0, 10, 101) - for ipol in range(4): - result = simulate_fmu( # type: ignore[reportArgumentType] - time_table_fmu, - stop_time=10.0, - step_size=0.1, - validate=True, - solver="Euler", - debug_logging=True, - logger=print, # fmi_call_logger=print, - start_values={"interpolate": ipol}, - ) - if show: - plot_result(result) - for i, t in enumerate(_t): - assert result[i][0] == t, f"Wrong time picked: {t} != {result[i][0]}" - if ipol >= 0: - pass - elif ipol == 0: - assert result[i][1] == 1.0, f"Result for {ipol}, time={t}: {result[i][1]} != 1.0" - elif ipol == 1: - assert abs(result[i][2] - t) < 1e-10, f"Result for {ipol}, time={t}: {result[i][1]} != {i}" - elif ipol == 2: - assert abs(result[i][3] - t**2) < 1e-10, f"Result for {ipol}, time={t}: {result[i][1]} != {i**2}" - - -def test_run_osp(time_table_fmu): - log_output_level(CosimLogLevel.DEBUG) - sim = CosimExecution.from_step_size(step_size=1e8) # empty execution object with fixed time step in nanos - st = CosimLocalSlave(fmu_path=str(time_table_fmu), instance_name="st") - - ist = sim.add_local_slave(st) - assert ist == 0, f"local slave number {ist}" - - reference_dict = {var_ref.name.decode(): var_ref.reference for var_ref in sim.slave_variables(ist)} - - # Set initial values - sim.boolean_initial_value(ist, reference_dict["interpolate"], True) - - sim_status = sim.status() - assert sim_status.current_time == 0 - assert CosimExecutionState(sim_status.state) == CosimExecutionState.STOPPED - - # Simulate for 1 second - sim.simulate_until(target_time=15e9) - - -def test_check_osp_system_structure(time_table_system_structure): - "Instantiate OSP from system structure" - log_output_level(CosimLogLevel.DEBUG) - simulator = CosimExecution.from_osp_config_file(str(time_table_system_structure)) - comps = [] - for comp in list(simulator.slave_infos()): - name = comp.name.decode() - comps.append(name) - assert comps == ["tab"], f"Components: {comps}" - variables = {} - for idx in range(simulator.num_slave_variables(0)): - struct = simulator.slave_variables(0)[idx] - variables.update( - { - struct.name.decode(): { - "reference": struct.reference, - "type": struct.type, - "causality": struct.causality, - "variability": struct.variability, - } - } - ) - assert variables["outs[0]"] == {"reference": 1, "type": 0, "causality": 2, "variability": 4} # similar: [1],[2] - assert variables["interpolate"] == {"reference": 0, "type": 1, "causality": 1, "variability": 1} - - -def test_run_osp_system_structure(time_table_system_structure): - "Run an OSP simulation in the same way as the SystemInterface of sim-explorer is implemented" - log_output_level(CosimLogLevel.TRACE) - for ipol in range(4): - simulator = CosimExecution.from_osp_config_file(str(time_table_system_structure)) # reset - simulator.integer_initial_value(0, 0, ipol) # set 'interpolate' - # manipulator and obeserver - manipulator = CosimManipulator.create_override() - simulator.add_manipulator(manipulator=manipulator) - observer = CosimObserver.create_last_value() - simulator.add_observer(observer=observer) - for time in np.linspace(0.1, 10, 100): - simulator.simulate_until(time * 1e9) - if time == 0.1: - assert observer.last_integer_values(0, [0]) == [ipol], ( - f"iPol {observer.last_integer_values(0, [0])} != {ipol}" - ) - if ipol == 0: - _x = observer.last_real_values(0, [1])[0] - assert _x == 1.0, f"Result for {ipol}: {_x} != 1.0" - elif ipol == 1: - _x = observer.last_real_values(0, [2])[0] - assert abs(_x - time) < 1e-10, f"Result for {ipol}: {_x} != {time}" - elif ipol == 2: - _x = observer.last_real_values(0, [3])[0] - assert abs(_x - time**2) < 1e-10, f"Result for {ipol}: {_x} != {time**2}" - - -def test_make_with_new_data(): - """Test and example how keyword arguments of the Model class can be used (changed) when building FMU.""" - times = np.linspace(0, 2 * np.pi, 100) - data = list(zip(times, np.cos(times), np.sin(times), strict=False)) - build_path = Path.cwd() - build_path.mkdir(exist_ok=True) - _ = Model.build( - script=str(Path(__file__).parent.parent / "examples" / "time_table_fmu.py"), - project_files=[Path(__file__).parent.parent / "examples" / "time_table.py"], - dest=build_path, - newargs={ - "data": data, - "header": ("x", "y"), - "interpolate": 1, - "default_experiment": {"startTime": 0, "stopTime": 2 * np.pi, "stepSize": 0.1, "tolerance": 1e-5}, - }, - ) - - -@pytest.mark.skip(reason="Does so far not work within pytest, only stand-alone") -def test_use_with_new_data(show): - fmu_path = Path(__file__).parent / "test_working_directory" / "TimeTableFMU.fmu" - result = simulate_fmu( # type: ignore[reportArgumentType] - fmu_path, - stop_time=2 * np.pi, - step_size=0.1, - validate=True, - solver="Euler", - debug_logging=True, - logger=print, # fmi_call_logger=print, - start_values={"interpolate": 2}, - ) - if show: - plot_result(result) - time = 0.0 - for t, x, y in result: - assert abs(t - time) < 1e-10, f"Expected time {time}!={t} from data" - assert abs(1.0 - (x**2 + y**2)) < 1e-5, f"time {t}: x={x}, y={y}, x**2+y**2 = {x**2 + y**2}" - time += 0.1 - if time > 2 * np.pi: - time = 2 * np.pi - - -if __name__ == "__main__": - retcode = pytest.main(["-rA", "-v", __file__]) - assert retcode == 0, f"Non-zero return code {retcode}" - import os - - os.chdir(Path(__file__).parent.absolute() / "test_working_directory") - # test_time_table_fmu() - # test_make_time_table(_time_table_fmu()) - # test_use_fmu(_time_table_fmu(), show=True) - # test_run_osp(_time_table_fmu()) - # test_check_osp_system_structure(_time_table_system_structure(_time_table_fmu())) - # test_run_osp_system_structure(_time_table_system_structure(_time_table_fmu())) - # test_make_with_new_data() - # test_use_with_new_data(show=True) diff --git a/tests/test_oscillator.py b/tests/test_oscillator.py index 683704b..3ba12ff 100644 --- a/tests/test_oscillator.py +++ b/tests/test_oscillator.py @@ -3,6 +3,7 @@ import matplotlib.pyplot as plt import numpy as np +from pathlib import Path def arrays_equal(res: tuple[float, ...] | list[float], expected: tuple[float, ...] | list[float], eps=1e-7): @@ -26,8 +27,11 @@ def do_show(time: list, z: list, v: list, compare1: list | None = None, compare2 plt.show() -def force(t: float, ampl: float = 1.0, omega: float = 0.1): - return np.array((0, 0, ampl * sin(omega * t)), float) +def force(t: float, ampl: float = 1.0, omega: float = 0.1, d_omega: float = 0.0): + if d_omega == 0.0: + return np.array((0, 0, ampl * sin(omega * t)), float) # fixed frequency + else: + return np.array((0, 0, ampl * sin((omega + d_omega*t) * t)), float) # frequency sweep def forced_oscillator( @@ -114,6 +118,45 @@ def run_oscillation_z( return (osc, times, z, v) +def sweep_oscillation_z( + k: float, + c: float, + m: float, + ampl: float, + d_omega: float, + x0: float = 1.0, + v0: float = 0.0, + dt: float = 0.01, + end: float = 30.0, + tol: float = 1e-3, +): + """Run the oscillator with the given settings + with linearly increasing force frequency + for the given time (only z-direction activated) + and return the oscillator object and the time series for z-position and z-velocity.""" + + from examples.oscillator import Oscillator + f_func = f_func=partial(force, ampl=ampl, omega=0.0, d_omega=d_omega) + osc = Oscillator(k=(1.0, 1.0, k), + c=(0.0, 0.0, c), + m=m, + tolerance=tol, + f_func = f_func + ) + osc.x[2] = x0 # set initial z value + osc.v[2] = v0 # set initial z-speed + times, z, v, f = [], [], [], [] + time = 0.0 + while time < end: + times.append(time) + z.append(osc.x[2]) + v.append(osc.v[2]) + osc.do_step(time, dt) + f.append(f_func(time)[2]) + time += dt + + return (osc, times, z, v, f) + def test_oscillator_class(show): """Test the Oscillator class in isolation. @@ -221,11 +264,70 @@ def area(x: list[float], y: list[float]): assert abs(y[-1] - 4.0) < 1e-12, f"Found {y[-1]}" osc, x, y = run_2d(x0=(1.0, 0.0, 0.0), v0=(0.0, 1.0, 0.0), k=(1.0, 1.0 / 15.8, 0), end=20 * np.pi) if show: - show_2d(x, y) + show_2d(x,y) +def test_sweep_oscillator(show: bool = True): + """A forced oscillator where the force frequency is changed linearly as d_omega*time. + The test demonstrates that a monolithic simulation provides accurate results in all ranges of the force frequency. + Co-simulating the oscillator and the force, this does not work. + """ + osc, times0, z0, v0, f0 = sweep_oscillation_z( k=1.0, + c=0.1, + m=1.0, + ampl=1.0, + d_omega=0.1, + x0=0.0, + v0=0.0, + dt=0.1, # 'ground truth', small dt + end=100.0, + tol=1e-3 + ) + with open(Path.cwd() / "oscillator_sweep0.dat", 'w') as fp: + for i in range(len(times0)): + fp.write( f"{times0[i]}\t{z0[i]}\t{v0[i]}\t{f0[i]}\n") + + if show: + freq = [0.1*t/2/np.pi for t in times0] + fig, ax = plt.subplots() + ax.plot(freq, z0, label="z0(t)") + ax.plot(freq, v0, label="v0(t)") + ax.plot(freq, f0, label="F0(t)") + ax.legend() + plt.show() + + osc, times, z, v, f = sweep_oscillation_z( k=1.0, + c=0.1, + m=1.0, + ampl=1.0, + d_omega=0.1, + x0=0.0, + v0=0.0, + dt=1, # dt similar to resonance frequency + end=100.0, + tol=1e-3 + ) + i0 = 0 + for i in range(len(times)): # demonstrate that the results are accurate, even if dt is large + t = times[i] + while abs(times0[i0]-t) > 1e-10: + i0 += 1 + assert times0[i0] - t < 0.1, f"Time entry for time {t} not found in times0" + + assert abs(z0[i0] - z[i]) < 2e-2, f"Time {t}. Found {z0[i0]} != {z[i]}" + assert abs(v0[i0] - v[i]) < 2e-2, f"Time {t}. Found {v0[i0]} != {v[i]}" + + if show: + fig, ax = plt.subplots() + ax.plot(times0, z0, label="z0(t)") + ax.plot(times, z, label="z(t)") + ax.legend() + plt.show() if __name__ == "__main__": retcode = 0 # pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__]) assert retcode == 0, f"Non-zero return code {retcode}" - test_oscillator_class(show=True) + import os + os.chdir(Path(__file__).parent.absolute() / "test_working_directory") + # test_oscillator_class(show=True) # test_2d(show=True) + test_sweep_oscillator() diff --git a/tests/test_structured_variables.py b/tests/test_structured_variables.py deleted file mode 100644 index 44d95bd..0000000 --- a/tests/test_structured_variables.py +++ /dev/null @@ -1,156 +0,0 @@ -import logging -from math import asin, pi -from pathlib import Path - -import matplotlib.pyplot as plt -import numpy as np -import pytest -from examples.axle import Axle -from fmpy.simulation import simulate_fmu -from fmpy.util import fmu_info -from fmpy.validation import validate_fmu - -from component_model.model import Model - -logger = logging.getLogger(__name__) -logging.basicConfig(level=logging.INFO) - - -def check_pos( - time: float, bi: list[float], pos0: list[np.ndarray], pos1: list[np.ndarray], a: float, eps: float = 1e-3 -): - """Check the new position with respect to the line integral and axle length.""" - a0 = np.linalg.norm(pos0[0] - pos0[1]) - assert abs(a0 - a) < eps, f"Axle length {a0} wrong at start. Expected: {a}" - a1 = np.linalg.norm(pos1[0] - pos1[1]) - assert abs(a1 - a) < eps, f"Axle length {a1}wrong at end. Expected: {a}" - s0_dot_s1 = np.dot(pos1[0] - pos0[0], pos1[1] - pos0[1]) - si = [np.linalg.norm(pos1[i] - pos0[i]) for i in range(2)] # secant vectors for distance traveled - if (si[0] < 0 and s0_dot_s1 > 0) or (si[0] > 0 and s0_dot_s1 < 0): - si[0] = -si[0] - if abs(si[0] - si[1]) < 1e-10: # straight line (infinite radius) - for i in range(2): - assert abs(si[i] - abs(bi[i])) < eps, f"Length @{time}. {abs(bi[i])} != {si[i]}" - else: - ri = [a * si[i] / (si[1] - si[0]) for i in range(2)] - if abs(si[0]) > abs(si[1]): # ensure accuracy - alpha = asin(si[0] / 2 / ri[0]) * 2 - else: - alpha = asin(si[1] / 2 / ri[1]) * 2 - _bi = [alpha * ri[i] for i in range(2)] - for i in range(2): - assert abs(abs(_bi[i]) - abs(bi[i])) < eps, f"Arc length @{time}. {abs(bi[i])} != {abs(_bi[i])}" - - -def test_axle_class(show: bool): - def do_drive(axle: Axle, t: float, dt: float): - """Drive one time step and do checks.""" - pos0 = [np.copy(axle.wheels[i].pos) for i in range(2)] - axle.drive(t, dt) - check_pos( - t, - [pi * axle.wheels[i].diameter * axle.wheels[i].motor.rpm * dt for i in range(2)], - pos0, - [axle.wheels[i].pos for i in range(2)], - axle.a, - ) - - axle = Axle() - axle.init_drive() - # go straight - dt = 0.01 - for t in np.linspace(0.0, 1, int(1.0 / dt)): - do_drive(axle, t, dt) - _pos = (0.0, pi) - assert abs(axle.wheels[0].track[1][-1] - _pos[1]) < 1e-10 - - # turn left - axle.wheels[1].motor.rpm = -1.1 - for t in np.linspace(1.0, 2.0, int(1.0 / dt)): - do_drive(axle, t, dt) - - # turn right - axle.wheels[1].motor.rpm = -0.9 - for t in np.linspace(2.0, 3.0, int(1.0 / dt)): - do_drive(axle, t, dt) - - # turn motors against each other (pirouette) - axle.wheels[1].motor.rpm = 1.0 - for t in np.linspace(3.0, 4.0, int(1.0 / dt)): - do_drive(axle, t, dt) - - axle.wheels[1].motor.rpm = -1.0 - axle.wheels[0].motor.acc = 0.1 # accelerate with both motor0 - for t in np.linspace(4.0, 10.0, int(6.0 / dt)): - axle.drive(t, dt) - # print("TRACK", axle.wheels[0]._track, axle.wheels[1]._track) - if show: - axle.show() - print("Positions", axle.wheels[0].pos, axle.wheels[1].pos) - - -@pytest.fixture(scope="session") -def axle_fmu(): - return _axle_fmu() - - -def _axle_fmu(): - """Make FMU and return .fmu file with path.""" - build_path = Path.cwd() - build_path.mkdir(exist_ok=True) - fmu_path = Model.build( - script=str(Path(__file__).parent.parent / "examples" / "axle_fmu.py"), - project_files=[Path(__file__).parent.parent / "examples" / "axle.py"], - dest=build_path, - ) - return fmu_path - - -def test_make_fmu(axle_fmu: Path, show: bool): - info = fmu_info(filename=str(axle_fmu)) # this is a formatted string. Not easy to check - if show: - print(f"Info Oscillator: {info}") - val = validate_fmu(filename=str(axle_fmu)) - assert not len(val), f"Validation of of {axle_fmu.name} was not successful. Errors: {val}" - - -def test_use_fmu(axle_fmu: Path, show: bool): - """Test single FMUs.""" - # sourcery skip: move-assign - result = simulate_fmu( - axle_fmu, - stop_time=10.0, - step_size=0.01, - validate=True, - solver="Euler", - debug_logging=True, - logger=print, # fmi_call_logger=print, - start_values={ - "wheels[0].motor.rpm": -1.0, - "wheels[1].motor.rpm": -1.0, - "der(wheels[1].motor.rpm)": 0.5, - }, - step_finished=None, # pyright: ignore[reportArgumentType] # (typing incorrect in fmpy) - fmu_instance=None, # pyright: ignore[reportArgumentType] # (typing incorrect in fmpy) - ) - if show: - data: list[list[float]] = [[], [], [], []] - for row in result: - for i in range(4): - data[i].append(row[i + 1]) - fig, ax = plt.subplots() - ax.plot(data[0], data[1], label="1") - ax.plot(data[2], data[3], label="2") - ax.legend() - plt.show() - - -if __name__ == "__main__": - retcode = pytest.main(["-rP -s -v", __file__]) - assert retcode == 0, f"Return code {retcode}" - import os - - os.chdir(Path(__file__).parent.absolute() / "test_working_directory") - # test_axle_class(show=True) - # test_make_fmu(_axle_fmu(), show=False)#True) - # test_use_fmu(_axle_fmu(), show=True) From 1fc34f74523ece4573d08dac3e968300b0fe6200 Mon Sep 17 00:00:00 2001 From: Eisinger Date: Thu, 5 Jun 2025 15:40:10 +0200 Subject: [PATCH 07/29] Added a few missing (renamed) files --- tests/test_axle_fmu.py | 156 +++++++++++++++++ tests/test_oscillator_fmu.py | 321 +++++++++++++++++++++++++++++++++++ tests/test_time_table_fmu.py | 283 ++++++++++++++++++++++++++++++ 3 files changed, 760 insertions(+) create mode 100644 tests/test_axle_fmu.py create mode 100644 tests/test_oscillator_fmu.py create mode 100644 tests/test_time_table_fmu.py diff --git a/tests/test_axle_fmu.py b/tests/test_axle_fmu.py new file mode 100644 index 0000000..02bf20f --- /dev/null +++ b/tests/test_axle_fmu.py @@ -0,0 +1,156 @@ +import logging +from math import asin, pi +from pathlib import Path + +import matplotlib.pyplot as plt +import numpy as np +import pytest +from examples.axle import Axle +from fmpy.simulation import simulate_fmu +from fmpy.util import fmu_info +from fmpy.validation import validate_fmu + +from component_model.model import Model + +logger = logging.getLogger(__name__) +logging.basicConfig(level=logging.INFO) + + +def check_pos( + time: float, bi: list[float], pos0: list[np.ndarray], pos1: list[np.ndarray], a: float, eps: float = 1e-3 +): + """Check the new position with respect to the line integral and axle length.""" + a0 = np.linalg.norm(pos0[0] - pos0[1]) + assert abs(a0 - a) < eps, f"Axle length {a0} wrong at start. Expected: {a}" + a1 = np.linalg.norm(pos1[0] - pos1[1]) + assert abs(a1 - a) < eps, f"Axle length {a1}wrong at end. Expected: {a}" + s0_dot_s1 = np.dot(pos1[0] - pos0[0], pos1[1] - pos0[1]) + si = [np.linalg.norm(pos1[i] - pos0[i]) for i in range(2)] # secant vectors for distance traveled + if (si[0] < 0 and s0_dot_s1 > 0) or (si[0] > 0 and s0_dot_s1 < 0): + si[0] = -si[0] + if abs(si[0] - si[1]) < 1e-10: # straight line (infinite radius) + for i in range(2): + assert abs(si[i] - abs(bi[i])) < eps, f"Length @{time}. {abs(bi[i])} != {si[i]}" + else: + ri = [a * si[i] / (si[1] - si[0]) for i in range(2)] + if abs(si[0]) > abs(si[1]): # ensure accuracy + alpha = asin(si[0] / 2 / ri[0]) * 2 + else: + alpha = asin(si[1] / 2 / ri[1]) * 2 + _bi = [alpha * ri[i] for i in range(2)] + for i in range(2): + assert abs(abs(_bi[i]) - abs(bi[i])) < eps, f"Arc length @{time}. {abs(bi[i])} != {abs(_bi[i])}" + + +def test_axle_class(show: bool): + def do_drive(axle: Axle, t: float, dt: float): + """Drive one time step and do checks.""" + pos0 = [np.copy(axle.wheels[i].pos) for i in range(2)] + axle.drive(t, dt) + check_pos( + t, + [pi * axle.wheels[i].diameter * axle.wheels[i].motor.rpm * dt for i in range(2)], + pos0, + [axle.wheels[i].pos for i in range(2)], + axle.a, + ) + + axle = Axle() + axle.init_drive() + # go straight + dt = 0.01 + for t in np.linspace(0.0, 1, int(1.0 / dt)): + do_drive(axle, t, dt) + _pos = (0.0, pi) + assert abs(axle.wheels[0].track[1][-1] - _pos[1]) < 1e-10 + + # turn left + axle.wheels[1].motor.rpm = -1.1 + for t in np.linspace(1.0, 2.0, int(1.0 / dt)): + do_drive(axle, t, dt) + + # turn right + axle.wheels[1].motor.rpm = -0.9 + for t in np.linspace(2.0, 3.0, int(1.0 / dt)): + do_drive(axle, t, dt) + + # turn motors against each other (pirouette) + axle.wheels[1].motor.rpm = 1.0 + for t in np.linspace(3.0, 4.0, int(1.0 / dt)): + do_drive(axle, t, dt) + + axle.wheels[1].motor.rpm = -1.0 + axle.wheels[0].motor.acc = 0.1 # accelerate with both motor0 + for t in np.linspace(4.0, 10.0, int(6.0 / dt)): + axle.drive(t, dt) + # print("TRACK", axle.wheels[0]._track, axle.wheels[1]._track) + if show: + axle.show() + print("Positions", axle.wheels[0].pos, axle.wheels[1].pos) + + +@pytest.fixture(scope="session") +def axle_fmu(): + return _axle_fmu() + + +def _axle_fmu(): + """Make FMU and return .fmu file with path.""" + build_path = Path.cwd() + build_path.mkdir(exist_ok=True) + fmu_path = Model.build( + script=str(Path(__file__).parent.parent / "examples" / "axle_fmu.py"), + project_files=[Path(__file__).parent.parent / "examples" / "axle.py"], + dest=build_path, + ) + return fmu_path + + +def test_make_fmu(axle_fmu: Path, show: bool): + info = fmu_info(filename=str(axle_fmu)) # this is a formatted string. Not easy to check + if show: + print(f"Info Oscillator: {info}") + val = validate_fmu(filename=str(axle_fmu)) + assert not len(val), f"Validation of of {axle_fmu.name} was not successful. Errors: {val}" + + +def test_use_fmu(axle_fmu: Path, show: bool): + """Test single FMUs.""" + # sourcery skip: move-assign + result = simulate_fmu( + axle_fmu, + stop_time=10.0, + step_size=0.01, + validate=True, + solver="Euler", + debug_logging=True, + logger=print, # fmi_call_logger=print, + start_values={ + "wheels[0].motor.rpm": -1.0, + "wheels[1].motor.rpm": -1.0, + "der(wheels[1].motor.rpm)": 0.5, + }, + step_finished=None, # pyright: ignore[reportArgumentType] # (typing incorrect in fmpy) + fmu_instance=None, # pyright: ignore[reportArgumentType] # (typing incorrect in fmpy) + ) + if show: + data: list[list[float]] = [[], [], [], []] + for row in result: + for i in range(4): + data[i].append(row[i + 1]) + fig, ax = plt.subplots() + ax.plot(data[0], data[1], label="1") + ax.plot(data[2], data[3], label="2") + ax.legend() + plt.show() + + +if __name__ == "__main__": + retcode = 0#pytest.main(["-rP -s -v", __file__]) + assert retcode == 0, f"Return code {retcode}" + import os + + os.chdir(Path(__file__).parent.absolute() / "test_working_directory") + # test_axle_class(show=True) + # test_make_fmu(_axle_fmu(), show=False)#True) + test_use_fmu(_axle_fmu(), show=True) diff --git a/tests/test_oscillator_fmu.py b/tests/test_oscillator_fmu.py new file mode 100644 index 0000000..03086a6 --- /dev/null +++ b/tests/test_oscillator_fmu.py @@ -0,0 +1,321 @@ +import shutil +import sys +from collections.abc import Iterable +from pathlib import Path +from typing import Any + +import matplotlib.pyplot as plt +import numpy as np +import pytest +from fmpy.simulation import simulate_fmu +from fmpy.util import fmu_info, plot_result +from fmpy.validation import validate_fmu +from libcosimpy.CosimEnums import CosimExecutionState +from libcosimpy.CosimExecution import CosimExecution +from libcosimpy.CosimLogging import CosimLogLevel, log_output_level +from libcosimpy.CosimManipulator import CosimManipulator +from libcosimpy.CosimObserver import CosimObserver +from libcosimpy.CosimSlave import CosimLocalSlave + +from component_model.model import Model +from component_model.utils.xml import read_xml +import xml.etree.ElementTree as ET # noqa: N817 + +def system_structure_change( structure_file: Path, tag: str, what: str, newval: str): + def register_all_namespaces(filename): + namespaces = dict([node for _, node in ET.iterparse(filename, events=['start-ns'])]) + for ns in namespaces: + ET.register_namespace(ns, namespaces[ns]) + return namespaces + + nss = register_all_namespaces( structure_file) + el = read_xml(structure_file) + elements = el.findall(f"ns:{tag}", {'ns' : nss['']}) + for e in elements: + if what=='text': + e.text = newval + else: # assume attribute name + e.attrib[what] = newval + ET.ElementTree(el).write(structure_file.name, encoding="utf-8") + + +def arrays_equal( + res: Iterable[Any], + expected: Iterable[Any], + eps: float = 1e-7, +): + len_res = len(list(res)) + len_exp = len(list(expected)) + if len_res != len_exp: + raise ValueError(f"Arrays of different lengths cannot be equal. Found {len_res} != {len_exp}") + for i, (x, y) in enumerate(zip(res, expected, strict=False)): + assert abs(x - y) < eps, f"Element {i} not nearly equal in {x}, {y}" + +def do_show( traces: dict[str, tuple[list[float],list[float]]]): + fig, ax = plt.subplots() + for label, trace in traces.items(): + _ = ax.plot(trace[0], trace[1], label=label) + _ = ax.legend() + plt.show() + + +# def force(t: float, ampl: float = 1.0, omega: float = 0.1): +# return np.array((0, 0, ampl * np.sin(omega * t)), dtype=float) + + +@pytest.fixture(scope="session") +def oscillator_fmu(): + return _oscillator_fmu() + + +def _oscillator_fmu(): + """Make FMU and return .fmu file with path.""" + build_path = Path.cwd() + build_path.mkdir(exist_ok=True) + src = Path(__file__).parent.parent / "examples" / "oscillator_fmu.py" + fmu_path = Model.build( + script=str(src), + project_files=[src], + dest=build_path, + ) + return fmu_path + +@pytest.fixture(scope="session") +def driver_fmu(): + return _driver_fmu() + + +def _driver_fmu(): + """Make FMU and return .fmu file with path.""" + build_path = Path.cwd() + build_path.mkdir(exist_ok=True) + src = Path(__file__).parent.parent / "examples" / "driving_force_fmu.py" + fmu_path = Model.build( + script=str(src), + project_files=[src], + dest=build_path + ) + return fmu_path + + +@pytest.fixture(scope="session") +def system_structure(): + return _system_structure() + + +def _system_structure(): + """Make a OSP structure file and return the path""" + shutil.copy(Path(__file__).parent.parent / "examples" / "ForcedOscillator.xml", Path.cwd()) + return Path.cwd() / "ForcedOscillator.xml" + +def test_make_fmus( + oscillator_fmu: Path, + driver_fmu: Path, +): + info = fmu_info(filename=str(oscillator_fmu)) # this is a formatted string. Not easy to check + print(f"Info Oscillator: {info}") + val = validate_fmu(filename=str(oscillator_fmu)) + assert not len(val), f"Validation of of {oscillator_fmu.name} was not successful. Errors: {val}" + + info = fmu_info(filename=str(driver_fmu)) # this is a formatted string. Not easy to check + print(f"Info Driver: {info}") + val = validate_fmu(filename=str(driver_fmu)) + assert not len(val), f"Validation of of {driver_fmu.name} was not successful. Errors: {val}" + + +# def test_make_system_structure(system_structure: Path): +# assert Path(system_structure).exists(), "System structure not created" +# el = read_xml(Path(system_structure)) +# assert isinstance(el, ET.Element), f"ElementTree element expected. Found {el}" +# ns = el.tag.split("{")[1].split("}")[0] +# print("NS", ns, system_structure) +# for s in el.findall(".//{*}Simulator"): +# assert (Path(system_structure).parent / s.get("source", "??")).exists(), f"Component {s.get('name')} not found" +# for _con in el.findall(".//{*}VariableConnection"): +# for c in _con: +# assert c.attrib in ({"simulator": "drv", "name": "f[2]"}, {"simulator": "osc", "name": "f[2]"}) +# + + +def test_use_fmu(oscillator_fmu: Path, driver_fmu: Path, show: bool): + """Test single FMUs.""" + # sourcery skip: move-assign + result = simulate_fmu( + oscillator_fmu, + stop_time=50, + step_size=0.01, + validate=True, + solver="Euler", + debug_logging=True, + logger=print, # fmi_call_logger=print, + start_values={"x[2]": 1.0, "c[2]": 0.1}, + step_finished=None, # pyright: ignore[reportArgumentType] # (typing incorrect in fmpy) + fmu_instance=None, # pyright: ignore[reportArgumentType] # (typing incorrect in fmpy) + ) + if show: + plot_result(result) + + +def test_run_osp(oscillator_fmu: Path, driver_fmu: Path): + # sourcery skip: extract-duplicate-method + sim = CosimExecution.from_step_size(step_size=1e8) # empty execution object with fixed time step in nanos + osc = CosimLocalSlave(fmu_path=str(oscillator_fmu), instance_name="osc") + _osc = sim.add_local_slave(osc) + assert _osc == 0, f"local slave number {_osc}" + reference_dict = {var_ref.name.decode(): var_ref.reference for var_ref in sim.slave_variables(_osc)} + + dri = CosimLocalSlave(fmu_path=str(driver_fmu), instance_name="dri") + _dri = sim.add_local_slave(dri) + assert _dri == 1, f"local slave number {_dri}" + + # Set initial values + sim.real_initial_value(slave_index=_osc, variable_reference=reference_dict["x[2]"], value=1.0) + sim.real_initial_value(slave_index=_osc, variable_reference=reference_dict["c[2]"], value=0.1) + + sim_status = sim.status() + assert sim_status.current_time == 0 + assert CosimExecutionState(sim_status.state) == CosimExecutionState.STOPPED + + # Simulate for 1 second + _ = sim.simulate_until(target_time=15e9) + + +@pytest.mark.skipif(sys.platform.startswith("linux"), reason="HarmonicOsciallatorFMU.fmu throws an error on Linux") +def test_run_osp_system_structure(system_structure: Path, show: bool): + "Run an OSP simulation in the same way as the SimulatorInterface of sim-explorer is implemented" + log_output_level(CosimLogLevel.TRACE) + print("STRUCTURE", system_structure) + try: + simulator = CosimExecution.from_osp_config_file(str(system_structure)) + except Exception as err: + print("ERR", err) + sim_status = simulator.status() + assert sim_status.current_time == 0 + assert CosimExecutionState(sim_status.state) == CosimExecutionState.STOPPED + comps = [] + for comp in list(simulator.slave_infos()): + name = comp.name.decode() + comps.append(name) + assert comps == ["osc", "drv"] + variables = {} + for idx in range(simulator.num_slave_variables(0)): + struct = simulator.slave_variables(0)[idx] + variables[struct.name.decode()] = { + "reference": struct.reference, + "type": struct.type, + "causality": struct.causality, + "variability": struct.variability, + } + + for idx in range(simulator.num_slave_variables(1)): + struct = simulator.slave_variables(1)[idx] + variables |= { + struct.name.decode(): { + "reference": struct.reference, + "type": struct.type, + "causality": struct.causality, + "variability": struct.variability, + } + } + assert variables["c[2]"]["type"] == 0 + assert variables["c[2]"]["causality"] == 1 + assert variables["c[2]"]["variability"] == 1 + + assert variables["x[2]"]["type"] == 0 + assert variables["x[2]"]["causality"] == 2 + assert variables["x[2]"]["variability"] == 4 + + assert variables["v[2]"]["type"] == 0 + assert variables["v[2]"]["causality"] == 2 + assert variables["v[2]"]["variability"] == 4 + + # Instantiate a suitable observer for collecting results. + # Instantiate a suitable manipulator for changing variables. + manipulator = CosimManipulator.create_override() + simulator.add_manipulator(manipulator=manipulator) + simulator.real_initial_value(slave_index=0, variable_reference=5, value=0.5) # c[2] + simulator.real_initial_value(slave_index=0, variable_reference=9, value=1.0) # x[2] + observer = CosimObserver.create_last_value() + simulator.add_observer(observer=observer) + times = [] + pos = [] + speed = [] + for step in range(1, 1000): + time = step * 0.01 + _ = simulator.simulate_until(step * 1e8) + values = observer.last_real_values(slave_index=0, variable_references=[9, 12]) # x[2], v[2] + times.append(time) + pos.append(values[0]) + speed.append(values[1]) + if show: + do_show(traces={'z-pos' : (times,pos), 'z-speed' : (times,speed)}) + +@pytest.mark.parameterize("alg", ['fixedStep', 'ecco']) +def test_run_osp_sweep(system_structure: Path, show: bool, alg:str): + "Run an OSP simulation of the oscillator and the force sweep as co-simulation." + + dt = 1.0 + t_end = 100.0 + + log_output_level(CosimLogLevel.TRACE) + system_structure_change(system_structure, "BaseStepSize", "text", str(dt)) + system_structure_change(system_structure, "Algorithm", "text", alg) + print(f"Running Algorithm {alg} on {system_structure}") + assert system_structure.exists(), f"File {system_structure} not found" + simulator = CosimExecution.from_osp_config_file(str(system_structure)) + print(get_last_error_message()) + sim_status = simulator.status() + #manipulator = CosimManipulator.create_override() + #simulator.add_manipulator(manipulator=manipulator) + simulator.real_initial_value(slave_index=0, variable_reference=2, value=1.0) # k[2] + simulator.real_initial_value(slave_index=0, variable_reference=5, value=0.1) # c[2] + simulator.real_initial_value(slave_index=0, variable_reference=6, value=1.0) # m + simulator.real_initial_value(slave_index=0, variable_reference=0, value=1.0) # ampl + simulator.real_initial_value(slave_index=1, variable_reference=1, value=0.0) # freq (start frequency) + simulator.real_initial_value(slave_index=1, variable_reference=2, value=0.1/2/np.pi) # d_freq + simulator.real_initial_value(slave_index=0, variable_reference=9, value=0.0) # x0[2] + simulator.real_initial_value(slave_index=0, variable_reference=12, value=0.0) # v0[2] + observer = CosimObserver.create_last_value() + simulator.add_observer(observer=observer) + times = [] + pos = [] + speed = [] + time = 0.0 + while time < t_end: + time += dt + times.append( time) + _ = simulator.simulate_until( int(time*1e9)) + values = observer.last_real_values(slave_index=0, variable_references=[9, 12]) # x[2], v[2] + pos.append(values[0]) + speed.append(values[1]) + if Path("oscillator_sweep0.dat").exists(): + times0, pos0, speed0, force0 = [], [], [], [] + with open("oscillator_sweep0.dat", 'r') as fp: + for line in fp: + t, p, v, f = line.split('\t') + times0.append(float(t)) + pos0.append(float(p)) + speed0.append(float(v)) + force0.append(float(f)) + if show: + freq0 = [0.1*t/2/np.pi for t in times0] + freq = [0.1*t/2/np.pi for t in times] + do_show({'z-pos0' : (freq0,pos0), 'z-pos' : (freq,pos)}) + + elif show: + do_show( {'z-pos' : (times,pos), 'z-speed' : (times,speed)}) + + +if __name__ == "__main__": + retcode = 0 #pytest.main(args=["-rA", "-v", __file__, "--show", "True"]) + assert retcode == 0, f"Non-zero return code {retcode}" + import os + + os.chdir(Path(__file__).parent.absolute() / "test_working_directory") + test_make_fmus(_oscillator_fmu(), _driver_fmu()) + # test_make_system_structure( _system_structure()) + # test_use_fmu(_oscillator_fmu(), _driver_fmu(), show=True) + # test_run_osp(_oscillator_fmu(), _driver_fmu()) + # test_run_osp_system_structure(_system_structure(), show=True) + # test_run_osp_sweep( _system_structure(), show=True, alg='fixedStep') + test_run_osp_sweep( _system_structure(), show=True, alg='ecco') diff --git a/tests/test_time_table_fmu.py b/tests/test_time_table_fmu.py new file mode 100644 index 0000000..c52a230 --- /dev/null +++ b/tests/test_time_table_fmu.py @@ -0,0 +1,283 @@ +import shutil +import xml.etree.ElementTree as ET # noqa: N817 +from pathlib import Path +from typing import Any, Iterable +from zipfile import ZipFile + +import numpy as np +import pytest +from fmpy.simulation import simulate_fmu # type: ignore +from fmpy.util import fmu_info, plot_result # type: ignore +from fmpy.validation import validate_fmu # type: ignore +from libcosimpy.CosimEnums import CosimExecutionState +from libcosimpy.CosimExecution import CosimExecution +from libcosimpy.CosimLogging import CosimLogLevel, log_output_level +from libcosimpy.CosimManipulator import CosimManipulator # type: ignore +from libcosimpy.CosimObserver import CosimObserver # type: ignore +from libcosimpy.CosimSlave import CosimLocalSlave +from pythonfmu.enums import Fmi2Causality as Causality +from pythonfmu.enums import Fmi2Variability as Variability + +from component_model.model import Model + + +def arrays_equal( + res: Iterable[Any], + expected: Iterable[Any], + eps: float = 1e-7, +): + len_res = len(list(res)) + len_exp = len(list(expected)) + if len_res != len_exp: + raise ValueError(f"Arrays of different lengths cannot be equal. Found {len_res} != {len_exp}") + for i, (x, y) in enumerate(zip(res, expected, strict=False)): + assert abs(x - y) < eps, f"Element {i} not nearly equal in {x}, {y}" + + +@pytest.fixture(scope="session") +def time_table_fmu(): + return _time_table_fmu() + + +def _time_table_fmu(): + """Make FMU and return .fmu file with path.""" + build_path = Path.cwd() + build_path.mkdir(exist_ok=True) + fmu_path = Model.build( + script=str(Path(__file__).parent.parent / "examples" / "time_table_fmu.py"), + project_files=[Path(__file__).parent.parent / "examples" / "time_table.py"], + dest=build_path, + ) + return fmu_path + + +@pytest.fixture(scope="session") +def time_table_system_structure(time_table_fmu): + return _time_table_system_structure(time_table_fmu) + + +def _time_table_system_structure(time_table_fmu): + """Make a structure file and return the path""" + # path = make_osp_system_structure( + # name="TimeTableStructure", + # simulators={"tab": {"source": "TimeTableFMU.fmu", "stepSize": 0.1}}, # , "interpolate": 1}}, + # version="0.1", + # start=0.0, + # base_step=0.1, + # algorithm="fixedStep", + # path=Path.cwd(), + # ) + shutil.copy(Path(__file__).parent.parent / "examples" / "TimeTableStructure.xml", Path.cwd()) + return Path.cwd() / "TimeTableStructure.xml" + + +def test_time_table_fmu(): + from examples.time_table_fmu import TimeTableFMU + + tbl = TimeTableFMU(data=[[0, 0, 0], [1, 1, 1], [2, 2, 4], [3, 3, 9]], header=["exp1", "exp2"], interpolate=1) + assert tbl._cols == 2 + assert tbl._rows == 4 + assert tbl.header == ("exp1", "exp2") + assert tbl.interpolate == 1 + arrays_equal(tbl.times, (0, 1, 2, 3)) + arrays_equal(tbl.data[1], [1, 1]) + assert tbl._outs.causality == Causality.output + assert tbl._outs.variability == Variability.continuous + arrays_equal(tbl._outs.start, tbl.outs) + assert tbl._interpolate.causality == Causality.parameter + assert tbl._interpolate.variability == Variability.fixed + assert tbl._interpolate.start == (tbl.interpolate,) + + +def _in_interval(x: float, x0: float, x1: float): + return x0 <= x <= x1 or x1 <= x <= x0 + + +def _linear(t: float, tt: tuple | list, xx: tuple | list): + if t <= tt[-1]: + return np.interp([t], tt, xx)[0] + else: + return xx[-1] + (t - tt[-1]) * (xx[-1] - xx[-2]) / (tt[-1] - tt[-2]) + + +def _to_et(file: str, sub: str = "modelDescription.xml"): + with ZipFile(file) as zp: + xml = zp.read(sub) + return ET.fromstring(xml) + + +def test_make_time_table(time_table_fmu): + info = fmu_info(time_table_fmu) # this is a formatted string. Not easy to check + print(f"Info: {info}") + et = _to_et(str(time_table_fmu)) + assert et.attrib["fmiVersion"] == "2.0", "FMI Version" + # similarly other critical issues of the modelDescription can be checked + assert et.attrib["variableNamingConvention"] == "structured", "Variable naming convention. => use [i] for arrays" + # print(et.attrib) + val = validate_fmu(str(time_table_fmu)) + assert not len(val), ( + f"Validation of the modelDescription of {time_table_fmu.name} was not successful. Errors: {val}" + ) + + +def test_use_fmu(time_table_fmu, show: bool = False): + """Use the FMU running it on fmpy using various interpolate settings.""" + print(fmu_info(time_table_fmu)) + _t = np.linspace(0, 10, 101) + for ipol in range(4): + result = simulate_fmu( # type: ignore[reportArgumentType] + time_table_fmu, + stop_time=10.0, + step_size=0.1, + validate=True, + solver="Euler", + debug_logging=True, + logger=print, # fmi_call_logger=print, + start_values={"interpolate": ipol}, + ) + if show: + plot_result(result) + for i, t in enumerate(_t): + assert result[i][0] == t, f"Wrong time picked: {t} != {result[i][0]}" + if ipol >= 0: + pass + elif ipol == 0: + assert result[i][1] == 1.0, f"Result for {ipol}, time={t}: {result[i][1]} != 1.0" + elif ipol == 1: + assert abs(result[i][2] - t) < 1e-10, f"Result for {ipol}, time={t}: {result[i][1]} != {i}" + elif ipol == 2: + assert abs(result[i][3] - t**2) < 1e-10, f"Result for {ipol}, time={t}: {result[i][1]} != {i**2}" + + +def test_run_osp(time_table_fmu): + log_output_level(CosimLogLevel.DEBUG) + sim = CosimExecution.from_step_size(step_size=1e8) # empty execution object with fixed time step in nanos + st = CosimLocalSlave(fmu_path=str(time_table_fmu), instance_name="st") + + ist = sim.add_local_slave(st) + assert ist == 0, f"local slave number {ist}" + + reference_dict = {var_ref.name.decode(): var_ref.reference for var_ref in sim.slave_variables(ist)} + + # Set initial values + sim.boolean_initial_value(ist, reference_dict["interpolate"], True) + + sim_status = sim.status() + assert sim_status.current_time == 0 + assert CosimExecutionState(sim_status.state) == CosimExecutionState.STOPPED + + # Simulate for 1 second + sim.simulate_until(target_time=15e9) + + +def test_check_osp_system_structure(time_table_system_structure): + "Instantiate OSP from system structure" + log_output_level(CosimLogLevel.DEBUG) + simulator = CosimExecution.from_osp_config_file(str(time_table_system_structure)) + comps = [] + for comp in list(simulator.slave_infos()): + name = comp.name.decode() + comps.append(name) + assert comps == ["tab"], f"Components: {comps}" + variables = {} + for idx in range(simulator.num_slave_variables(0)): + struct = simulator.slave_variables(0)[idx] + variables.update( + { + struct.name.decode(): { + "reference": struct.reference, + "type": struct.type, + "causality": struct.causality, + "variability": struct.variability, + } + } + ) + assert variables["outs[0]"] == {"reference": 1, "type": 0, "causality": 2, "variability": 4} # similar: [1],[2] + assert variables["interpolate"] == {"reference": 0, "type": 1, "causality": 1, "variability": 1} + + +def test_run_osp_system_structure(time_table_system_structure): + "Run an OSP simulation in the same way as the SystemInterface of sim-explorer is implemented" + log_output_level(CosimLogLevel.TRACE) + for ipol in range(4): + simulator = CosimExecution.from_osp_config_file(str(time_table_system_structure)) # reset + simulator.integer_initial_value(0, 0, ipol) # set 'interpolate' + # manipulator and obeserver + manipulator = CosimManipulator.create_override() + simulator.add_manipulator(manipulator=manipulator) + observer = CosimObserver.create_last_value() + simulator.add_observer(observer=observer) + for time in np.linspace(0.1, 10, 100): + simulator.simulate_until(time * 1e9) + if time == 0.1: + assert observer.last_integer_values(0, [0]) == [ipol], ( + f"iPol {observer.last_integer_values(0, [0])} != {ipol}" + ) + if ipol == 0: + _x = observer.last_real_values(0, [1])[0] + assert _x == 1.0, f"Result for {ipol}: {_x} != 1.0" + elif ipol == 1: + _x = observer.last_real_values(0, [2])[0] + assert abs(_x - time) < 1e-10, f"Result for {ipol}: {_x} != {time}" + elif ipol == 2: + _x = observer.last_real_values(0, [3])[0] + assert abs(_x - time**2) < 1e-10, f"Result for {ipol}: {_x} != {time**2}" + + +def test_make_with_new_data(): + """Test and example how keyword arguments of the Model class can be used (changed) when building FMU.""" + times = np.linspace(0, 2 * np.pi, 100) + data = list(zip(times, np.cos(times), np.sin(times), strict=False)) + build_path = Path.cwd() + build_path.mkdir(exist_ok=True) + _ = Model.build( + script=str(Path(__file__).parent.parent / "examples" / "time_table_fmu.py"), + project_files=[Path(__file__).parent.parent / "examples" / "time_table.py"], + dest=build_path, + newargs={ + "data": data, + "header": ("x", "y"), + "interpolate": 1, + "default_experiment": {"startTime": 0, "stopTime": 2 * np.pi, "stepSize": 0.1, "tolerance": 1e-5}, + }, + ) + + +@pytest.mark.skip(reason="Does so far not work within pytest, only stand-alone") +def test_use_with_new_data(show): + fmu_path = Path(__file__).parent / "test_working_directory" / "TimeTableFMU.fmu" + result = simulate_fmu( # type: ignore[reportArgumentType] + fmu_path, + stop_time=2 * np.pi, + step_size=0.1, + validate=True, + solver="Euler", + debug_logging=True, + logger=print, # fmi_call_logger=print, + start_values={"interpolate": 2}, + ) + if show: + plot_result(result) + time = 0.0 + for t, x, y in result: + assert abs(t - time) < 1e-10, f"Expected time {time}!={t} from data" + assert abs(1.0 - (x**2 + y**2)) < 1e-5, f"time {t}: x={x}, y={y}, x**2+y**2 = {x**2 + y**2}" + time += 0.1 + if time > 2 * np.pi: + time = 2 * np.pi + + +if __name__ == "__main__": + retcode = 0#pytest.main(["-rA", "-v", __file__]) + assert retcode == 0, f"Non-zero return code {retcode}" + import os + + os.chdir(Path(__file__).parent.absolute() / "test_working_directory") + # test_time_table_fmu() + # test_make_time_table(_time_table_fmu()) + # test_use_fmu(_time_table_fmu(), show=True) + # test_run_osp(_time_table_fmu()) + test_check_osp_system_structure(_time_table_system_structure(_time_table_fmu())) + # test_run_osp_system_structure(_time_table_system_structure(_time_table_fmu())) + # test_make_with_new_data() + # test_use_with_new_data(show=True) From 8fe58ca4e23fba144eadde40491e426893bc2874 Mon Sep 17 00:00:00 2001 From: Eisinger Date: Wed, 11 Jun 2025 13:15:38 +0200 Subject: [PATCH 08/29] Changes related to using libcosimpy with the ECCO algorithm. Test is included in test_oscillator_fmu --- examples/ForcedOscillator.xml | 10 ++- examples/bouncing_ball_3d.py | 2 +- examples/driving_force_fmu.py | 39 +++++++--- examples/oscillator.py | 6 +- src/component_model/variable.py | 2 +- tests/test_axle_fmu.py | 2 +- tests/test_bouncing_ball_3d.py | 9 ++- tests/test_oscillator.py | 90 +++++++++++------------ tests/test_oscillator_fmu.py | 124 ++++++++++++++++++-------------- tests/test_time_table_fmu.py | 2 +- 10 files changed, 163 insertions(+), 123 deletions(-) diff --git a/examples/ForcedOscillator.xml b/examples/ForcedOscillator.xml index 7a1d491..47a1fa3 100644 --- a/examples/ForcedOscillator.xml +++ b/examples/ForcedOscillator.xml @@ -7,9 +7,13 @@ - - - + + + + + + + diff --git a/examples/bouncing_ball_3d.py b/examples/bouncing_ball_3d.py index 29593af..2e05770 100644 --- a/examples/bouncing_ball_3d.py +++ b/examples/bouncing_ball_3d.py @@ -130,7 +130,7 @@ def _interface(self, name: str, start: str | float | tuple) -> Variable: variability="continuous", initial="exact", start=start, - rng=((0, "100 m"), None, (0, "10 m")), + rng=((0, "100 m"), None, (0, "39.371 inch")), ) elif name == "speed": return Variable( diff --git a/examples/driving_force_fmu.py b/examples/driving_force_fmu.py index bfef884..ea672a4 100644 --- a/examples/driving_force_fmu.py +++ b/examples/driving_force_fmu.py @@ -21,20 +21,21 @@ def func(time: float, ampl: float = 1.0, omega: float = 0.1, d_omega: float = 0. """ if d_omega == 0.0: return np.array((0, 0, ampl * sin(omega * time)), float) - else: - return np.array((0, 0, ampl * sin((omega + d_omega*time) * time)), float) + else: + return np.array((0, 0, ampl * sin((omega + d_omega * time) * time)), float) class DrivingForce(Model): """A driving force in 3 dimensions which produces an ouput per time and can be connected to the oscillator. + Note1: the FMU model is made directly (without a basic python class model), which is not recommended! + Note2: the speed of the connected oscillator is added as additional connector. + Since the driving is forced, the input speed is ignored, but it is needed for the ECCO algorithm (power bonds). + Note: This completely replaces DrivingForce (do_step and other functions are not re-used). Args: func (callable)=func: The driving force function f(t). - Note: The func can currently not really be handled as parameter and must be hard-coded here (see above). - Soon to come: Model.build() function which honors parameters, such that function can be supplied from - outside and the FMU can be re-build without changing the class. """ def __init__( @@ -51,7 +52,13 @@ def __init__( "Siegfried Eisinger", **kwargs, ) - # interface Variables + # interface Variables. We define first their values, to help pyright, since the basic model is missing + self.ampl: float = ampl + self.freq: float = freq + self.d_freq: float = d_freq + self.function: Callable = function + self.f = np.array((0, 0, 0), float) + self.v_osc = np.array((0, 0, 0), float) self._ampl = Variable(self, "ampl", "The amplitude of the force in N", start=ampl) self._freq = Variable(self, "freq", "The frequency of the force in 1/s", start=freq) self._d_freq = Variable(self, "d_freq", "Change of frequency of the force in 1/s**2", start=d_freq) @@ -64,15 +71,25 @@ def __init__( variability="continuous", start=np.array((0, 0, 0), float), ) + self._v_osc = Variable( + self, + "v_osc", + "Input connector for the speed of the connected element in m/s", + causality="input", + variability="continuous", + start=np.array((0, 0, 0), float), + ) def do_step(self, current_time: float, step_size: float): - self.f = self.func(current_time+step_size) + self.f = self.func(current_time + step_size) return True # very important! def exit_initialization_mode(self): """Set internal state after initial variables are set.""" - self.func = partial(self.function, - ampl=self.ampl, - omega=2 * pi * self.freq, - d_omega= 0.0 if self.d_freq == 0.0 else 2 * pi * self.d_freq) + self.func = partial( + self.function, + ampl=self.ampl, + omega=2 * pi * self.freq, + d_omega=0.0 if self.d_freq == 0.0 else 2 * pi * self.d_freq, + ) logger.info(f"Initial settings: ampl={self.ampl}, freq={self.freq}, d_freq={self.d_freq}") diff --git a/examples/oscillator.py b/examples/oscillator.py index 6ed2b11..fc31d04 100644 --- a/examples/oscillator.py +++ b/examples/oscillator.py @@ -33,7 +33,7 @@ def __init__( c: tuple[float, float, float] | tuple[str, str, str] = (0.0, 0.0, 0.0), m: float = 1.0, tolerance: float = 1e-5, - f_func: Callable|None = None + f_func: Callable | None = None, ): self.k = np.array(k, float) self.c = np.array(c, float) @@ -51,8 +51,8 @@ def ode_func(self, t: float, y: np.ndarray, i: int, f: float) -> np.ndarray: if self.f_func is None: if f != 0: res += np.array((f, 0), float) - elif i==2: # only implemented for z - res += np.array((self.f_func(t)[i], 0), float) + elif i == 2: # only implemented for z + res += np.array((self.f_func(t)[i], 0), float) return res def do_step(self, current_time: float, step_size: int | float) -> bool: diff --git a/src/component_model/variable.py b/src/component_model/variable.py index 07d308a..1763da8 100644 --- a/src/component_model/variable.py +++ b/src/component_model/variable.py @@ -68,7 +68,7 @@ def __str__(self): return txt def parse_quantity(self, quantity: PyType, ureg: UnitRegistry, typ: type | None = None) -> PyType: - """Disect the provided quantity in terms of magnitude and unit, if provided as string. + """Parse the provided quantity in terms of magnitude and unit, if provided as string. If another type is provided, dimensionless units are assumed. Args: diff --git a/tests/test_axle_fmu.py b/tests/test_axle_fmu.py index 02bf20f..15d2481 100644 --- a/tests/test_axle_fmu.py +++ b/tests/test_axle_fmu.py @@ -146,7 +146,7 @@ def test_use_fmu(axle_fmu: Path, show: bool): if __name__ == "__main__": - retcode = 0#pytest.main(["-rP -s -v", __file__]) + retcode = 0 # pytest.main(["-rP -s -v", __file__]) assert retcode == 0, f"Return code {retcode}" import os diff --git a/tests/test_bouncing_ball_3d.py b/tests/test_bouncing_ball_3d.py index a579df5..d1faa54 100644 --- a/tests/test_bouncing_ball_3d.py +++ b/tests/test_bouncing_ball_3d.py @@ -420,6 +420,8 @@ def get_status(sim): assert values[6] == 1.5, "Initial setting did not work" assert values[5] == -0.015, "Initial setting did not have the expected effect on speed" + manipulator.slave_real_values(ibb, [0, 1, 2], [1.0, 2.0, 3.0]) + # values = observer.last_real_values(0, list(range(11))) # print("VALUES2", values) @@ -428,6 +430,7 @@ def get_status(sim): # sim.simulate_until(target_time=3e9) + def test_from_fmu(bouncing_ball_fmu): assert bouncing_ball_fmu.exists(), "FMU not found" model = model_from_fmu(bouncing_ball_fmu) @@ -450,13 +453,13 @@ def test_from_fmu(bouncing_ball_fmu): if __name__ == "__main__": - retcode = 0 # pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__]) + retcode = pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__]) assert retcode == 0, f"Non-zero return code {retcode}" import os os.chdir(Path(__file__).parent / "test_working_directory") # test_bouncing_ball_class(show=False) - test_make_bouncing_ball(_bouncing_ball_fmu()) + # test_make_bouncing_ball(_bouncing_ball_fmu()) # test_use_fmu(_bouncing_ball_fmu(), True) # test_from_fmu( _bouncing_ball_fmu()) - # test_from_osp( _bouncing_ball_fmu()) + # test_from_osp(_bouncing_ball_fmu()) diff --git a/tests/test_oscillator.py b/tests/test_oscillator.py index 3ba12ff..f133ea4 100644 --- a/tests/test_oscillator.py +++ b/tests/test_oscillator.py @@ -1,9 +1,9 @@ from functools import partial from math import atan2, cos, exp, pi, sin, sqrt +from pathlib import Path import matplotlib.pyplot as plt import numpy as np -from pathlib import Path def arrays_equal(res: tuple[float, ...] | list[float], expected: tuple[float, ...] | list[float], eps=1e-7): @@ -29,9 +29,9 @@ def do_show(time: list, z: list, v: list, compare1: list | None = None, compare2 def force(t: float, ampl: float = 1.0, omega: float = 0.1, d_omega: float = 0.0): if d_omega == 0.0: - return np.array((0, 0, ampl * sin(omega * t)), float) # fixed frequency + return np.array((0, 0, ampl * sin(omega * t)), float) # fixed frequency else: - return np.array((0, 0, ampl * sin((omega + d_omega*t) * t)), float) # frequency sweep + return np.array((0, 0, ampl * sin((omega + d_omega * t) * t)), float) # frequency sweep def forced_oscillator( @@ -118,6 +118,7 @@ def run_oscillation_z( return (osc, times, z, v) + def sweep_oscillation_z( k: float, c: float, @@ -136,13 +137,9 @@ def sweep_oscillation_z( and return the oscillator object and the time series for z-position and z-velocity.""" from examples.oscillator import Oscillator - f_func = f_func=partial(force, ampl=ampl, omega=0.0, d_omega=d_omega) - osc = Oscillator(k=(1.0, 1.0, k), - c=(0.0, 0.0, c), - m=m, - tolerance=tol, - f_func = f_func - ) + + f_func = f_func = partial(force, ampl=ampl, omega=0.0, d_omega=d_omega) + osc = Oscillator(k=(1.0, 1.0, k), c=(0.0, 0.0, c), m=m, tolerance=tol, f_func=f_func) osc.x[2] = x0 # set initial z value osc.v[2] = v0 # set initial z-speed times, z, v, f = [], [], [], [] @@ -264,58 +261,61 @@ def area(x: list[float], y: list[float]): assert abs(y[-1] - 4.0) < 1e-12, f"Found {y[-1]}" osc, x, y = run_2d(x0=(1.0, 0.0, 0.0), v0=(0.0, 1.0, 0.0), k=(1.0, 1.0 / 15.8, 0), end=20 * np.pi) if show: - show_2d(x,y) + show_2d(x, y) + def test_sweep_oscillator(show: bool = True): """A forced oscillator where the force frequency is changed linearly as d_omega*time. The test demonstrates that a monolithic simulation provides accurate results in all ranges of the force frequency. Co-simulating the oscillator and the force, this does not work. """ - osc, times0, z0, v0, f0 = sweep_oscillation_z( k=1.0, - c=0.1, - m=1.0, - ampl=1.0, - d_omega=0.1, - x0=0.0, - v0=0.0, - dt=0.1, # 'ground truth', small dt - end=100.0, - tol=1e-3 - ) - with open(Path.cwd() / "oscillator_sweep0.dat", 'w') as fp: + osc, times0, z0, v0, f0 = sweep_oscillation_z( + k=1.0, + c=0.1, + m=1.0, + ampl=1.0, + d_omega=0.1, + x0=0.0, + v0=0.0, + dt=0.1, # 'ground truth', small dt + end=100.0, + tol=1e-3, + ) + with open(Path.cwd() / "oscillator_sweep0.dat", "w") as fp: for i in range(len(times0)): - fp.write( f"{times0[i]}\t{z0[i]}\t{v0[i]}\t{f0[i]}\n") - + fp.write(f"{times0[i]}\t{z0[i]}\t{v0[i]}\t{f0[i]}\n") + if show: - freq = [0.1*t/2/np.pi for t in times0] + freq = [0.1 * t / 2 / np.pi for t in times0] fig, ax = plt.subplots() ax.plot(freq, z0, label="z0(t)") ax.plot(freq, v0, label="v0(t)") ax.plot(freq, f0, label="F0(t)") ax.legend() plt.show() - - osc, times, z, v, f = sweep_oscillation_z( k=1.0, - c=0.1, - m=1.0, - ampl=1.0, - d_omega=0.1, - x0=0.0, - v0=0.0, - dt=1, # dt similar to resonance frequency - end=100.0, - tol=1e-3 - ) + + osc, times, z, v, f = sweep_oscillation_z( + k=1.0, + c=0.1, + m=1.0, + ampl=1.0, + d_omega=0.1, + x0=0.0, + v0=0.0, + dt=1, # dt similar to resonance frequency + end=100.0, + tol=1e-3, + ) i0 = 0 - for i in range(len(times)): # demonstrate that the results are accurate, even if dt is large + for i in range(len(times)): # demonstrate that the results are accurate, even if dt is large t = times[i] - while abs(times0[i0]-t) > 1e-10: + while abs(times0[i0] - t) > 1e-10: i0 += 1 assert times0[i0] - t < 0.1, f"Time entry for time {t} not found in times0" - + assert abs(z0[i0] - z[i]) < 2e-2, f"Time {t}. Found {z0[i0]} != {z[i]}" assert abs(v0[i0] - v[i]) < 2e-2, f"Time {t}. Found {v0[i0]} != {v[i]}" - + if show: fig, ax = plt.subplots() ax.plot(times0, z0, label="z0(t)") @@ -323,11 +323,13 @@ def test_sweep_oscillator(show: bool = True): ax.legend() plt.show() + if __name__ == "__main__": - retcode = 0 # pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__]) + retcode = pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__]) assert retcode == 0, f"Non-zero return code {retcode}" import os + os.chdir(Path(__file__).parent.absolute() / "test_working_directory") # test_oscillator_class(show=True) # test_2d(show=True) - test_sweep_oscillator() + # test_sweep_oscillator() diff --git a/tests/test_oscillator_fmu.py b/tests/test_oscillator_fmu.py index 03086a6..f207d8d 100644 --- a/tests/test_oscillator_fmu.py +++ b/tests/test_oscillator_fmu.py @@ -1,5 +1,6 @@ import shutil import sys +import xml.etree.ElementTree as ET # noqa: N817 from collections.abc import Iterable from pathlib import Path from typing import Any @@ -19,22 +20,27 @@ from component_model.model import Model from component_model.utils.xml import read_xml -import xml.etree.ElementTree as ET # noqa: N817 -def system_structure_change( structure_file: Path, tag: str, what: str, newval: str): + +def system_structure_change(structure_file: Path, tag: str, what: str, newval: str): def register_all_namespaces(filename): - namespaces = dict([node for _, node in ET.iterparse(filename, events=['start-ns'])]) - for ns in namespaces: - ET.register_namespace(ns, namespaces[ns]) + namespaces: dict = {} + for _, (ns, uri) in ET.iterparse(filename, events=["start-ns"]): + print("TYPES", ns, type(ns), uri, type(uri)) + namespaces.update({ns: uri}) + ET.register_namespace(str(ns), str(uri)) + # namespaces: dict = dict([node )]) + # for ns in namespaces: + # ET.register_namespace(ns, namespaces[ns]) return namespaces - nss = register_all_namespaces( structure_file) + nss = register_all_namespaces(structure_file) el = read_xml(structure_file) - elements = el.findall(f"ns:{tag}", {'ns' : nss['']}) + elements = el.findall(f"ns:{tag}", {"ns": nss[""]}) for e in elements: - if what=='text': + if what == "text": e.text = newval - else: # assume attribute name + else: # assume attribute name e.attrib[what] = newval ET.ElementTree(el).write(structure_file.name, encoding="utf-8") @@ -51,7 +57,8 @@ def arrays_equal( for i, (x, y) in enumerate(zip(res, expected, strict=False)): assert abs(x - y) < eps, f"Element {i} not nearly equal in {x}, {y}" -def do_show( traces: dict[str, tuple[list[float],list[float]]]): + +def do_show(traces: dict[str, tuple[list[float], list[float]]]): fig, ax = plt.subplots() for label, trace in traces.items(): _ = ax.plot(trace[0], trace[1], label=label) @@ -80,6 +87,7 @@ def _oscillator_fmu(): ) return fmu_path + @pytest.fixture(scope="session") def driver_fmu(): return _driver_fmu() @@ -90,11 +98,7 @@ def _driver_fmu(): build_path = Path.cwd() build_path.mkdir(exist_ok=True) src = Path(__file__).parent.parent / "examples" / "driving_force_fmu.py" - fmu_path = Model.build( - script=str(src), - project_files=[src], - dest=build_path - ) + fmu_path = Model.build(script=str(src), project_files=[src], dest=build_path) return fmu_path @@ -108,6 +112,7 @@ def _system_structure(): shutil.copy(Path(__file__).parent.parent / "examples" / "ForcedOscillator.xml", Path.cwd()) return Path.cwd() / "ForcedOscillator.xml" + def test_make_fmus( oscillator_fmu: Path, driver_fmu: Path, @@ -137,7 +142,7 @@ def test_make_fmus( # -def test_use_fmu(oscillator_fmu: Path, driver_fmu: Path, show: bool): +def test_use_fmu(oscillator_fmu: Path, driver_fmu: Path, show: bool = True): """Test single FMUs.""" # sourcery skip: move-assign result = simulate_fmu( @@ -181,7 +186,7 @@ def test_run_osp(oscillator_fmu: Path, driver_fmu: Path): @pytest.mark.skipif(sys.platform.startswith("linux"), reason="HarmonicOsciallatorFMU.fmu throws an error on Linux") -def test_run_osp_system_structure(system_structure: Path, show: bool): +def test_run_osp_system_structure(system_structure: Path, show: bool = True): "Run an OSP simulation in the same way as the SimulatorInterface of sim-explorer is implemented" log_output_level(CosimLogLevel.TRACE) print("STRUCTURE", system_structure) @@ -189,6 +194,7 @@ def test_run_osp_system_structure(system_structure: Path, show: bool): simulator = CosimExecution.from_osp_config_file(str(system_structure)) except Exception as err: print("ERR", err) + return sim_status = simulator.status() assert sim_status.current_time == 0 assert CosimExecutionState(sim_status.state) == CosimExecutionState.STOPPED @@ -209,14 +215,16 @@ def test_run_osp_system_structure(system_structure: Path, show: bool): for idx in range(simulator.num_slave_variables(1)): struct = simulator.slave_variables(1)[idx] - variables |= { - struct.name.decode(): { - "reference": struct.reference, - "type": struct.type, - "causality": struct.causality, - "variability": struct.variability, + variables.update( + { + struct.name.decode(): { + "reference": struct.reference, + "type": struct.type, + "causality": struct.causality, + "variability": struct.variability, + } } - } + ) assert variables["c[2]"]["type"] == 0 assert variables["c[2]"]["causality"] == 1 assert variables["c[2]"]["variability"] == 1 @@ -226,7 +234,7 @@ def test_run_osp_system_structure(system_structure: Path, show: bool): assert variables["x[2]"]["variability"] == 4 assert variables["v[2]"]["type"] == 0 - assert variables["v[2]"]["causality"] == 2 + assert variables["v[2]"]["causality"] == 2, f"Found {variables['v[2]']['causality']}" assert variables["v[2]"]["variability"] == 4 # Instantiate a suitable observer for collecting results. @@ -248,33 +256,38 @@ def test_run_osp_system_structure(system_structure: Path, show: bool): pos.append(values[0]) speed.append(values[1]) if show: - do_show(traces={'z-pos' : (times,pos), 'z-speed' : (times,speed)}) + do_show(traces={"z-pos": (times, pos), "z-speed": (times, speed)}) + + +def test_system_structure_change(system_structure): + system_structure_change(system_structure, "BaseStepSize", "text", str(0.99)) + -@pytest.mark.parameterize("alg", ['fixedStep', 'ecco']) -def test_run_osp_sweep(system_structure: Path, show: bool, alg:str): +@pytest.mark.parametrize("alg", ["fixedStep", "ecco"]) +def test_run_osp_sweep(system_structure: Path, show: bool = True, alg: str = "fixedStep"): "Run an OSP simulation of the oscillator and the force sweep as co-simulation." dt = 1.0 t_end = 100.0 - + log_output_level(CosimLogLevel.TRACE) system_structure_change(system_structure, "BaseStepSize", "text", str(dt)) system_structure_change(system_structure, "Algorithm", "text", alg) print(f"Running Algorithm {alg} on {system_structure}") assert system_structure.exists(), f"File {system_structure} not found" simulator = CosimExecution.from_osp_config_file(str(system_structure)) - print(get_last_error_message()) sim_status = simulator.status() - #manipulator = CosimManipulator.create_override() - #simulator.add_manipulator(manipulator=manipulator) - simulator.real_initial_value(slave_index=0, variable_reference=2, value=1.0) # k[2] - simulator.real_initial_value(slave_index=0, variable_reference=5, value=0.1) # c[2] - simulator.real_initial_value(slave_index=0, variable_reference=6, value=1.0) # m - simulator.real_initial_value(slave_index=0, variable_reference=0, value=1.0) # ampl - simulator.real_initial_value(slave_index=1, variable_reference=1, value=0.0) # freq (start frequency) - simulator.real_initial_value(slave_index=1, variable_reference=2, value=0.1/2/np.pi) # d_freq - simulator.real_initial_value(slave_index=0, variable_reference=9, value=0.0) # x0[2] - simulator.real_initial_value(slave_index=0, variable_reference=12, value=0.0) # v0[2] + assert sim_status.error_code == 0 + # manipulator = CosimManipulator.create_override() + # simulator.add_manipulator(manipulator=manipulator) + simulator.real_initial_value(slave_index=0, variable_reference=2, value=1.0) # k[2] + simulator.real_initial_value(slave_index=0, variable_reference=5, value=0.1) # c[2] + simulator.real_initial_value(slave_index=0, variable_reference=6, value=1.0) # m + simulator.real_initial_value(slave_index=0, variable_reference=0, value=1.0) # ampl + simulator.real_initial_value(slave_index=1, variable_reference=1, value=0.0) # freq (start frequency) + simulator.real_initial_value(slave_index=1, variable_reference=2, value=0.1 / 2 / np.pi) # d_freq + simulator.real_initial_value(slave_index=0, variable_reference=9, value=0.0) # x0[2] + simulator.real_initial_value(slave_index=0, variable_reference=12, value=0.0) # v0[2] observer = CosimObserver.create_last_value() simulator.add_observer(observer=observer) times = [] @@ -283,39 +296,40 @@ def test_run_osp_sweep(system_structure: Path, show: bool, alg:str): time = 0.0 while time < t_end: time += dt - times.append( time) - _ = simulator.simulate_until( int(time*1e9)) + times.append(time) + _ = simulator.simulate_until(int(time * 1e9)) values = observer.last_real_values(slave_index=0, variable_references=[9, 12]) # x[2], v[2] pos.append(values[0]) speed.append(values[1]) if Path("oscillator_sweep0.dat").exists(): times0, pos0, speed0, force0 = [], [], [], [] - with open("oscillator_sweep0.dat", 'r') as fp: + with open("oscillator_sweep0.dat", "r") as fp: for line in fp: - t, p, v, f = line.split('\t') + t, p, v, f = line.split("\t") times0.append(float(t)) pos0.append(float(p)) speed0.append(float(v)) force0.append(float(f)) if show: - freq0 = [0.1*t/2/np.pi for t in times0] - freq = [0.1*t/2/np.pi for t in times] - do_show({'z-pos0' : (freq0,pos0), 'z-pos' : (freq,pos)}) - + freq0 = [0.1 * t / 2 / np.pi for t in times0] + freq = [0.1 * t / 2 / np.pi for t in times] + do_show({"z-pos0": (freq0, pos0), "z-pos": (freq, pos)}) + elif show: - do_show( {'z-pos' : (times,pos), 'z-speed' : (times,speed)}) - + do_show({"z-pos": (times, pos), "z-speed": (times, speed)}) + if __name__ == "__main__": - retcode = 0 #pytest.main(args=["-rA", "-v", __file__, "--show", "True"]) + retcode = 0 # pytest.main(args=["-rA", "-v", __file__, "--show", "True"]) assert retcode == 0, f"Non-zero return code {retcode}" import os os.chdir(Path(__file__).parent.absolute() / "test_working_directory") - test_make_fmus(_oscillator_fmu(), _driver_fmu()) - # test_make_system_structure( _system_structure()) + test_system_structure_change(_system_structure()) + # test_make_fmus(_oscillator_fmu(), _driver_fmu(), show=True) + # test_make_system_structure( _system_structure(), show=True) # test_use_fmu(_oscillator_fmu(), _driver_fmu(), show=True) # test_run_osp(_oscillator_fmu(), _driver_fmu()) # test_run_osp_system_structure(_system_structure(), show=True) - # test_run_osp_sweep( _system_structure(), show=True, alg='fixedStep') - test_run_osp_sweep( _system_structure(), show=True, alg='ecco') + # test_run_osp_sweep(_system_structure(), show=True, alg="fixedStep") + # test_run_osp_sweep( _system_structure(), show=True, alg='ecco') diff --git a/tests/test_time_table_fmu.py b/tests/test_time_table_fmu.py index c52a230..74f03da 100644 --- a/tests/test_time_table_fmu.py +++ b/tests/test_time_table_fmu.py @@ -268,7 +268,7 @@ def test_use_with_new_data(show): if __name__ == "__main__": - retcode = 0#pytest.main(["-rA", "-v", __file__]) + retcode = 0 # pytest.main(["-rA", "-v", __file__]) assert retcode == 0, f"Non-zero return code {retcode}" import os From 353a6cc1a71fbec358bf350ecca800954d5123a4 Mon Sep 17 00:00:00 2001 From: Eisinger Date: Sun, 29 Jun 2025 23:10:43 +0200 Subject: [PATCH 09/29] Intermediate update to demonstrate a problem --- examples/ForcedOscillator.xml | 20 +--- examples/driving_force_fmu.py | 57 ++++++---- examples/oscillator.py | 31 +++-- examples/oscillator_fmu.py | 8 +- src/component_model/model.py | 3 +- tests/test_axle_fmu.py | 6 +- tests/test_oscillator.py | 3 +- tests/test_oscillator_fmu.py | 206 +++++++++++++++++++++++----------- 8 files changed, 210 insertions(+), 124 deletions(-) diff --git a/examples/ForcedOscillator.xml b/examples/ForcedOscillator.xml index 47a1fa3..9ce865d 100644 --- a/examples/ForcedOscillator.xml +++ b/examples/ForcedOscillator.xml @@ -3,29 +3,17 @@ 0.01 fixedStep - - + + - + - + - - 0.99 - 0.0001 - 0.00001 - 0.01 - 0.2 - 1.5 - 0.2 - 0.15 - 1e-6 - 1e-6 - \ No newline at end of file diff --git a/examples/driving_force_fmu.py b/examples/driving_force_fmu.py index ea672a4..f8ce8a5 100644 --- a/examples/driving_force_fmu.py +++ b/examples/driving_force_fmu.py @@ -1,5 +1,4 @@ import logging -from collections.abc import Callable from functools import partial from math import pi, sin from typing import Any @@ -15,14 +14,18 @@ # Note: PythonFMU (which component-model is built on) works on files and thus only one Model class allowed per file -def func(time: float, ampl: float = 1.0, omega: float = 0.1, d_omega: float = 0.0): +def func(time: float, + ampl: np.ndarray, + omega: np.ndarray, + d_omega: np.ndarray): """Generate a harmonic oscillating force function. Optionally it is possible to linearly change the angular frequency as omega + d_omega*time. + The function is intended to be initialized through partial, so that only 'time' is left as variable. """ if d_omega == 0.0: - return np.array((0, 0, ampl * sin(omega * time)), float) + return ampl * np.sin(omega * time) else: - return np.array((0, 0, ampl * sin((omega + d_omega * time) * time)), float) + return ampl * sin((omega + d_omega * time) * time) class DrivingForce(Model): @@ -36,13 +39,17 @@ class DrivingForce(Model): Args: func (callable)=func: The driving force function f(t). + ampl (float|tuple) = 1.0: the amplitude of the (sinusoidal) driving force. Same for all 3D if float. + Optional with units. + freq (float|tuple) = 1.0: the frequency of the (sinusoidal) driving force. Same for all 3D if float. + Optional with units. + d_freq (float) = 0.0: Optional frequency change per time unit (for frequency sweep experiments). """ def __init__( self, - function: Callable[..., Any] = func, - ampl: float = 1.0, - freq: float = 1.0, + ampl: float | tuple[float] | tuple[str] = 1.0, + freq: float | tuple[float] | tuple[str] = 1.0, d_freq: float = 0.0, **kwargs: Any, ): @@ -53,23 +60,29 @@ def __init__( **kwargs, ) # interface Variables. We define first their values, to help pyright, since the basic model is missing - self.ampl: float = ampl - self.freq: float = freq - self.d_freq: float = d_freq - self.function: Callable = function - self.f = np.array((0, 0, 0), float) - self.v_osc = np.array((0, 0, 0), float) - self._ampl = Variable(self, "ampl", "The amplitude of the force in N", start=ampl) - self._freq = Variable(self, "freq", "The frequency of the force in 1/s", start=freq) - self._d_freq = Variable(self, "d_freq", "Change of frequency of the force in 1/s**2", start=d_freq) - self.function = function + print(f"DRIVING_FORCE.fmu from {__file__}( {ampl}, {freq}, {d_freq}") + _ampl = ampl if isinstance( ampl, tuple) else (ampl,) + self.dim = len(_ampl) + _freq = freq if isinstance( freq, tuple) else (freq,) + assert len(_freq) == self.dim, f"ampl and freq are expected of same length. Found {ampl}, {freq}" + _d_freq = d_freq if isinstance(d_freq, tuple) else (d_freq,)*self.dim + assert len(_d_freq) == self.dim, f"d_freq expected as float or has same length as ampl:{ampl}. Found {d_freq}" + self.ampl = (1.0,) * self.dim + self.freq = (1.0,) * self.dim + self.d_freq = (0.0,)* self.dim + self.function = func + self.f = (0.0,) * self.dim + self.v_osc = (0.0,) * self.dim + self._ampl = Variable(self, "ampl", "The amplitude of the force in N", start=_ampl) + self._freq = Variable(self, "freq", "The frequency of the force in 1/s", start=_freq) + self._d_freq = Variable(self, "d_freq", "Change of frequency of the force in 1/s**2", start=_d_freq) self._f = Variable( self, "f", "Output connector for the driving force f(t) in N", causality="output", variability="continuous", - start=np.array((0, 0, 0), float), + start=(0.0,) * self.dim, ) self._v_osc = Variable( self, @@ -77,7 +90,7 @@ def __init__( "Input connector for the speed of the connected element in m/s", causality="input", variability="continuous", - start=np.array((0, 0, 0), float), + start=(0.0,) * self.dim, ) def do_step(self, current_time: float, step_size: float): @@ -88,8 +101,8 @@ def exit_initialization_mode(self): """Set internal state after initial variables are set.""" self.func = partial( self.function, - ampl=self.ampl, - omega=2 * pi * self.freq, - d_omega=0.0 if self.d_freq == 0.0 else 2 * pi * self.d_freq, + ampl=np.array(self.ampl, float), + omega=np.array(2 * pi * self.freq, float), + d_omega=np.array( 2 * pi * self.d_freq, float), ) logger.info(f"Initial settings: ampl={self.ampl}, freq={self.freq}, d_freq={self.d_freq}") diff --git a/examples/oscillator.py b/examples/oscillator.py index fc31d04..c5ad23b 100644 --- a/examples/oscillator.py +++ b/examples/oscillator.py @@ -23,27 +23,33 @@ class Oscillator: Args: k (tuple)=(1.0, 1.0, 1.0): spring constant in N/m. May vary in 3D c (tuple)=(0.0, 0.0, 0.0): Viscous damping coefficient in N.s/m. May vary in 3D - m (float)=1.0: Mass of the spring load (spring mass negligible) in kg + m (float | tuple)=1.0: Mass of the spring load (spring mass negligible) in kg. Same in all dim. if float tolerance (float)=1e-5: Optional tolerance in m, i.e. maximum uncertainty in displacement x. """ def __init__( self, - k: tuple[float, float, float] | tuple[str, str, str] = (1.0, 1.0, 1.0), # type str for FMU option - c: tuple[float, float, float] | tuple[str, str, str] = (0.0, 0.0, 0.0), - m: float = 1.0, + k: tuple[float, ...] | tuple[str, ...] = (1.0, 1.0, 1.0), # type str for FMU option + c: tuple[float, ...] | tuple[str, ...] = (0.0, 0.0, 0.0), + m: float | tuple[float, ...] = 1.0, tolerance: float = 1e-5, f_func: Callable | None = None, ): + self.dim = len(k) self.k = np.array(k, float) self.c = np.array(c, float) - self.m = m + if isinstance(m, float): + self.m = np.array((m,) * self.dim, float) + else: + self.m = np.array(m, float) self.tolerance = tolerance - self.x = np.array((0, 0, 0), float) - self.v = np.array((0, 0, 0), float) - self.f = np.array((0, 0, 0), float) + self.x = np.array((0,) * self.dim, float) + self.v = np.array((0,) * self.dim, float) + self.f = np.array((0,) * self.dim, float) # standard ODE matrix (homogeneous system): - self.ode = [np.array(((-self.c[i] / self.m, -self.k[i] / self.m), (1, 0)), float) for i in range(3)] + self.ode = [ + np.array(((-self.c[i] / self.m[i], -self.k[i] / self.m[i]), (1, 0)), float) for i in range(self.dim) + ] self.f_func = f_func def ode_func(self, t: float, y: np.ndarray, i: int, f: float) -> np.ndarray: @@ -60,7 +66,8 @@ def do_step(self, current_time: float, step_size: int | float) -> bool: We implement a very simplistic algoritm based on difference calculus. """ - for i in range(3): # this is a 3D oscillator + print(f"OSC do_step") + for i in range(self.dim): # this is a xD oscillator if self.x[i] != 0 or self.v[i] != 0 or self.f[i] != 0 or self.f_func is not None: y0 = np.array([self.v[i], self.x[i]], float) sol = integrate.solve_ivp( @@ -78,8 +85,8 @@ def do_step(self, current_time: float, step_size: int | float) -> bool: def period(self): """Calculate the natural period of the oscillator (without damping an).""" w2 = [] - for i in range(3): - w2i = self.k[i] / self.m - (self.c[i] / 2 / self.m) ** 2 + for i in range(self.dim): + w2i = self.k[i] / self.m[i] - (self.c[i] / 2 / self.m[i]) ** 2 if w2i > 0: w2.append(2 * np.pi / np.sqrt(w2i)) else: diff --git a/examples/oscillator_fmu.py b/examples/oscillator_fmu.py index 5771034..144397f 100644 --- a/examples/oscillator_fmu.py +++ b/examples/oscillator_fmu.py @@ -30,12 +30,12 @@ class HarmonicOscillator(Model, Oscillator): # refer to Model first! def __init__( self, - k: tuple[float, float, float] | tuple[str, str, str] = (1.0, 1.0, 1.0), - c: tuple[float, float, float] | tuple[str, str, str] = (0.0, 0.0, 0.0), + k: tuple[float, ...] | tuple[str, ...] = (1.0, 1.0, 1.0), + c: tuple[float, ...] | tuple[str, ...] = (0.0, 0.0, 0.0), m: float | str = 1.0, tolerance: float = 1e-5, - x0: tuple[float, float, float] | tuple[str, str, str] = (1.0, 1.0, 1.0), - v0: tuple[float, float, float] | tuple[str, str, str] = (0.0, 0.0, 0.0), + x0: tuple[float, ...] | tuple[str, ...] = (1.0, 1.0, 1.0), + v0: tuple[float, ...] | tuple[str, ...] = (0.0, 0.0, 0.0), **kwargs: Any, ): Model.__init__( diff --git a/src/component_model/model.py b/src/component_model/model.py index bf591a1..73f273f 100644 --- a/src/component_model/model.py +++ b/src/component_model/model.py @@ -250,6 +250,7 @@ def register_variable( # type: ignore [reportIncompatibleMethodOverride] # not assert var.getter is not None, f"No getter method defined for {var}" # logger.info(f"REGISTER Variable {var.name}. getter: {var.getter}, setter: {var.setter}") + print(f"MODEL_var_register {var.name}: {vref}. Len:{len(var)}. {var}") for i in range(1, len(var)): self.vars[var.value_reference + i] = None # marking that this is a sub-element self._unit_ensure_registered(var) @@ -445,7 +446,7 @@ def build( dest=dest, documentation_folder=doc_dir, newargs=newargs, - ) # , xFunc=None) + ) return asBuilt def to_xml(self, model_options: dict | None = None) -> ET.Element: diff --git a/tests/test_axle_fmu.py b/tests/test_axle_fmu.py index 15d2481..a6d3fae 100644 --- a/tests/test_axle_fmu.py +++ b/tests/test_axle_fmu.py @@ -99,7 +99,7 @@ def _axle_fmu(): build_path = Path.cwd() build_path.mkdir(exist_ok=True) fmu_path = Model.build( - script=str(Path(__file__).parent.parent / "examples" / "axle_fmu.py"), + script=Path(__file__).parent.parent / "examples" / "axle_fmu.py", project_files=[Path(__file__).parent.parent / "examples" / "axle.py"], dest=build_path, ) @@ -146,11 +146,11 @@ def test_use_fmu(axle_fmu: Path, show: bool): if __name__ == "__main__": - retcode = 0 # pytest.main(["-rP -s -v", __file__]) + retcode = pytest.main(["-rP -s -v", __file__]) assert retcode == 0, f"Return code {retcode}" import os os.chdir(Path(__file__).parent.absolute() / "test_working_directory") # test_axle_class(show=True) # test_make_fmu(_axle_fmu(), show=False)#True) - test_use_fmu(_axle_fmu(), show=True) + # test_use_fmu(_axle_fmu(), show=True) diff --git a/tests/test_oscillator.py b/tests/test_oscillator.py index f133ea4..707d257 100644 --- a/tests/test_oscillator.py +++ b/tests/test_oscillator.py @@ -4,6 +4,7 @@ import matplotlib.pyplot as plt import numpy as np +import pytest def arrays_equal(res: tuple[float, ...] | list[float], expected: tuple[float, ...] | list[float], eps=1e-7): @@ -332,4 +333,4 @@ def test_sweep_oscillator(show: bool = True): os.chdir(Path(__file__).parent.absolute() / "test_working_directory") # test_oscillator_class(show=True) # test_2d(show=True) - # test_sweep_oscillator() + # test_sweep_oscillator(show=True) diff --git a/tests/test_oscillator_fmu.py b/tests/test_oscillator_fmu.py index f207d8d..917166f 100644 --- a/tests/test_oscillator_fmu.py +++ b/tests/test_oscillator_fmu.py @@ -11,7 +11,12 @@ from fmpy.simulation import simulate_fmu from fmpy.util import fmu_info, plot_result from fmpy.validation import validate_fmu -from libcosimpy.CosimEnums import CosimExecutionState +from libcosimpy.CosimEnums import ( + CosimExecutionState, + CosimVariableCausality, + CosimVariableType, + CosimVariableVariability, +) from libcosimpy.CosimExecution import CosimExecution from libcosimpy.CosimLogging import CosimLogLevel, log_output_level from libcosimpy.CosimManipulator import CosimManipulator @@ -22,11 +27,77 @@ from component_model.utils.xml import read_xml +def _slave_index(simulator: CosimExecution, name: str): + """Get the slave index from the name.""" + return simulator.slave_index_from_instance_name(name) + + +def _var_ref(simulator: CosimExecution, slave_name: str, var_name: str): + """Get the variable value reference from slave and variable name. + Return both the slave index and the value reference as tuple.""" + slave = _slave_index(simulator, slave_name) + if slave is None: + return (-1, -1) + for idx in range(simulator.num_slave_variables(slave)): + struct = simulator.slave_variables(slave)[idx] + if struct.name.decode() == var_name: + return (slave, struct.reference) + return (-1, -1) + + +def _check_var(simulator: CosimExecution, slave_name: str, var_name: str, expected: int | tuple | None): + if var_name == "": + idx = _slave_index(simulator, slave_name) + assert idx == expected, f"Slave {slave_name}. Index: {idx}. Expected: {expected}" + else: + idx, vref = _var_ref(simulator, slave_name, var_name) + assert (idx, vref) == expected, f"Variable {slave_name}.{var_name}: ({idx},{vref}). Expected:{expected}" + + +def _var_info(simulator: CosimExecution, slave_name: str, var_name: str): + slave = _slave_index(simulator, slave_name) + if slave is None: + return {} + for idx in range(simulator.num_slave_variables(slave)): + struct = simulator.slave_variables(slave)[idx] + if struct.name.decode() == var_name: + return { + "slave": 0, + "idx": idx, + "reference": struct.reference, + "type": CosimVariableType(struct.type).name, + "causality": CosimVariableCausality(struct.causality).name, + "variability": CosimVariableVariability(struct.variability).name, + } + return {} + + +def _var_list(simulator: CosimExecution, slave_name: str, ret: str = "print"): + slave = _slave_index(simulator, slave_name) + assert slave is not None, f"Slave {slave_name} not found in system" + variables = {} + for idx in range(simulator.num_slave_variables(slave)): + struct = simulator.slave_variables(slave)[idx] + if ret == "print": + print( + f"Slave {slave_name}({slave}), var {struct.name.decode()}({struct.reference}), type: {CosimVariableType(struct.type).name}, causality: {CosimVariableCausality(struct.causality).name}, variability: {CosimVariableVariability(struct.variability)}" + ) + else: + variables[struct.name.decode()] = { + "slave": slave, + "idx": idx, + "reference": struct.reference, + "type": CosimVariableType(struct.type).name, + "causality": CosimVariableCausality(struct.causality).name, + "variability": CosimVariableVariability(struct.variability), + } + + def system_structure_change(structure_file: Path, tag: str, what: str, newval: str): def register_all_namespaces(filename): namespaces: dict = {} for _, (ns, uri) in ET.iterparse(filename, events=["start-ns"]): - print("TYPES", ns, type(ns), uri, type(uri)) + # print("TYPES", ns, type(ns), uri, type(uri)) namespaces.update({ns: uri}) ET.register_namespace(str(ns), str(uri)) # namespaces: dict = dict([node )]) @@ -81,8 +152,8 @@ def _oscillator_fmu(): build_path.mkdir(exist_ok=True) src = Path(__file__).parent.parent / "examples" / "oscillator_fmu.py" fmu_path = Model.build( - script=str(src), - project_files=[src], + script=src, + project_files=[src.parent], dest=build_path, ) return fmu_path @@ -98,7 +169,12 @@ def _driver_fmu(): build_path = Path.cwd() build_path.mkdir(exist_ok=True) src = Path(__file__).parent.parent / "examples" / "driving_force_fmu.py" - fmu_path = Model.build(script=str(src), project_files=[src], dest=build_path) + fmu_path = Model.build( + script=src, + project_files=[], + dest=build_path, + newargs={"ampl": ("3N", "2N", "1N"), "freq": ("3Hz", "2Hz", "1Hz")}, + ) return fmu_path @@ -142,7 +218,7 @@ def test_make_fmus( # -def test_use_fmu(oscillator_fmu: Path, driver_fmu: Path, show: bool = True): +def test_run_fmpy(oscillator_fmu: Path, driver_fmu: Path, show: bool = True): """Test single FMUs.""" # sourcery skip: move-assign result = simulate_fmu( @@ -167,15 +243,22 @@ def test_run_osp(oscillator_fmu: Path, driver_fmu: Path): osc = CosimLocalSlave(fmu_path=str(oscillator_fmu), instance_name="osc") _osc = sim.add_local_slave(osc) assert _osc == 0, f"local slave number {_osc}" - reference_dict = {var_ref.name.decode(): var_ref.reference for var_ref in sim.slave_variables(_osc)} dri = CosimLocalSlave(fmu_path=str(driver_fmu), instance_name="dri") _dri = sim.add_local_slave(dri) assert _dri == 1, f"local slave number {_dri}" + _check_var(sim, "dri", "", _dri) + _check_var(sim, "dri", "d_freq", (1, 6)) + _check_var(sim, "dri", "f[2]", (1, 9)) + info = _var_info(sim, "dri", "f[2]") + assert info["causality"] == "OUTPUT" + assert info["variability"] == "CONTINUOUS" + assert info["type"] == "REAL" + # Set initial values - sim.real_initial_value(slave_index=_osc, variable_reference=reference_dict["x[2]"], value=1.0) - sim.real_initial_value(slave_index=_osc, variable_reference=reference_dict["c[2]"], value=0.1) + sim.real_initial_value(*_var_ref(sim, "osc", "x[2]"), value=1.0) + sim.real_initial_value(*_var_ref(sim, "osc", "c[2]"), value=0.1) sim_status = sim.status() assert sim_status.current_time == 0 @@ -191,66 +274,56 @@ def test_run_osp_system_structure(system_structure: Path, show: bool = True): log_output_level(CosimLogLevel.TRACE) print("STRUCTURE", system_structure) try: - simulator = CosimExecution.from_osp_config_file(str(system_structure)) + sim = CosimExecution.from_osp_config_file(str(system_structure)) except Exception as err: print("ERR", err) return - sim_status = simulator.status() + sim_status = sim.status() assert sim_status.current_time == 0 - assert CosimExecutionState(sim_status.state) == CosimExecutionState.STOPPED - comps = [] - for comp in list(simulator.slave_infos()): - name = comp.name.decode() - comps.append(name) - assert comps == ["osc", "drv"] - variables = {} - for idx in range(simulator.num_slave_variables(0)): - struct = simulator.slave_variables(0)[idx] - variables[struct.name.decode()] = { - "reference": struct.reference, - "type": struct.type, - "causality": struct.causality, - "variability": struct.variability, - } - - for idx in range(simulator.num_slave_variables(1)): - struct = simulator.slave_variables(1)[idx] - variables.update( - { - struct.name.decode(): { - "reference": struct.reference, - "type": struct.type, - "causality": struct.causality, - "variability": struct.variability, - } - } - ) - assert variables["c[2]"]["type"] == 0 - assert variables["c[2]"]["causality"] == 1 - assert variables["c[2]"]["variability"] == 1 - - assert variables["x[2]"]["type"] == 0 - assert variables["x[2]"]["causality"] == 2 - assert variables["x[2]"]["variability"] == 4 - - assert variables["v[2]"]["type"] == 0 - assert variables["v[2]"]["causality"] == 2, f"Found {variables['v[2]']['causality']}" - assert variables["v[2]"]["variability"] == 4 + assert CosimExecutionState(sim_status.state).name == "STOPPED" + _check_var(sim, "osc", "", 0) + _check_var(sim, "drv", "", 1) + #_var_list(sim, 'drv') + _check_var(sim, "drv", "ampl[0]", (1, 0)) + _check_var(sim, "drv", "ampl[1]", (1, 1)) + _check_var(sim, "drv", "ampl[2]", (1, 2)) + _check_var(sim, "drv", "d_freq[2]", (1, 8)) + _check_var(sim, "drv", "f[2]", (1, 11)) + info = _var_info(sim, "drv", "f[2]") + assert info["causality"] == "OUTPUT" + assert info["variability"] == "CONTINUOUS" + assert info["type"] == "REAL" + + info = _var_info(sim, "osc", "c[2]") + assert info["type"] == "REAL" + assert info["causality"] == "PARAMETER" + assert info["variability"] == "FIXED" + + info = _var_info(sim, "osc", "x[2]") + assert info["type"] == "REAL" + assert info["causality"] == "OUTPUT" + assert info["variability"] == "CONTINUOUS" + + info = _var_info(sim, "osc", "v[2]") + assert info["type"] == "REAL" + assert info["causality"] == "OUTPUT" + assert info["variability"] == "CONTINUOUS" # Instantiate a suitable observer for collecting results. # Instantiate a suitable manipulator for changing variables. manipulator = CosimManipulator.create_override() - simulator.add_manipulator(manipulator=manipulator) - simulator.real_initial_value(slave_index=0, variable_reference=5, value=0.5) # c[2] - simulator.real_initial_value(slave_index=0, variable_reference=9, value=1.0) # x[2] + sim.add_manipulator(manipulator=manipulator) + sim.real_initial_value(*_var_ref(sim, "osc", "c[2]"), value=0.5) # c[2] + sim.real_initial_value(*_var_ref(sim, "osc", "x[2]"), value=1.0) # x[2] observer = CosimObserver.create_last_value() - simulator.add_observer(observer=observer) - times = [] - pos = [] - speed = [] + sim.add_observer(observer=observer) + times : list[float] = [] + pos : list[float] = [] + speed : list[float] = [] for step in range(1, 1000): time = step * 0.01 - _ = simulator.simulate_until(step * 1e8) + _ = sim.simulate_until(step * 1e8) + return values = observer.last_real_values(slave_index=0, variable_references=[9, 12]) # x[2], v[2] times.append(time) pos.append(values[0]) @@ -264,7 +337,11 @@ def test_system_structure_change(system_structure): @pytest.mark.parametrize("alg", ["fixedStep", "ecco"]) -def test_run_osp_sweep(system_structure: Path, show: bool = True, alg: str = "fixedStep"): +def test_run_osp_sweep(system_structure: Path, alg: str, show: bool = True): + _test_run_osp_sweep(system_structure, show, alg) + + +def _test_run_osp_sweep(system_structure: Path, show: bool = True, alg: str = "fixedStep"): "Run an OSP simulation of the oscillator and the force sweep as co-simulation." dt = 1.0 @@ -325,11 +402,10 @@ def test_run_osp_sweep(system_structure: Path, show: bool = True, alg: str = "fi import os os.chdir(Path(__file__).parent.absolute() / "test_working_directory") - test_system_structure_change(_system_structure()) - # test_make_fmus(_oscillator_fmu(), _driver_fmu(), show=True) - # test_make_system_structure( _system_structure(), show=True) - # test_use_fmu(_oscillator_fmu(), _driver_fmu(), show=True) + # test_system_structure_change(_system_structure()) + # test_make_fmus(_oscillator_fmu(), _driver_fmu()) + # test_run_fmpy(_oscillator_fmu(), _driver_fmu(), show=True) # test_run_osp(_oscillator_fmu(), _driver_fmu()) - # test_run_osp_system_structure(_system_structure(), show=True) + test_run_osp_system_structure(_system_structure(), show=True) # test_run_osp_sweep(_system_structure(), show=True, alg="fixedStep") - # test_run_osp_sweep( _system_structure(), show=True, alg='ecco') + # _test_run_osp_sweep( _system_structure(), show=True, alg='ecco') From 6375582901aeb670625bb8cec3bfb2a735ba1862 Mon Sep 17 00:00:00 2001 From: Eisinger Date: Fri, 4 Jul 2025 10:08:31 +0200 Subject: [PATCH 10/29] Updates with respect to oscillator_6dof and PythonFMU issues --- examples/BouncingBallStructure.xml | 9 + examples/ForcedOscillator.xml | 20 +- examples/driving_force_fmu.py | 37 ++- examples/oscillator.py | 11 +- examples/oscillator_6dof_fmu.py | 112 +++++++++ src/component_model/model.py | 1 - tests/test_oscillator.py | 2 +- tests/test_oscillator_6dof_fmu.py | 365 +++++++++++++++++++++++++++++ tests/test_oscillator_fmu.py | 55 ++--- tests/test_utils.py | 6 +- 10 files changed, 560 insertions(+), 58 deletions(-) create mode 100644 examples/BouncingBallStructure.xml create mode 100644 examples/oscillator_6dof_fmu.py create mode 100644 tests/test_oscillator_6dof_fmu.py diff --git a/examples/BouncingBallStructure.xml b/examples/BouncingBallStructure.xml new file mode 100644 index 0000000..eb1b995 --- /dev/null +++ b/examples/BouncingBallStructure.xml @@ -0,0 +1,9 @@ + + 0.0 + 0.1 + + + + + + \ No newline at end of file diff --git a/examples/ForcedOscillator.xml b/examples/ForcedOscillator.xml index 9ce865d..47a1fa3 100644 --- a/examples/ForcedOscillator.xml +++ b/examples/ForcedOscillator.xml @@ -3,17 +3,29 @@ 0.01 fixedStep - - + + - + - + + + 0.99 + 0.0001 + 0.00001 + 0.01 + 0.2 + 1.5 + 0.2 + 0.15 + 1e-6 + 1e-6 + \ No newline at end of file diff --git a/examples/driving_force_fmu.py b/examples/driving_force_fmu.py index f8ce8a5..5ed1976 100644 --- a/examples/driving_force_fmu.py +++ b/examples/driving_force_fmu.py @@ -1,7 +1,6 @@ import logging from functools import partial -from math import pi, sin -from typing import Any +from typing import Any, Callable import numpy as np @@ -14,18 +13,15 @@ # Note: PythonFMU (which component-model is built on) works on files and thus only one Model class allowed per file -def func(time: float, - ampl: np.ndarray, - omega: np.ndarray, - d_omega: np.ndarray): +def func(time: float, ampl: np.ndarray, omega: np.ndarray, d_omega: np.ndarray): """Generate a harmonic oscillating force function. Optionally it is possible to linearly change the angular frequency as omega + d_omega*time. The function is intended to be initialized through partial, so that only 'time' is left as variable. """ - if d_omega == 0.0: + if all(_do == 0.0 for _do in d_omega): return ampl * np.sin(omega * time) else: - return ampl * sin((omega + d_omega * time) * time) + return ampl * np.sin((omega + d_omega * time) * time) class DrivingForce(Model): @@ -39,9 +35,9 @@ class DrivingForce(Model): Args: func (callable)=func: The driving force function f(t). - ampl (float|tuple) = 1.0: the amplitude of the (sinusoidal) driving force. Same for all 3D if float. + ampl (float|tuple) = 1.0: the amplitude of the (sinusoidal) driving force. Same for all D if float. Optional with units. - freq (float|tuple) = 1.0: the frequency of the (sinusoidal) driving force. Same for all 3D if float. + freq (float|tuple) = 1.0: the frequency of the (sinusoidal) driving force. Same for all D if float. Optional with units. d_freq (float) = 0.0: Optional frequency change per time unit (for frequency sweep experiments). """ @@ -50,7 +46,7 @@ def __init__( self, ampl: float | tuple[float] | tuple[str] = 1.0, freq: float | tuple[float] | tuple[str] = 1.0, - d_freq: float = 0.0, + d_freq: float | tuple[float] | tuple[str] = 0.0, **kwargs: Any, ): super().__init__( @@ -60,18 +56,18 @@ def __init__( **kwargs, ) # interface Variables. We define first their values, to help pyright, since the basic model is missing - print(f"DRIVING_FORCE.fmu from {__file__}( {ampl}, {freq}, {d_freq}") - _ampl = ampl if isinstance( ampl, tuple) else (ampl,) + _ampl = ampl if isinstance(ampl, tuple) else (ampl,) self.dim = len(_ampl) - _freq = freq if isinstance( freq, tuple) else (freq,) + _freq = freq if isinstance(freq, tuple) else (freq,) assert len(_freq) == self.dim, f"ampl and freq are expected of same length. Found {ampl}, {freq}" - _d_freq = d_freq if isinstance(d_freq, tuple) else (d_freq,)*self.dim + _d_freq = d_freq if isinstance(d_freq, tuple) else (d_freq,) * self.dim assert len(_d_freq) == self.dim, f"d_freq expected as float or has same length as ampl:{ampl}. Found {d_freq}" self.ampl = (1.0,) * self.dim self.freq = (1.0,) * self.dim - self.d_freq = (0.0,)* self.dim + self.d_freq = (0.0,) * self.dim self.function = func - self.f = (0.0,) * self.dim + self.func: Callable + self.f = np.array((0.0,) * self.dim, float) self.v_osc = (0.0,) * self.dim self._ampl = Variable(self, "ampl", "The amplitude of the force in N", start=_ampl) self._freq = Variable(self, "freq", "The frequency of the force in 1/s", start=_freq) @@ -99,10 +95,13 @@ def do_step(self, current_time: float, step_size: float): def exit_initialization_mode(self): """Set internal state after initial variables are set.""" + assert isinstance(self.ampl, np.ndarray) + assert isinstance(self.freq, np.ndarray) + assert isinstance(self.d_freq, np.ndarray) self.func = partial( self.function, ampl=np.array(self.ampl, float), - omega=np.array(2 * pi * self.freq, float), - d_omega=np.array( 2 * pi * self.d_freq, float), + omega=np.array(2 * np.pi * self.freq, float), # type: ignore # it is an ndarray! + d_omega=np.array(2 * np.pi * self.d_freq, float), # type: ignore # it is an ndarray! ) logger.info(f"Initial settings: ampl={self.ampl}, freq={self.freq}, d_freq={self.d_freq}") diff --git a/examples/oscillator.py b/examples/oscillator.py index c5ad23b..a7dd68e 100644 --- a/examples/oscillator.py +++ b/examples/oscillator.py @@ -52,12 +52,18 @@ def __init__( ] self.f_func = f_func - def ode_func(self, t: float, y: np.ndarray, i: int, f: float) -> np.ndarray: + def ode_func( + self, + t: float, # scalar time + y: np.ndarray, # combined array of position and speed for component i + i: int, # dimension + f: float, + ) -> np.ndarray: # force for component i res = self.ode[i].dot(y) if self.f_func is None: if f != 0: res += np.array((f, 0), float) - elif i == 2: # only implemented for z + else: res += np.array((self.f_func(t)[i], 0), float) return res @@ -66,7 +72,6 @@ def do_step(self, current_time: float, step_size: int | float) -> bool: We implement a very simplistic algoritm based on difference calculus. """ - print(f"OSC do_step") for i in range(self.dim): # this is a xD oscillator if self.x[i] != 0 or self.v[i] != 0 or self.f[i] != 0 or self.f_func is not None: y0 = np.array([self.v[i], self.x[i]], float) diff --git a/examples/oscillator_6dof_fmu.py b/examples/oscillator_6dof_fmu.py new file mode 100644 index 0000000..1f0b304 --- /dev/null +++ b/examples/oscillator_6dof_fmu.py @@ -0,0 +1,112 @@ +import logging +from typing import Any + +import numpy as np + +from component_model.model import Model +from component_model.variable import Variable +from examples.oscillator import Oscillator + +logger = logging.getLogger(__name__) + + +class HarmonicOscillator6D(Model, Oscillator): # refer to Model first! + """General harmonic oscillator with 6 DoF, extended for FMU packaging. + + The system obeys the equations + F(t) - k*x - c*dx/dt = m*d^2x/dt^2 (first 3 equations, one per linear dimension) + with F: external force, x: displacement and other symbols and units as defined below. + See also `Harmonic_oscillator `_ + and + T(t) - k*a - c*da/dt = I* d^a2/dt^2 + with T: external torque, a: deflection angle and other symbols and units as defined below. + See also `Torsion_spring `_ + Torque, the angles and all parameters are in cartesian pseudo vectors, + i.e. a torque in z-direction implies a right-handed turn in the x-y plane. + All parameters may vary along the 3 spatial dimensions. + + See also test_oscillator_fmu() where build process and further testing is found. + For details see oscillator.py and test_oscillator.py, where the basic model is defined and tested. + + Args: + k (tuple[float] | tuple[str])=(1.0,)*6: spring/torsion constants in N/m or N.m/rad + c (tuple[float] | tuple[str])=(0.0,)*6: Viscous damping coefficients in N.s/m or N.m.s/rad + m (float)=1.0: Mass of the spring load (spring mass negligible) in kg + mi (tuple[float] | tuple[str])=(1.0,)*3: Moments of inertia in kg.m^2 + tolerance (float)=1e-5: Optional tolerance in m, i.e. maximum uncertainty in displacement x. + x0 (tuple) = (0.0)*6: Start position in m or rad + v0 (tuple) = (0.0)*6: Start speed in m/s or rad/s + """ + + def __init__( + self, + k: tuple[float, ...] | tuple[str, ...] = (1.0,) * 6, + c: tuple[float, ...] | tuple[str, ...] = (0.1,) * 6, + m: float | str = 1.0, + mi: tuple[float, ...] | tuple[str, ...] = (1.0,) * 3, + tolerance: float = 1e-5, + x0: tuple[float, ...] | tuple[str, ...] = (0.0,) * 6, + v0: tuple[float, ...] | tuple[str, ...] = (0.0,) * 6, + **kwargs: Any, + ): + Model.__init__( + self, # here we define a few standard entries for FMU + name="HarmonicOscillator", + description="3D harmonic oscillator prepared for FMU packaging. 3D driving force can be connected", + author="Siegfried Eisinger", + version="0.2", + default_experiment={"startTime": 0.0, "stopTime": 10.0, "stepSize": 0.01, "tolerance": 1e-5}, + **kwargs, + ) + # include arguments to get the dimension right! + Oscillator.__init__(self, (1.0,) * 6, (0.1,) * 6, (1.0,) * 6, tolerance=1e-3, f_func=None) + # interface Variables. + # Note that the Variable object is accessible as self._, while the value is self. + self._k = Variable(self, "k", "The 6D spring constant in N/m or N.m/rad", start=k) + self._c = Variable(self, "c", "The 6D spring damping in in N.s/m or N.m.s/rad", start=c) + self._m = Variable(self, "m", "The mass at end of spring in kg", start=(m, m, m, mi[0], mi[1], mi[2])) + self._x = Variable( + self, + "x", + "The time-dependent 6D generalized position of the mass in m or rad", + causality="output", + variability="continuous", + initial="exact", + start=x0, + ) + self._v = Variable( + self, + "v", + "The time-dependent 6D generalized speed of the mass in m/s or rad/s", + causality="output", + variability="continuous", + initial="exact", + start=v0, + ) + self._f = Variable( + self, + "f", + "Input connector for the 6D external force acting on the mass in N or N.m", + causality="input", + variability="continuous", + start=np.array((0,) * 6, float), + ) + + def do_step(self, current_time: float, step_size: float): + if not Model.do_step(self, current_time, step_size): # some housekeeping functions (not really needed here) + return False + Oscillator.do_step(self, current_time, step_size) # this does the integration itself + return True # very important for the FMU mechanism + + def exit_initialization_mode(self): + """Set internal state after initial variables are set. + + Note: need to update the ODE matrix to reflect changes in c, k or m! + """ + self.ode = [ + np.array(((-self.c[i] / self.m[i], -self.k[i] / self.m[i]), (1, 0)), float) for i in range(self.dim) + ] + logger.info(f"Initial settings: k={self.k}, c={self.c}, m={self.m}, x={self.x}, v={self.v}, f={self.f}") + + # Note: The other FMU functions like .setup_experiment and .exit_initialization_mode + # do not need special attention here and can be left out diff --git a/src/component_model/model.py b/src/component_model/model.py index 73f273f..a1a92e9 100644 --- a/src/component_model/model.py +++ b/src/component_model/model.py @@ -250,7 +250,6 @@ def register_variable( # type: ignore [reportIncompatibleMethodOverride] # not assert var.getter is not None, f"No getter method defined for {var}" # logger.info(f"REGISTER Variable {var.name}. getter: {var.getter}, setter: {var.setter}") - print(f"MODEL_var_register {var.name}: {vref}. Len:{len(var)}. {var}") for i in range(1, len(var)): self.vars[var.value_reference + i] = None # marking that this is a sub-element self._unit_ensure_registered(var) diff --git a/tests/test_oscillator.py b/tests/test_oscillator.py index 707d257..f1610cb 100644 --- a/tests/test_oscillator.py +++ b/tests/test_oscillator.py @@ -291,7 +291,7 @@ def test_sweep_oscillator(show: bool = True): fig, ax = plt.subplots() ax.plot(freq, z0, label="z0(t)") ax.plot(freq, v0, label="v0(t)") - ax.plot(freq, f0, label="F0(t)") + # ax.plot(freq, f0, label="F0(t)") ax.legend() plt.show() diff --git a/tests/test_oscillator_6dof_fmu.py b/tests/test_oscillator_6dof_fmu.py new file mode 100644 index 0000000..221582c --- /dev/null +++ b/tests/test_oscillator_6dof_fmu.py @@ -0,0 +1,365 @@ +import shutil +import sys +import xml.etree.ElementTree as ET # noqa: N817 +from collections.abc import Iterable +from pathlib import Path +from typing import Any + +import matplotlib.pyplot as plt +import numpy as np +import pytest +from fmpy.simulation import simulate_fmu +from fmpy.util import fmu_info, plot_result +from fmpy.validation import validate_fmu +from libcosimpy.CosimEnums import ( + CosimExecutionState, + CosimVariableCausality, + CosimVariableType, + CosimVariableVariability, +) +from libcosimpy.CosimExecution import CosimExecution +from libcosimpy.CosimLogging import CosimLogLevel, log_output_level +from libcosimpy.CosimManipulator import CosimManipulator +from libcosimpy.CosimObserver import CosimObserver +from libcosimpy.CosimSlave import CosimLocalSlave + +from component_model.model import Model +from component_model.utils.xml import read_xml + + +def _slave_index(simulator: CosimExecution, name: str): + """Get the slave index from the name.""" + return simulator.slave_index_from_instance_name(name) + + +def _var_ref(simulator: CosimExecution, slave_name: str, var_name: str): + """Get the variable value reference from slave and variable name. + Return both the slave index and the value reference as tuple.""" + slave = _slave_index(simulator, slave_name) + if slave is None: + return (-1, -1) + for idx in range(simulator.num_slave_variables(slave)): + struct = simulator.slave_variables(slave)[idx] + if struct.name.decode() == var_name: + return (slave, struct.reference) + return (-1, -1) + + +def _var_list(simulator: CosimExecution, slave_name: str, ret: str = "print"): + slave = _slave_index(simulator, slave_name) + assert slave is not None, f"Slave {slave_name} not found in system" + variables = {} + for idx in range(simulator.num_slave_variables(slave)): + struct = simulator.slave_variables(slave)[idx] + if ret == "print": + print( + f"Slave {slave_name}({slave}), var {struct.name.decode()}({struct.reference}), type: {CosimVariableType(struct.type).name}, causality: {CosimVariableCausality(struct.causality).name}, variability: {CosimVariableVariability(struct.variability)}" + ) + else: + variables[struct.name.decode()] = { + "slave": slave, + "idx": idx, + "reference": struct.reference, + "type": CosimVariableType(struct.type).name, + "causality": CosimVariableCausality(struct.causality).name, + "variability": CosimVariableVariability(struct.variability), + } + + +def system_structure_change(structure_file: Path, tag: str, what: str, newval: str): + def register_all_namespaces(filename): + namespaces: dict = {} + for _, (ns, uri) in ET.iterparse(filename, events=["start-ns"]): + # print("TYPES", ns, type(ns), uri, type(uri)) + namespaces.update({ns: uri}) + ET.register_namespace(str(ns), str(uri)) + # namespaces: dict = dict([node )]) + # for ns in namespaces: + # ET.register_namespace(ns, namespaces[ns]) + return namespaces + + nss = register_all_namespaces(structure_file) + el = read_xml(structure_file) + elements = el.findall(f"ns:{tag}", {"ns": nss[""]}) + for e in elements: + if what == "text": + e.text = newval + else: # assume attribute name + e.attrib[what] = newval + ET.ElementTree(el).write(structure_file.name, encoding="utf-8") + + +def arrays_equal( + res: Iterable[Any], + expected: Iterable[Any], + eps: float = 1e-7, +): + len_res = len(list(res)) + len_exp = len(list(expected)) + if len_res != len_exp: + raise ValueError(f"Arrays of different lengths cannot be equal. Found {len_res} != {len_exp}") + for i, (x, y) in enumerate(zip(res, expected, strict=False)): + assert abs(x - y) < eps, f"Element {i} not nearly equal in {x}, {y}" + + +def do_show(traces: dict[str, tuple[list[float], list[float]]]): + fig, ax = plt.subplots() + ax.set_title("External force frequency sweep with time between co-sim calls = 1.0") + ax.set_xlabel("frequency in Hz") + ax.set_ylabel("position/angle") + for label, trace in traces.items(): + _ = ax.plot(trace[0], trace[1], label=label) + _ = ax.legend() + plt.show() + + +# def force(t: float, ampl: float = 1.0, omega: float = 0.1): +# return np.array((0, 0, ampl * np.sin(omega * t)), dtype=float) + + +@pytest.fixture(scope="session") +def oscillator_fmu(): + return _oscillator_fmu() + + +def _oscillator_fmu(): + """Make FMU and return .fmu file with path.""" + build_path = Path.cwd() + build_path.mkdir(exist_ok=True) + src = Path(__file__).parent.parent / "examples" / "oscillator_6dof_fmu.py" + fmu_path = Model.build( + script=str(src), + dest=build_path, + newargs={ + "k": ("1N/m", "1N/m", "1N/m", "1N*m/rad", "1N*m/rad", "1N*m/rad"), + "c": ("0.1N*s/m", "0.1N*s/m", "0.1N*s/m", "0.1N*m*s/rad", "0.1N*m*s/rad", "0.1N*m*s/rad"), + "m": "1.0kg", + "mi": ("1.0 kg*m**2", "1.0 kg*m**2", "1.0 kg*m**2"), + "x0": ("0.0m", "0.0m", "0.0m", "0.0rad", "0.0rad", "0.0rad"), + "v0": ("0.0m/s", "0.0m/s", "0.0m/s", "0.0rad/s", "0.0rad/s", "0.0rad/s"), + }, + ) + return fmu_path + + +@pytest.fixture(scope="session") +def driver_fmu(): + return _driver_fmu() + + +def _driver_fmu(): + """Make FMU and return .fmu file with path.""" + src = Path(__file__).parent.parent / "examples" / "driving_force_fmu.py" + fmu_path = Model.build( + script=src, + dest=Path.cwd() / "DrivingForce6D.fmu", + newargs={"ampl": ("1.0N", "1.0N", "1.0N", "1.0N*m", "1.0N*m", "1.0N*m"), "freq": ("1.0Hz",) * 6}, + ) + return fmu_path + + +@pytest.fixture(scope="session") +def system_structure(): + return _system_structure() + + +def _system_structure(): + """Make a OSP structure file and return the path""" + shutil.copy(Path(__file__).parent.parent / "examples" / "ForcedOscillator6D.xml", Path.cwd()) + return Path.cwd() / "ForcedOscillator6D.xml" + + +def test_make_fmus( + oscillator_fmu: Path, + driver_fmu: Path, +): + info = fmu_info(filename=str(oscillator_fmu)) # this is a formatted string. Not easy to check + print(f"Info Oscillator: {info}") + val = validate_fmu(filename=str(oscillator_fmu)) + assert not len(val), f"Validation of of {oscillator_fmu.name} was not successful. Errors: {val}" + + info = fmu_info(filename=str(driver_fmu)) # this is a formatted string. Not easy to check + print(f"Info Driver: {info}") + val = validate_fmu(filename=str(driver_fmu)) + assert not len(val), f"Validation of of {driver_fmu.name} was not successful. Errors: {val}" + + +# def test_make_system_structure(system_structure: Path): +# assert Path(system_structure).exists(), "System structure not created" +# el = read_xml(Path(system_structure)) +# assert isinstance(el, ET.Element), f"ElementTree element expected. Found {el}" +# ns = el.tag.split("{")[1].split("}")[0] +# print("NS", ns, system_structure) +# for s in el.findall(".//{*}Simulator"): +# assert (Path(system_structure).parent / s.get("source", "??")).exists(), f"Component {s.get('name')} not found" +# for _con in el.findall(".//{*}VariableConnection"): +# for c in _con: +# assert c.attrib in ({"simulator": "drv", "name": "f[2]"}, {"simulator": "osc", "name": "f[2]"}) +# + + +def test_use_fmu(oscillator_fmu: Path, driver_fmu: Path, show: bool = True): + """Test single FMUs.""" + # sourcery skip: move-assign + result = simulate_fmu( + oscillator_fmu, + stop_time=50, + step_size=0.01, + validate=True, + solver="Euler", + debug_logging=True, + logger=print, # fmi_call_logger=print, + start_values={"x[2]": 1.0, "c[2]": 0.1}, + step_finished=None, # pyright: ignore[reportArgumentType] # (typing incorrect in fmpy) + fmu_instance=None, # pyright: ignore[reportArgumentType] # (typing incorrect in fmpy) + ) + if show: + plot_result(result) + + +def test_run_osp(oscillator_fmu: Path, driver_fmu: Path): + # sourcery skip: extract-duplicate-method + sim = CosimExecution.from_step_size(step_size=1e8) # empty execution object with fixed time step in nanos + osc = CosimLocalSlave(fmu_path=str(oscillator_fmu), instance_name="osc") + _osc = sim.add_local_slave(osc) + assert _osc == 0, f"local slave number {_osc}" + reference_dict = {var_ref.name.decode(): var_ref.reference for var_ref in sim.slave_variables(_osc)} + + dri = CosimLocalSlave(fmu_path=str(driver_fmu), instance_name="dri") + _dri = sim.add_local_slave(dri) + assert _dri == 1, f"local slave number {_dri}" + + # Set initial values + sim.real_initial_value(slave_index=_osc, variable_reference=reference_dict["x[2]"], value=1.0) + sim.real_initial_value(slave_index=_osc, variable_reference=reference_dict["c[2]"], value=0.1) + + sim_status = sim.status() + assert sim_status.current_time == 0 + assert CosimExecutionState(sim_status.state) == CosimExecutionState.STOPPED + + # Simulate for 1 second + _ = sim.simulate_until(target_time=15e9) + + +@pytest.mark.skipif(sys.platform.startswith("linux"), reason="HarmonicOsciallatorFMU.fmu throws an error on Linux") +def test_run_osp_system_structure(system_structure: Path, show: bool = True): + "Run an OSP simulation in the same way as the SimulatorInterface of sim-explorer is implemented" + log_output_level(CosimLogLevel.TRACE) + print("STRUCTURE", system_structure) + try: + sim = CosimExecution.from_osp_config_file(str(system_structure)) + except Exception as err: + print("ERR", err) + return + sim_status = sim.status() + assert sim_status.current_time == 0 + assert CosimExecutionState(sim_status.state) == CosimExecutionState.STOPPED + assert _slave_index(sim, "osc") == 0 + assert _slave_index(sim, "drv") == 1 + + _var_list(sim, "osc") + assert _var_ref(sim, "osc", "f[5]") == (0, 35), f"Found {_var_ref(sim, 'osc', 'f[5]')}" # last variable + assert _var_ref(sim, "drv", "v_osc[5]") == (1, 29), f"Found {_var_ref(sim, 'drv', 'v_osc[5]')}" # last variable + + # Instantiate a suitable observer for collecting results. + # Instantiate a suitable manipulator for changing variables. + manipulator = CosimManipulator.create_override() + sim.add_manipulator(manipulator=manipulator) + sim.real_initial_value(*_var_ref(sim, "osc", "c[2]"), value=0.1) + observer = CosimObserver.create_last_value() + sim.add_observer(observer=observer) + times = [] + pos = [] + speed = [] + slave, x2_ref = _var_ref(sim, "osc", "x[2]") + slave, v2_ref = _var_ref(sim, "osc", "v[2]") + for step in range(1, 1000): + time = step * 0.01 + _ = sim.simulate_until(step * 1e8) + values = observer.last_real_values(slave_index=0, variable_references=[x2_ref, v2_ref]) + times.append(time) + pos.append(values[0]) + speed.append(values[1]) + if show: + do_show(traces={"z-pos": (times, pos), "z-speed": (times, speed)}) + + +def test_system_structure_change(system_structure): + system_structure_change(system_structure, "BaseStepSize", "text", str(0.99)) + + +@pytest.mark.parametrize("alg", ["fixedStep", "ecco"]) +def test_run_osp_sweep(system_structure: Path, alg: str, show: bool = True): + _test_run_osp_sweep(system_structure, show, alg) + + +def _test_run_osp_sweep(system_structure: Path, show: bool = True, alg: str = "fixedStep"): + "Run an OSP simulation of the oscillator and the force sweep as co-simulation." + + dt = 1.0 + t_end = 100.0 + + log_output_level(CosimLogLevel.TRACE) + system_structure_change(system_structure, "BaseStepSize", "text", str(dt)) + system_structure_change(system_structure, "Algorithm", "text", alg) + print(f"Running Algorithm {alg} on {system_structure}") + assert system_structure.exists(), f"File {system_structure} not found" + sim = CosimExecution.from_osp_config_file(str(system_structure)) + sim_status = sim.status() + assert sim_status.error_code == 0 + # manipulator = CosimManipulator.create_override() + # sim.add_manipulator(manipulator=manipulator) + sim.real_initial_value(*_var_ref(sim, "osc", "k[5]"), value=1.0) + sim.real_initial_value(*_var_ref(sim, "osc", "c[5]"), value=0.1) + sim.real_initial_value(*_var_ref(sim, "osc", "m[5]"), value=1.0) + sim.real_initial_value(*_var_ref(sim, "drv", "ampl[5]"), value=1.0) + sim.real_initial_value(*_var_ref(sim, "drv", "freq[5]"), value=0.0) # freq (start frequency) + sim.real_initial_value(*_var_ref(sim, "drv", "d_freq[5]"), value=0.1 / 2 / np.pi) + sim.real_initial_value(*_var_ref(sim, "osc", "x0[2]"), value=0.0) + sim.real_initial_value(*_var_ref(sim, "osc", "v0[2]"), value=0.0) + observer = CosimObserver.create_last_value() + sim.add_observer(observer=observer) + _osc, _x5_ref = _var_ref(sim, "osc", "x[5]") + _osc, _v5_ref = _var_ref(sim, "osc", "v[5]") + times = [] + pos = [] + speed = [] + time = 0.0 + while time < t_end: + time += dt + times.append(time) + _ = sim.simulate_until(int(time * 1e9)) + values = observer.last_real_values(slave_index=_osc, variable_references=[_x5_ref, _v5_ref]) + pos.append(values[0]) + speed.append(values[1]) + if Path("oscillator_sweep0.dat").exists(): + times0, pos0, speed0, force0 = [], [], [], [] + with open("oscillator_sweep0.dat", "r") as fp: + for line in fp: + t, p, v, f = line.split("\t") + times0.append(float(t)) + pos0.append(float(p)) + speed0.append(float(v)) + force0.append(float(f)) + if show: + freq0 = [0.1 * t / 2 / np.pi for t in times0] + freq = [0.1 * t / 2 / np.pi for t in times] + do_show({"monolithic model": (freq0, pos0), "co-simulation": (freq, pos)}) + + elif show: + do_show({"z-pos": (times, pos), "z-speed": (times, speed)}) + + +if __name__ == "__main__": + retcode = pytest.main(args=["-rA", "-v", __file__, "--show", "True"]) + assert retcode == 0, f"Non-zero return code {retcode}" + import os + + os.chdir(Path(__file__).parent.absolute() / "test_working_directory") + # test_system_structure_change(_system_structure()) + # test_make_fmus(_oscillator_fmu(), _driver_fmu()) + # test_use_fmu(_oscillator_fmu(), _driver_fmu(), show=True) + # test_run_osp(_oscillator_fmu(), _driver_fmu()) + # test_run_osp_system_structure(_system_structure(), show=True) + # _test_run_osp_sweep(_system_structure(), show=True, alg="fixedStep") + # _test_run_osp_sweep( _system_structure(), show=True, alg='ecco') diff --git a/tests/test_oscillator_fmu.py b/tests/test_oscillator_fmu.py index 917166f..9a2dcff 100644 --- a/tests/test_oscillator_fmu.py +++ b/tests/test_oscillator_fmu.py @@ -153,7 +153,6 @@ def _oscillator_fmu(): src = Path(__file__).parent.parent / "examples" / "oscillator_fmu.py" fmu_path = Model.build( script=src, - project_files=[src.parent], dest=build_path, ) return fmu_path @@ -171,7 +170,6 @@ def _driver_fmu(): src = Path(__file__).parent.parent / "examples" / "driving_force_fmu.py" fmu_path = Model.build( script=src, - project_files=[], dest=build_path, newargs={"ampl": ("3N", "2N", "1N"), "freq": ("3Hz", "2Hz", "1Hz")}, ) @@ -249,8 +247,8 @@ def test_run_osp(oscillator_fmu: Path, driver_fmu: Path): assert _dri == 1, f"local slave number {_dri}" _check_var(sim, "dri", "", _dri) - _check_var(sim, "dri", "d_freq", (1, 6)) - _check_var(sim, "dri", "f[2]", (1, 9)) + _check_var(sim, "dri", "d_freq[2]", (1, 8)) + _check_var(sim, "dri", "f[2]", (1, 11)) info = _var_info(sim, "dri", "f[2]") assert info["causality"] == "OUTPUT" assert info["variability"] == "CONTINUOUS" @@ -283,7 +281,7 @@ def test_run_osp_system_structure(system_structure: Path, show: bool = True): assert CosimExecutionState(sim_status.state).name == "STOPPED" _check_var(sim, "osc", "", 0) _check_var(sim, "drv", "", 1) - #_var_list(sim, 'drv') + # _var_list(sim, 'drv') _check_var(sim, "drv", "ampl[0]", (1, 0)) _check_var(sim, "drv", "ampl[1]", (1, 1)) _check_var(sim, "drv", "ampl[2]", (1, 2)) @@ -317,14 +315,15 @@ def test_run_osp_system_structure(system_structure: Path, show: bool = True): sim.real_initial_value(*_var_ref(sim, "osc", "x[2]"), value=1.0) # x[2] observer = CosimObserver.create_last_value() sim.add_observer(observer=observer) - times : list[float] = [] - pos : list[float] = [] - speed : list[float] = [] + times: list[float] = [] + pos: list[float] = [] + speed: list[float] = [] + slave, x2_ref = _var_ref(sim, "osc", "x[2]") + slave, v2_ref = _var_ref(sim, "osc", "v[2]") for step in range(1, 1000): time = step * 0.01 _ = sim.simulate_until(step * 1e8) - return - values = observer.last_real_values(slave_index=0, variable_references=[9, 12]) # x[2], v[2] + values = observer.last_real_values(slave_index=0, variable_references=[x2_ref, v2_ref]) times.append(time) pos.append(values[0]) speed.append(values[1]) @@ -352,30 +351,32 @@ def _test_run_osp_sweep(system_structure: Path, show: bool = True, alg: str = "f system_structure_change(system_structure, "Algorithm", "text", alg) print(f"Running Algorithm {alg} on {system_structure}") assert system_structure.exists(), f"File {system_structure} not found" - simulator = CosimExecution.from_osp_config_file(str(system_structure)) - sim_status = simulator.status() + sim = CosimExecution.from_osp_config_file(str(system_structure)) + sim_status = sim.status() assert sim_status.error_code == 0 # manipulator = CosimManipulator.create_override() - # simulator.add_manipulator(manipulator=manipulator) - simulator.real_initial_value(slave_index=0, variable_reference=2, value=1.0) # k[2] - simulator.real_initial_value(slave_index=0, variable_reference=5, value=0.1) # c[2] - simulator.real_initial_value(slave_index=0, variable_reference=6, value=1.0) # m - simulator.real_initial_value(slave_index=0, variable_reference=0, value=1.0) # ampl - simulator.real_initial_value(slave_index=1, variable_reference=1, value=0.0) # freq (start frequency) - simulator.real_initial_value(slave_index=1, variable_reference=2, value=0.1 / 2 / np.pi) # d_freq - simulator.real_initial_value(slave_index=0, variable_reference=9, value=0.0) # x0[2] - simulator.real_initial_value(slave_index=0, variable_reference=12, value=0.0) # v0[2] + # sim.add_manipulator(manipulator=manipulator) + sim.real_initial_value(*_var_ref(sim, "osc", "k[2]"), value=1.0) + sim.real_initial_value(*_var_ref(sim, "osc", "c[2]"), value=0.1) + sim.real_initial_value(*_var_ref(sim, "osc", "m"), value=1.0) + sim.real_initial_value(*_var_ref(sim, "drv", "ampl[2]"), value=1.0) + sim.real_initial_value(*_var_ref(sim, "drv", "freq[2]"), value=0.0) # start frequency + sim.real_initial_value(*_var_ref(sim, "drv", "d_freq[2]"), value=0.1 / 2 / np.pi) + sim.real_initial_value(*_var_ref(sim, "osc", "x[2]"), value=0.0) + sim.real_initial_value(*_var_ref(sim, "osc", "v[2]"), value=0.0) observer = CosimObserver.create_last_value() - simulator.add_observer(observer=observer) + sim.add_observer(observer=observer) times = [] pos = [] speed = [] time = 0.0 + slave, x2_ref = _var_ref(sim, "osc", "x[2]") + slave, v2_ref = _var_ref(sim, "osc", "v[2]") while time < t_end: time += dt times.append(time) - _ = simulator.simulate_until(int(time * 1e9)) - values = observer.last_real_values(slave_index=0, variable_references=[9, 12]) # x[2], v[2] + _ = sim.simulate_until(int(time * 1e9)) + values = observer.last_real_values(slave_index=0, variable_references=[x2_ref, v2_ref]) pos.append(values[0]) speed.append(values[1]) if Path("oscillator_sweep0.dat").exists(): @@ -397,7 +398,7 @@ def _test_run_osp_sweep(system_structure: Path, show: bool = True, alg: str = "f if __name__ == "__main__": - retcode = 0 # pytest.main(args=["-rA", "-v", __file__, "--show", "True"]) + retcode = 0#pytest.main(args=["-rA", "-v", __file__, "--show", "True"]) assert retcode == 0, f"Non-zero return code {retcode}" import os @@ -406,6 +407,6 @@ def _test_run_osp_sweep(system_structure: Path, show: bool = True, alg: str = "f # test_make_fmus(_oscillator_fmu(), _driver_fmu()) # test_run_fmpy(_oscillator_fmu(), _driver_fmu(), show=True) # test_run_osp(_oscillator_fmu(), _driver_fmu()) - test_run_osp_system_structure(_system_structure(), show=True) - # test_run_osp_sweep(_system_structure(), show=True, alg="fixedStep") + # test_run_osp_system_structure(_system_structure(), show=True) + # _test_run_osp_sweep(_system_structure(), show=True, alg="fixedStep") # _test_run_osp_sweep( _system_structure(), show=True, alg='ecco') diff --git a/tests/test_utils.py b/tests/test_utils.py index 46ffe39..233697f 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -77,7 +77,7 @@ def test_model_description(bouncing_ball_fmu): "canNotUseMemoryManagementFunctions": "true", }, ) - assert el.find("./SourceFiles") is not None, "SourceFiles expected" + #assert el.find("./SourceFiles") is not None, "SourceFiles expected" el = et.find("./UnitDefinitions") assert el is not None, "UnitDefinitions element expected" assert len(el) == 4, f"4 UnitDefinitions expected. Found {el}" @@ -152,9 +152,9 @@ def test_variables_from_fmu(bouncing_ball_fmu): if __name__ == "__main__": - retcode = 0 # pytest.main(["-rA", "-v", __file__]) + retcode = pytest.main(["-rA", "-v", __file__]) assert retcode == 0, f"Non-zero return code {retcode}" # import os # os.chdir( Path(__file__).parent / "test_working_directory") # test_model_from_fmu(_bouncing_ball_fmu()) - test_model_description(_bouncing_ball_fmu()) + # test_model_description(_bouncing_ball_fmu()) From c59b1cb4b961da0c1676201173321f8be653b7fd Mon Sep 17 00:00:00 2001 From: Mendez Date: Fri, 4 Jul 2025 14:10:07 +0200 Subject: [PATCH 11/29] Fix ruff and mypy issues --- .github/workflows/_code_quality.yml | 2 +- tests/test_oscillator_fmu.py | 2 +- tests/test_utils.py | 2 +- uv.lock | 254 ++++------------------------ 4 files changed, 38 insertions(+), 222 deletions(-) diff --git a/.github/workflows/_code_quality.yml b/.github/workflows/_code_quality.yml index a036e77..a1df23e 100644 --- a/.github/workflows/_code_quality.yml +++ b/.github/workflows/_code_quality.yml @@ -78,6 +78,6 @@ jobs: python-version: "3.12.8" python-version-file: "pyproject.toml" - name: Install the project - run: uv sync --upgrade + run: uv sync --upgrade --extra modelTest - name: Run mypy run: uv run mypy diff --git a/tests/test_oscillator_fmu.py b/tests/test_oscillator_fmu.py index 9a2dcff..f3f6414 100644 --- a/tests/test_oscillator_fmu.py +++ b/tests/test_oscillator_fmu.py @@ -398,7 +398,7 @@ def _test_run_osp_sweep(system_structure: Path, show: bool = True, alg: str = "f if __name__ == "__main__": - retcode = 0#pytest.main(args=["-rA", "-v", __file__, "--show", "True"]) + retcode = 0 # pytest.main(args=["-rA", "-v", __file__, "--show", "True"]) assert retcode == 0, f"Non-zero return code {retcode}" import os diff --git a/tests/test_utils.py b/tests/test_utils.py index 233697f..68e104e 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -77,7 +77,7 @@ def test_model_description(bouncing_ball_fmu): "canNotUseMemoryManagementFunctions": "true", }, ) - #assert el.find("./SourceFiles") is not None, "SourceFiles expected" + # assert el.find("./SourceFiles") is not None, "SourceFiles expected" el = et.find("./UnitDefinitions") assert el is not None, "UnitDefinitions element expected" assert len(el) == 4, f"4 UnitDefinitions expected. Found {el}" diff --git a/uv.lock b/uv.lock index 444e8ea..c7b4585 100644 --- a/uv.lock +++ b/uv.lock @@ -24,27 +24,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3b/00/2344469e2084fb287c2e0b57b72910309874c3245463acd6cf5e3db69324/appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128", size = 9566 }, ] -[[package]] -name = "astroid" -version = "3.3.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/39/33/536530122a22a7504b159bccaf30a1f76aa19d23028bd8b5009eb9b2efea/astroid-3.3.9.tar.gz", hash = "sha256:622cc8e3048684aa42c820d9d218978021c3c3d174fb03a9f0d615921744f550", size = 398731 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/80/c749efbd8eef5ea77c7d6f1956e8fbfb51963b7f93ef79647afd4d9886e3/astroid-3.3.9-py3-none-any.whl", hash = "sha256:d05bfd0acba96a7bd43e222828b7d9bc1e138aaeb0649707908d3702a9831248", size = 275339 }, -] - -[[package]] -name = "asttokens" -version = "3.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918 }, -] - [[package]] name = "attrs" version = "25.3.0" @@ -174,21 +153,18 @@ dependencies = [ { name = "libcosimpy" }, { name = "matplotlib" }, { name = "numpy" }, - { name = "pdfminer" }, { name = "pint" }, - { name = "pypdf2" }, + { name = "plotly" }, { name = "pythonfmu" }, { name = "scipy" }, { name = "sympy" }, ] [package.optional-dependencies] -editor = [ - { name = "thonny" }, -] modeltest = [ { name = "fmpy" }, { name = "matplotlib" }, + { name = "plotly" }, ] rest = [ { name = "docutils" }, @@ -219,17 +195,16 @@ requires-dist = [ { name = "flexparser", specifier = "<0.4" }, { name = "fmpy", marker = "extra == 'modeltest'", specifier = "==0.3.21" }, { name = "jsonpath-ng", specifier = ">=1.7.0" }, - { name = "libcosimpy", specifier = ">=0.0.2" }, + { name = "libcosimpy", git = "https://github.com/open-simulation-platform/libcosimpy?rev=cosimc-loading-fix" }, { name = "matplotlib", specifier = ">=3.9.1" }, { name = "matplotlib", marker = "extra == 'modeltest'", specifier = ">=3.9.1" }, { name = "numpy", specifier = ">=1.26,<2.0" }, - { name = "pdfminer", specifier = ">=20191125" }, { name = "pint", specifier = ">=0.24" }, - { name = "pypdf2", specifier = ">=3.0.1" }, - { name = "pythonfmu", specifier = ">=0.6.6" }, + { name = "plotly", specifier = ">=6.0.1" }, + { name = "plotly", marker = "extra == 'modeltest'", specifier = ">=6.0.1" }, + { name = "pythonfmu", specifier = ">=0.6.7" }, { name = "scipy", specifier = ">=1.15.1" }, { name = "sympy", specifier = ">=1.13.3" }, - { name = "thonny", marker = "extra == 'editor'", specifier = ">=4.1" }, ] [package.metadata.requires-dev] @@ -387,15 +362,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 }, ] -[[package]] -name = "dill" -version = "0.3.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/70/43/86fe3f9e130c4137b0f1b50784dd70a5087b911fe07fa81e53e0c4c47fea/dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c", size = 187000 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/46/d1/e73b6ad76f0b1fb7f23c35c6d95dbc506a9c8804f43dda8cb5b0fa6331fd/dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a", size = 119418 }, -] - [[package]] name = "distlib" version = "0.3.9" @@ -458,7 +424,7 @@ wheels = [ [[package]] name = "fmpy" -version = "0.3.22" +version = "0.3.21" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -470,7 +436,7 @@ dependencies = [ { name = "pywin32", marker = "sys_platform == 'win32'" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/86/87a735aa1177be40e0fdc01ba87509eec6f5a80d7738cd84a732ba3bae23/FMPy-0.3.22-py3-none-any.whl", hash = "sha256:d280f63852cfc571341648c73653f2e4aec28d33f98189515cc34bf696feb88c", size = 4891796 }, + { url = "https://files.pythonhosted.org/packages/22/ee/974c39c5476ce20342a38483060450bd75f3cd5bb7b2c7ba712d63e763e3/FMPy-0.3.21-py3-none-any.whl", hash = "sha256:77d6896cbd4737ad8e11a185bd3eb54f1c71d3e22c6308b4e8ed55e39142fe80", size = 6721581 }, ] [[package]] @@ -566,27 +532,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 }, ] -[[package]] -name = "isort" -version = "6.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b8/21/1e2a441f74a653a144224d7d21afe8f4169e6c7c20bb13aec3a2dc3815e0/isort-6.0.1.tar.gz", hash = "sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450", size = 821955 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/11/114d0a5f4dabbdcedc1125dee0888514c3c3b16d3e9facad87ed96fad97c/isort-6.0.1-py3-none-any.whl", hash = "sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615", size = 94186 }, -] - -[[package]] -name = "jedi" -version = "0.19.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "parso" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278 }, -] - [[package]] name = "jinja2" version = "3.1.6" @@ -709,16 +654,8 @@ wheels = [ [[package]] name = "libcosimpy" -version = "0.0.2" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/b8/89b64b5920439bcaab95ce52686bb2bbc22c4cd72683e94f669761293619/libcosimpy-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb58d5fb9cf9ee8fa460e1cbce66ebe94cfb2719181333dc7435e80727509135", size = 23413889 }, - { url = "https://files.pythonhosted.org/packages/6c/9d/3a5004840737c1cc84874757b43a0cb5f259518200450faf8986addf4d18/libcosimpy-0.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:ba2e82245425066464297325c6074bb6829c8282dff1057f9ad28f2df97171e1", size = 4474674 }, - { url = "https://files.pythonhosted.org/packages/21/1d/74a1b078692b453022228d0a73695302d764b7d7e69b59ea1a478ab388c4/libcosimpy-0.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:439198d6ff92521df2fb705f1c0516886854ab7c9d53e2c86c2a8e37422f5dd0", size = 23413885 }, - { url = "https://files.pythonhosted.org/packages/05/99/93fd9c6b0b290a31e29287f3b0721171033f64d7636f7c7b1e9192fd2dbb/libcosimpy-0.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:93c5acf67f95a53e9ce9a27aafa863de9461a04a04933652b2991c0b0e52b3d2", size = 4474674 }, - { url = "https://files.pythonhosted.org/packages/06/e7/3dc745a7d41e563c5c50daad1198c86eb463cb522b9f90599dad4445e3b4/libcosimpy-0.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f94574843496e9692c86c955989cd411c749d6454b4fd891a3881282682b4bc7", size = 23413888 }, - { url = "https://files.pythonhosted.org/packages/69/c0/fb50b030b7b2af61b22bf21519b6f50f7e2c3b1b54472f7b8b23627394d4/libcosimpy-0.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:aeeef8d4fbcabf257a512ea2d626b00d281512204da19b9c07b83fe28ab4e9b2", size = 4474675 }, -] +version = "0.0.3.post2" +source = { git = "https://github.com/open-simulation-platform/libcosimpy?rev=cosimc-loading-fix#f7c6eb5ad2bdf67807834c1321927468c188753f" } [[package]] name = "lxml" @@ -924,15 +861,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fd/7d/6a8b31dd07ed856b3eae001c9129670ef75c4698fa1c2a6ac9f00a4a7054/matplotlib-3.10.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3809916157ba871bcdd33d3493acd7fe3037db5daa917ca6e77975a94cef779", size = 8590087 }, ] -[[package]] -name = "mccabe" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350 }, -] - [[package]] name = "mdit-py-plugins" version = "0.4.2" @@ -1080,6 +1008,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5f/df/76d0321c3797b54b60fef9ec3bd6f4cfd124b9e422182156a1dd418722cf/myst_parser-4.0.1-py3-none-any.whl", hash = "sha256:9134e88959ec3b5780aedf8a99680ea242869d012e8821db3126d427edc9c95d", size = 84579 }, ] +[[package]] +name = "narwhals" +version = "1.45.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/9f/284886c5cea849b4ed1c55babcb48cb1084886139e8ac31e9849112ce6d0/narwhals-1.45.0.tar.gz", hash = "sha256:f9ecefb9d09cda6fefa8ead10dc37a79129b6c78b0ac7117d21b4d4486bdd0d1", size = 508812 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/a4/337a229d184b23ee63e6b730ac1588d77067af77c550dbf69cf1d74c3298/narwhals-1.45.0-py3-none-any.whl", hash = "sha256:0585612aa7ec89f9d061e78410b6fb8772794389d1a29d5799572d6b81999497", size = 371633 }, +] + [[package]] name = "nodeenv" version = "1.9.1" @@ -1130,24 +1067,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, ] -[[package]] -name = "parso" -version = "0.8.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/66/94/68e2e17afaa9169cf6412ab0f28623903be73d1b32e208d9e8e541bb086d/parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d", size = 400609 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650 }, -] - -[[package]] -name = "pdfminer" -version = "20191125" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycryptodome" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/a3/155c5cde5f9c0b1069043b2946a93f54a41fd72cc19c6c100f6f2f5bdc15/pdfminer-20191125.tar.gz", hash = "sha256:9e700bc731300ed5c8936343c1dd4529638184198e54e91dd2b59b64a755dc01", size = 4173248 } - [[package]] name = "pillow" version = "11.1.0" @@ -1239,6 +1158,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6d/45/59578566b3275b8fd9157885918fcd0c4d74162928a5310926887b856a51/platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94", size = 18499 }, ] +[[package]] +name = "plotly" +version = "6.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "narwhals" }, + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6e/5c/0efc297df362b88b74957a230af61cd6929f531f72f48063e8408702ffba/plotly-6.2.0.tar.gz", hash = "sha256:9dfa23c328000f16c928beb68927444c1ab9eae837d1fe648dbcda5360c7953d", size = 6801941 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/20/f2b7ac96a91cc5f70d81320adad24cc41bf52013508d649b1481db225780/plotly-6.2.0-py3-none-any.whl", hash = "sha256:32c444d4c940887219cb80738317040363deefdfee4f354498cc0b6dab8978bd", size = 9635469 }, +] + [[package]] name = "pluggy" version = "1.5.0" @@ -1273,29 +1205,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/74/a88bf1b1efeae488a0c0b7bdf71429c313722d1fc0f377537fbe554e6180/pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd", size = 220707 }, ] -[[package]] -name = "pycryptodome" -version = "3.22.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/44/e6/099310419df5ada522ff34ffc2f1a48a11b37fc6a76f51a6854c182dbd3e/pycryptodome-3.22.0.tar.gz", hash = "sha256:fd7ab568b3ad7b77c908d7c3f7e167ec5a8f035c64ff74f10d47a4edd043d723", size = 4917300 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/65/a05831c3e4bcd1bf6c2a034e399f74b3d6f30bb4e37e36b9c310c09dc8c0/pycryptodome-3.22.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:009e1c80eea42401a5bd5983c4bab8d516aef22e014a4705622e24e6d9d703c6", size = 2490637 }, - { url = "https://files.pythonhosted.org/packages/5c/76/ff3c2e7a60d17c080c4c6120ebaf60f38717cd387e77f84da4dcf7f64ff0/pycryptodome-3.22.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3b76fa80daeff9519d7e9f6d9e40708f2fce36b9295a847f00624a08293f4f00", size = 1635372 }, - { url = "https://files.pythonhosted.org/packages/cc/7f/cc5d6da0dbc36acd978d80a72b228e33aadaec9c4f91c93221166d8bdc05/pycryptodome-3.22.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a31fa5914b255ab62aac9265654292ce0404f6b66540a065f538466474baedbc", size = 2177456 }, - { url = "https://files.pythonhosted.org/packages/92/65/35f5063e68790602d892ad36e35ac723147232a9084d1999630045c34593/pycryptodome-3.22.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0092fd476701eeeb04df5cc509d8b739fa381583cda6a46ff0a60639b7cd70d", size = 2263744 }, - { url = "https://files.pythonhosted.org/packages/cc/67/46acdd35b1081c3dbc72dc466b1b95b80d2f64cad3520f994a9b6c5c7d00/pycryptodome-3.22.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18d5b0ddc7cf69231736d778bd3ae2b3efb681ae33b64b0c92fb4626bb48bb89", size = 2303356 }, - { url = "https://files.pythonhosted.org/packages/3d/f9/a4f8a83384626098e3f55664519bec113002b9ef751887086ae63a53135a/pycryptodome-3.22.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f6cf6aa36fcf463e622d2165a5ad9963b2762bebae2f632d719dfb8544903cf5", size = 2176714 }, - { url = "https://files.pythonhosted.org/packages/88/65/e5f8c3a885f70a6e05c84844cd5542120576f4369158946e8cfc623a464d/pycryptodome-3.22.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:aec7b40a7ea5af7c40f8837adf20a137d5e11a6eb202cde7e588a48fb2d871a8", size = 2337329 }, - { url = "https://files.pythonhosted.org/packages/b8/2a/25e0be2b509c28375c7f75c7e8d8d060773f2cce4856a1654276e3202339/pycryptodome-3.22.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d21c1eda2f42211f18a25db4eaf8056c94a8563cd39da3683f89fe0d881fb772", size = 2262255 }, - { url = "https://files.pythonhosted.org/packages/41/58/60917bc4bbd91712e53ce04daf237a74a0ad731383a01288130672994328/pycryptodome-3.22.0-cp37-abi3-win32.whl", hash = "sha256:f02baa9f5e35934c6e8dcec91fcde96612bdefef6e442813b8ea34e82c84bbfb", size = 1763403 }, - { url = "https://files.pythonhosted.org/packages/55/f4/244c621afcf7867e23f63cfd7a9630f14cfe946c9be7e566af6c3915bcde/pycryptodome-3.22.0-cp37-abi3-win_amd64.whl", hash = "sha256:d086aed307e96d40c23c42418cbbca22ecc0ab4a8a0e24f87932eeab26c08627", size = 1794568 }, - { url = "https://files.pythonhosted.org/packages/37/c3/e3423e72669ca09f141aae493e1feaa8b8475859898b04f57078280a61c4/pycryptodome-3.22.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b4bdce34af16c1dcc7f8c66185684be15f5818afd2a82b75a4ce6b55f9783e13", size = 1618698 }, - { url = "https://files.pythonhosted.org/packages/f9/b7/35eec0b3919cafea362dcb68bb0654d9cb3cde6da6b7a9d8480ce0bf203a/pycryptodome-3.22.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2988ffcd5137dc2d27eb51cd18c0f0f68e5b009d5fec56fbccb638f90934f333", size = 1666957 }, - { url = "https://files.pythonhosted.org/packages/b0/1f/f49bccdd8d61f1da4278eb0d6aee7f988f1a6ec4056b0c2dc51eda45ae27/pycryptodome-3.22.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e653519dedcd1532788547f00eeb6108cc7ce9efdf5cc9996abce0d53f95d5a9", size = 1659242 }, - { url = "https://files.pythonhosted.org/packages/95/43/a01dcf1ed39c9a9e9c9d3f9d98040deeceedaa7cdf043e175251f2e13f44/pycryptodome-3.22.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5810bc7494e4ac12a4afef5a32218129e7d3890ce3f2b5ec520cc69eb1102ad", size = 1697246 }, - { url = "https://files.pythonhosted.org/packages/3b/49/195842931f9ee6f14cd63ef85e06b93073463ed59601fb283ba9b813cd53/pycryptodome-3.22.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e7514a1aebee8e85802d154fdb261381f1cb9b7c5a54594545145b8ec3056ae6", size = 1797436 }, -] - [[package]] name = "pygments" version = "2.19.1" @@ -1305,25 +1214,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, ] -[[package]] -name = "pylint" -version = "3.3.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "astroid" }, - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "dill" }, - { name = "isort" }, - { name = "mccabe" }, - { name = "platformdirs" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "tomlkit" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/69/a7/113d02340afb9dcbb0c8b25454e9538cd08f0ebf3e510df4ed916caa1a89/pylint-3.3.6.tar.gz", hash = "sha256:b634a041aac33706d56a0d217e6587228c66427e20ec21a019bc4cdee48c040a", size = 1519586 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/31/21/9537fc94aee9ec7316a230a49895266cf02d78aa29b0a2efbc39566e0935/pylint-3.3.6-py3-none-any.whl", hash = "sha256:8b7c2d3e86ae3f94fb27703d521dd0b9b6b378775991f504d7c3a6275aa0a6a6", size = 522462 }, -] - [[package]] name = "pyparsing" version = "3.2.3" @@ -1333,15 +1223,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf", size = 111120 }, ] -[[package]] -name = "pypdf2" -version = "3.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9f/bb/18dc3062d37db6c491392007dfd1a7f524bb95886eb956569ac38a23a784/PyPDF2-3.0.1.tar.gz", hash = "sha256:a74408f69ba6271f71b9352ef4ed03dc53a31aa404d29b5d31f53bfecfee1440", size = 227419 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/5e/c86a5643653825d3c913719e788e41386bee415c2b87b4f955432f2de6b2/pypdf2-3.0.1-py3-none-any.whl", hash = "sha256:d16e4205cfee272fbdc0568b68d82be796540b1537508cef59388f839c191928", size = 232572 }, -] - [[package]] name = "pyright" version = "1.1.398" @@ -1355,15 +1236,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/58/e0/5283593f61b3c525d6d7e94cfb6b3ded20b3df66e953acaf7bb4f23b3f6e/pyright-1.1.398-py3-none-any.whl", hash = "sha256:0a70bfd007d9ea7de1cf9740e1ad1a40a122592cfe22a3f6791b06162ad08753", size = 5780235 }, ] -[[package]] -name = "pyserial" -version = "3.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1e/7d/ae3f0a63f41e4d2f6cb66a5b57197850f919f59e558159a4dd3a818f5082/pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb", size = 159125 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/bc/587a445451b253b285629263eb51c2d8e9bcea4fc97826266d186f96f558/pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0", size = 90585 }, -] - [[package]] name = "pytest" version = "8.3.5" @@ -1408,11 +1280,11 @@ wheels = [ [[package]] name = "pythonfmu" -version = "0.6.6" +version = "0.6.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ef/f2/0f12b4fce8f88e68b87c88d497d51fb141760c7968c93e120e2b9b97e948/pythonfmu-0.6.6.tar.gz", hash = "sha256:a2d0043cfd843c75842085aa1273cc5f6cba3f6e3fcab23cfb1f9928d109c0f5", size = 342602 } +sdist = { url = "https://files.pythonhosted.org/packages/7f/22/08cdfef2fdc9a451ddaa35aaba05643184d3e709f67ded6d142aaf469e8c/pythonfmu-0.6.9.tar.gz", hash = "sha256:13eab981d3c86704ab19b35ebcee9347b2dcc378bbe8d0b43c9ae1fe35bc9fa7", size = 244885 } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/45/cdc2ca486d272fa6fbb4637ab0482c13e013988f93a07bc383ad99f11b3a/pythonfmu-0.6.6-py3-none-any.whl", hash = "sha256:32b0f5384002aea8dc24ee55e85f9f2916436158edfbf7bdba4f503c2d5f90bf", size = 357936 }, + { url = "https://files.pythonhosted.org/packages/1b/11/73c37bfb38f0feaab0e0054de5afd9518da21095f371b7beb58183f5bf2b/pythonfmu-0.6.9-py3-none-any.whl", hash = "sha256:9b196f1e4b76ec67197cbf70e93452f1595b2459ed735fa4488ada913d713949", size = 259448 }, ] [[package]] @@ -1583,24 +1455,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0a/c8/b3f566db71461cabd4b2d5b39bcc24a7e1c119535c8361f81426be39bb47/scipy-1.15.2-cp313-cp313t-win_amd64.whl", hash = "sha256:fe8a9eb875d430d81755472c5ba75e84acc980e4a8f6204d402849234d3017db", size = 40477705 }, ] -[[package]] -name = "send2trash" -version = "1.8.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/3a/aec9b02217bb79b87bbc1a21bc6abc51e3d5dcf65c30487ac96c0908c722/Send2Trash-1.8.3.tar.gz", hash = "sha256:b18e7a3966d99871aefeb00cfbcfdced55ce4871194810fc71f4aa484b953abf", size = 17394 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/40/b0/4562db6223154aa4e22f939003cb92514c79f3d4dccca3444253fd17f902/Send2Trash-1.8.3-py3-none-any.whl", hash = "sha256:0c31227e0bd08961c7665474a3d1ef7193929fedda4233843689baa056be46c9", size = 18072 }, -] - -[[package]] -name = "setuptools" -version = "78.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/5a/0db4da3bc908df06e5efae42b44e75c81dd52716e10192ff36d0c1c8e379/setuptools-78.1.0.tar.gz", hash = "sha256:18fd474d4a82a5f83dac888df697af65afa82dec7323d09c3e37d1f14288da54", size = 1367827 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/21/f43f0a1fa8b06b32812e0975981f4677d28e0f3271601dc88ac5a5b83220/setuptools-78.1.0-py3-none-any.whl", hash = "sha256:3e386e96793c8702ae83d17b853fb93d3e09ef82ec62722e61da5cd22376dcd8", size = 1256108 }, -] - [[package]] name = "six" version = "1.17.0" @@ -1838,26 +1692,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/99/ff/c87e0622b1dadea79d2fb0b25ade9ed98954c9033722eb707053d310d4f3/sympy-1.13.3-py3-none-any.whl", hash = "sha256:54612cf55a62755ee71824ce692986f23c88ffa77207b30c1368eda4a7060f73", size = 6189483 }, ] -[[package]] -name = "thonny" -version = "4.1.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "asttokens" }, - { name = "docutils" }, - { name = "jedi" }, - { name = "mypy" }, - { name = "pylint" }, - { name = "pyserial" }, - { name = "send2trash" }, - { name = "setuptools" }, - { name = "wheel" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/49/9d/4f51f79865f293024d601f9d6e8de367164f59d6892e4635bec0f9692a4e/thonny-4.1.7.tar.gz", hash = "sha256:6b3b5605f524c12462830e232c8eaf01235645820dd13c24d720acab44a58123", size = 2177370 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/59/f9d2960539042303dcc7a3fce00d2ee4f24ab117c4ebf8ef8115de2f160e/thonny-4.1.7-py3-none-any.whl", hash = "sha256:1364d16f71071cc1dc56ed844f77b1981f1ef0b87f787aabaec2d228bea1c3e3", size = 2528987 }, -] - [[package]] name = "tomli" version = "2.2.1" @@ -1897,15 +1731,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, ] -[[package]] -name = "tomlkit" -version = "0.13.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b1/09/a439bec5888f00a54b8b9f05fa94d7f901d6735ef4e55dcec9bc37b5d8fa/tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79", size = 192885 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/b6/a447b5e4ec71e13871be01ba81f5dfc9d0af7e473da256ff46bc0e24026f/tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", size = 37955 }, -] - [[package]] name = "typing-extensions" version = "4.13.0" @@ -1937,12 +1762,3 @@ sdist = { url = "https://files.pythonhosted.org/packages/c7/9c/57d19fa093bcf5ac6 wheels = [ { url = "https://files.pythonhosted.org/packages/c2/eb/c6db6e3001d58c6a9e67c74bb7b4206767caa3ccc28c6b9eaf4c23fb4e34/virtualenv-20.29.3-py3-none-any.whl", hash = "sha256:3e3d00f5807e83b234dfb6122bf37cfadf4be216c53a49ac059d02414f819170", size = 4301458 }, ] - -[[package]] -name = "wheel" -version = "0.45.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8a/98/2d9906746cdc6a6ef809ae6338005b3f21bb568bea3165cfc6a243fdc25c/wheel-0.45.1.tar.gz", hash = "sha256:661e1abd9198507b1409a20c02106d9670b2576e916d58f520316666abca6729", size = 107545 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/2c/87f3254fd8ffd29e4c02732eee68a83a1d3c346ae39bc6822dcbcb697f2b/wheel-0.45.1-py3-none-any.whl", hash = "sha256:708e7481cc80179af0e556bbf0cc00b8444c7321e2700b8d8580231d13017248", size = 72494 }, -] From 66f0fa50a93a89139b06514587cd12e7be736308 Mon Sep 17 00:00:00 2001 From: Eisinger Date: Fri, 4 Jul 2025 16:49:52 +0200 Subject: [PATCH 12/29] Added ForcedOscillator6D.xml to the project --- examples/ForcedOscillator6D.xml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 examples/ForcedOscillator6D.xml diff --git a/examples/ForcedOscillator6D.xml b/examples/ForcedOscillator6D.xml new file mode 100644 index 0000000..f730641 --- /dev/null +++ b/examples/ForcedOscillator6D.xml @@ -0,0 +1,31 @@ + + 0.0 + 0.01 + fixedStep + + + + + + + + + + + + + + + + 0.99 + 0.0001 + 0.00001 + 0.01 + 0.2 + 1.5 + 0.2 + 0.15 + 1e-6 + 1e-6 + + \ No newline at end of file From 35f655c0bf3490085c70b011b9ebdd135167ee7f Mon Sep 17 00:00:00 2001 From: Eisinger Date: Thu, 23 Oct 2025 15:07:12 +0200 Subject: [PATCH 13/29] Minor chagnes in relation with updates on the crane-fmu package --- src/component_model/model.py | 9 +- src/component_model/variable.py | 154 +++++++++++++++++++++++++++++--- tests/test_oscillator.py | 48 +++++++++- tests/test_oscillator_fmu.py | 2 +- tests/test_variable.py | 102 ++++++++++++++++++++- tests/test_variable_naming.py | 1 + 6 files changed, 292 insertions(+), 24 deletions(-) diff --git a/src/component_model/model.py b/src/component_model/model.py index a1a92e9..ffe0b31 100644 --- a/src/component_model/model.py +++ b/src/component_model/model.py @@ -71,8 +71,6 @@ class Model(Fmi2Slave): * TypeDefinitions. Instead of defining SimpleType variables, ScalarVariable variables are always based on the pre-defined types and details provided there. - * Explicit (see ) are so far not implemented. - This could be done as an additional (optional) property in Variable. Args: name (str): name of the model. The name is also used to construct the FMU file name. @@ -107,7 +105,7 @@ def __init__( copyright: str | None = None, default_experiment: dict[str, float] | None = None, flags: dict | None = None, - guid=None, + guid : str = None, **kwargs, ): kwargs.update( @@ -123,8 +121,6 @@ def __init__( self.name = name if "instance_name" not in kwargs: # NOTE: within builder.py this is always changed to 'dummyInstance' kwargs["instance_name"] = self.name # make_instancename(__name__) - if "resources" not in kwargs: - kwargs["resources"] = None super().__init__(**kwargs) # in addition, OrderedDict vars is initialized # Additional variables which are hidden here: .vars, # PythonFMU sets the default_experiment and the following items always to None! Correct it here @@ -164,7 +160,7 @@ def enter_initialization_mode(self): def exit_initialization_mode(self): """Initialize the model after initial variables are set.""" - super().exit_initialization_mode() + #super().exit_initialization_mode() self.dirty_do() # run on_set on all dirty variables @abstractmethod # mark the class as 'still abstract' @@ -793,6 +789,7 @@ def _set(self, vrs: Sequence[int], values: Sequence[int | float | bool | str], t Variable range check, unit check and type check are performed by setter() function. on_set (if defined) is only run if the whole variable (all elements) are set. """ + print(f"Set {vrs} to {values}. Type {typ.__qualname__}") for var, sv, svr in self._vrs_slices(vrs): assert isinstance(var, Variable) assert isinstance(var.typ, type) diff --git a/src/component_model/variable.py b/src/component_model/variable.py index 1763da8..8fe3ebc 100644 --- a/src/component_model/variable.py +++ b/src/component_model/variable.py @@ -4,10 +4,11 @@ import xml.etree.ElementTree as ET # noqa: N817 from enum import Enum, IntFlag from functools import partial -from math import acos, atan2, cos, degrees, radians, sin, sqrt +from math import acos, atan2, cos, degrees, radians, sin, sqrt, isinf from typing import Any, Callable, Sequence, TypeAlias import numpy as np +from scipy.spatial.transform import Rotation as Rot from pint import Quantity, UnitRegistry # management of units from pythonfmu.enums import Fmi2Causality as Causality # type: ignore from pythonfmu.enums import Fmi2Initial as Initial # type: ignore @@ -303,14 +304,14 @@ class Variable(ScalarVariable): and do not need to be repeated on every simulation step. If given, the function shall apply to the whole (vecor) variable, and after unit conversion and range checking. - The function is completely invisible by the user specifying inputs to the variable. + The function is invisible by the user specifying inputs to the variable. owner = None: Optional possibility to overwrite the default owner. If the related model uses structured variable naming this should not be necessary, but for flat variable naming within complex models (not recommended) ownership setting might be necessary. local_name (str) = None: Optional possibility to overwrite the automatic determination of local_name, which is used to access the variable value and must be a property of owner. - This is convenient for example to link a derivative name to a variable if the default name der_ - is not acceptable. + This is convenient for example to link a derivative name to a variable if the default name, + i.e. der_ is not acceptable. """ def __init__( @@ -417,15 +418,15 @@ def der1(self, current_time: float, step_size: float): isinstance(der, (Sequence, np.ndarray)) and any(x != 0.0 for x in der) ): # there is a slope varname = self.local_name[5:] # local name of the base variable - val = getattr(self.owner, varname) # previous value of base variable + basevar = self.model.derivatives[self.name] # base variable object + val = getattr(self.owner, basevar.local_name) #getattr(self.owner, varname) # previous value of base variable # if not isinstance(der, (Sequence, np.ndarray)): der = [der] assert not isinstance(val, (Sequence, np.ndarray)), "Should be the same as der" val = [val] - is_nparray = isinstance(val, np.ndarray) - basevar = self.model.derivatives[self.name] # base variable object - if is_nparray: - basevar.setter_internal(val + step_size * np.array(der, float), -1, True) + if isinstance(val, np.ndarray): + newval = val + step_size * np.array(der, float) + basevar.setter_internal(newval, -1, True) else: newval = [val[i] + step_size * der[i] for i in range(len(der))] basevar.setter_internal(newval, -1, False) @@ -504,11 +505,11 @@ def setter(self, values: Sequence[int | float | bool | str | Enum] | np.ndarray, for i in range(self._len): values[i] = self._typ(values[i]) # type: ignore - if self._check & Check.ranges: # do that before unit conversion, since range is stored in display units! + if True:#??self._check & Check.ranges: # do that before unit conversion, since range is stored in display units! if not self.check_range(values, idx): raise VariableRangeError(f"set(): values {values} outside range.") from None - if self._check & Check.units: #'values' expected as displayUnit. Convert to unit + if True: #?? self._check & Check.units: #'values' expected as displayUnit. Convert to unit if idx >= 0: # explicit index of single values if self._unit[idx].du is None: dvals = list(values) @@ -559,6 +560,7 @@ def setter_internal( setattr(self.owner, self.local_name, values if self.on_set is None else self.on_set(values)) if self.on_set is None: logger.debug(f"SETTER {self.name}, {values}[{idx}] => {getattr(self.owner, self.local_name)}") + logger.info(f"Set variable {self.name}->{values}") def getter(self) -> list[PyType]: """Get the value (output a value from the model), including range checking and unit conversion. @@ -915,6 +917,129 @@ def cylindrical_to_cartesian(vec: np.ndarray | tuple, deg: bool = False) -> np.n phi = radians(vec[1]) if deg else vec[1] return np.array((vec[0] * cos(phi), vec[0] * sin(phi), vec[2]), dtype="float") +def euler_rot_spherical( + rpy: Sequence|Rot, + vec: Sequence | None = None, + seq: str = 'XYZ', # sequence of axis of rotation as defined in scipy Rotation object + degrees:bool=False) -> Sequence: + """ Rotate the spherical vector vec using the Euler angles (yaw,pitch,roll). + + Args: + rpy (Sequence|Rotation): The sequence of (yaw,pitch,roll) Euler angles or the pre-calculated Rotation object + vec: (Sequence): the spherical vector to be rotated + None: Use unit vector in z-direction, i.e. (1,0,0) + 2-sequence: only polar and azimuth provided and returned => (polar,azimuth) + 3-sequence: (r,polar,azimuth) + seq (str) = 'XYZ': Sequence of rotations as defined in scipy.spatial.transform.Rotation.from_euler() + degrees (bool): angles optionally provided in degrees. Default: radians + Returns: + The rotated vector in spherical coordinates (radius only if 3-vector is provided) + """ + if isinstance(rpy, Rot): + r = rpy + elif isinstance( rpy, (Sequence,np.ndarray)): + r = Rot.from_euler(seq, (rpy[0], rpy[1], rpy[2]), degrees) #0: roll, 1: pitch, 2: yaw + else: + raise NotImplementedError(f"Unknown object {rpy} to rotate") from None + if vec is None: + tp = (0,0) + else: # explicit vector provided + if len(vec)==3: + radius = vec[0] + tp = vec[1:] + else: + tp = vec + if degrees: + tp = np.radians( tp) + st = np.sin(tp[0]) + x = r.apply( (st*np.cos(tp[1]), st*np.sin(tp[1]), np.cos(tp[0]))) # rotate the cartesian vector + x2 = x[2] + if abs(x2) < 1.0: + pass + elif abs(x2-1.0) < 1e-10: + x2 = 1.0 + elif abs(x2+1.0) < 1e-10: + x2 = -1.0 + else: + raise ValueError(f"Invalid argument {x2} for arccos calculation") from None + if abs(x[0])<1e-10 and abs(x[1])<1e-10: # define the normally undefined arctan + phi = 0.0 + else: + phi = np.arctan2( x[1], x[0]) + if vec is not None and len(vec) == 3: + return (radius, np.arccos( x2), phi) # return the spherical vector + else: + return (np.arccos( x2), phi) # return only the direction + +def rot_from_spherical( vec : Sequence | np.ndarray, degrees:bool = False): + """Return a scipy Rotation object from the spherical coordinates vec, + i.e. the rotation which turns a vector along the z-axis into vec. + + Args: + vec (Sequence | np.ndarray): a spherical vector as 3D or 2D (radius omitted) + degrees (bool): optional possibility to provide angles in degrees + """ + angle = vec[1:] if len(vec)==3 else vec + return Rot.from_rotvec( (0.0,0.0,angle[1]), degrees) * Rot.from_rotvec( (0.0,angle[0],0.0), degrees) + +def rot_from_vectors( vec1 : Sequence | np.ndarray, vec2 : Sequence | np.ndarray): + """Find the rotation object which rotates vec1 into vec2. Lengths of vec1 and vec2 shall be equal.""" + n = np.linalg.norm( vec1) + assert abs( n - np.linalg.norm(vec2)) < 1e-10, f"Vectors len({vec1}={n} != len{vec2}. Cannot rotate into each other" + if abs(n-1.0) >1e-10: + vec1 /= n + vec2 /= n + _c = vec1.dot(vec2) + if abs(_c+1.0) < 1e-10: # vectors are exactly opposite to each other + imax,vmax,_sum = (-1, float('-inf'), 0.0) + for k, v in enumerate( vec1): + if isinf(vmax) or abs(v) > abs(vmax): + imax,vmax = (k,v) + _sum += v + vec = np.zeros(3) + vec[imax] = -(_sum-vmax)/vmax + vec[imax+1 if imax<2 else 0] = 0.0 + i_remain = imax+2 if imax<1 else 1 + vec[i_remain] = np.sqrt( 1.0/ (1 + (vec1[i_remain]/vmax)**2)) + return Rot.from_rotvec( np.pi*vec) + else: + x = np.cross( vec1, vec2) + vx = np.array( [[0, -x[2], x[1]], + [x[2], 0, -x[0]], + [-x[1], x[0], 0]]) + # print(vec1, vec2, _c, x,'\n',vx,'\n',np.matmul(vx,vx)) + return Rot.from_matrix( np.identity(3) + vx + np.matmul(vx,vx)/ (1+_c)) + +def spherical_unique( vec : np.ndarray, eps : float = 1e-10) -> np.ndarray: + if len(vec) == 3: + if abs(vec[0]) < eps: + return np.array( (0,0,0), float) + elif vec[0] < 0: + return np.append( -vec[0], spherical_unique( np.array( (vec[1]+np.pi, vec[2]), float))) + else: + return np.append( vec[0], spherical_unique( vec[1:], eps)) + else: + if abs( vec[0]) < eps: + return np.array( (0,0), float) + elif 0 <= vec[0] <= np.pi and 0 <= vec[1] <= 2*np.pi: + return vec + # angles not in unique range + theta = vec[0] + phi = vec[1] + _2pi = np.pi + np.pi + while theta > np.pi: + theta -= _2pi + while theta < -np.pi: + theta += _2pi + if theta < 0: + theta = -theta + phi += np.pi + while phi < 0: + phi += _2pi + while phi >= _2pi: + phi -= _2pi + return np.array( (theta,phi), float) + def quantity_direction(quantity_direction: tuple, spherical: bool = False, deg: bool = False) -> np.ndarray: """Turn a 4-tuple, consisting of quantity (float) and a direction 3-vector to a direction 3-vector, @@ -935,3 +1060,10 @@ def quantity_direction(quantity_direction: tuple, spherical: bool = False, deg: direction = np.array(quantity_direction[1:], dtype="float") n = np.linalg.norm(direction) # normalize return quantity_direction[0] / n * direction + +def normalized(vec: np.ndarray): + """Return the normalized vector. Helper function.""" + assert len(vec) == 3, f"{vec} should be a 3-dim vector" + norm = np.linalg.norm(vec) + assert norm > 0, f"Zero norm detected for vector {vec}" + return vec / norm \ No newline at end of file diff --git a/tests/test_oscillator.py b/tests/test_oscillator.py index f1610cb..d17373d 100644 --- a/tests/test_oscillator.py +++ b/tests/test_oscillator.py @@ -4,6 +4,7 @@ import matplotlib.pyplot as plt import numpy as np +from scipy.integrate import solve_ivp import pytest @@ -16,10 +17,17 @@ def arrays_equal(res: tuple[float, ...] | list[float], expected: tuple[float, .. return True -def do_show(time: list, z: list, v: list, compare1: list | None = None, compare2: list | None = None): +def do_show( + time: list, + z: list, + v: list, + compare1: list | None = None, + compare2: list | None = None, + z_label: str = "z-position", + v_label: str = "z-speed"): fig, ax = plt.subplots() - ax.plot(time, z, label="z-position") - ax.plot(time, v, label="z-speed") + ax.plot(time, z, label=z_label) + ax.plot(time, v, label=v_label) if compare1 is not None: ax.plot(time, compare1, label="compare1") if compare2 is not None: @@ -324,9 +332,40 @@ def test_sweep_oscillator(show: bool = True): ax.legend() plt.show() +def test_ivp(show: bool = True): + """Perform a few tests to get more acquainted with the IVP solver. Taken from scipy documentation""" + def upward_cannon(t, y): # return speed and accelleration as function of (position, speed) + return [y[1], -9.81] + def hit_ground(t, y): + return y[0] + sol = solve_ivp( + upward_cannon, # initial value function + [0, 100], # time range + [0, 200], # start values (position, speed) + t_eval=[t for t in range(100)] # evaluate at these points (not only last time value. For plotting) + ) + assert sol.status==0, "No events involved. Successful status should be 0" + assert len(sol.y)==2, "y is a double vector of (position, speed), which is also reflected in results" + if show: + do_show( sol.t, sol.y[0], sol.y[1], z_label='pos', v_label='speed') + # include hit_ground event + hit_ground.terminal = True # attributes can be added to any function! Used here to qualify event. + hit_ground.direction = -1 + sol = solve_ivp( + upward_cannon, + [0, 100], + [0, 200], + t_eval=[t for t in range(100)], + events=hit_ground) + print( sol) + assert np.allclose(sol.t_events, [2*200/9.81]), "Time when hitting the ground" + assert np.allclose( sol.y_events, [[ 0.0, -200.0]]), "Position and speed when hitting the ground" + #print( sol) + if show: + do_show( sol.t, sol.y[0], sol.y[1], z_label='pos', v_label='speed') if __name__ == "__main__": - retcode = pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__]) + retcode = 0#pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__]) assert retcode == 0, f"Non-zero return code {retcode}" import os @@ -334,3 +373,4 @@ def test_sweep_oscillator(show: bool = True): # test_oscillator_class(show=True) # test_2d(show=True) # test_sweep_oscillator(show=True) + # test_ivp() diff --git a/tests/test_oscillator_fmu.py b/tests/test_oscillator_fmu.py index f3f6414..c7bd15a 100644 --- a/tests/test_oscillator_fmu.py +++ b/tests/test_oscillator_fmu.py @@ -398,7 +398,7 @@ def _test_run_osp_sweep(system_structure: Path, show: bool = True, alg: str = "f if __name__ == "__main__": - retcode = 0 # pytest.main(args=["-rA", "-v", __file__, "--show", "True"]) + retcode = pytest.main(args=["-rA", "-v", __file__, "--show", "True"]) assert retcode == 0, f"Non-zero return code {retcode}" import os diff --git a/tests/test_variable.py b/tests/test_variable.py index 250395e..2c3d632 100644 --- a/tests/test_variable.py +++ b/tests/test_variable.py @@ -1,10 +1,12 @@ # pyright: ignore[reportAttributeAccessIssue] # PythonFMU generates variable value objects using setattr() import logging +from typing import Sequence import math import xml.etree.ElementTree as ET # noqa: N817 from enum import Enum import numpy as np +from scipy.spatial.transform import Rotation as rot import pytest from pythonfmu.enums import Fmi2Causality as Causality # type: ignore from pythonfmu.enums import Fmi2Initial as Initial # type: ignore @@ -18,6 +20,11 @@ VariableRangeError, cartesian_to_spherical, spherical_to_cartesian, + spherical_unique, + rot_from_spherical, + rot_from_vectors, + euler_rot_spherical, + normalized ) logger = logging.getLogger(__name__) @@ -64,7 +71,6 @@ def tuples_nearly_equal(tuple1: tuple, tuple2: tuple, eps=1e-10): assert t1 == t2, f"{t1} != {t2}" return True - def test_var_check(): ck = Check.u_none | Check.r_none assert Check.r_none in ck, "'Check.u_none | Check.r_none' sets both unit and range checking to None" @@ -122,7 +128,90 @@ def test_spherical_cartesian(): _vec = spherical_to_cartesian(sVec) arrays_equal(np.array(vec, dtype="float"), _vec) - +def test_spherical_unique(): + def do_test( x0: tuple, x1: tuple): + if len(x0) == 3: + _unique = spherical_unique( np.append(x0[0], np.radians(x0[1:]))) + unique = np.append( _unique[0], np.degrees(_unique[1:])) + else: + unique = np.degrees(spherical_unique( np.radians(x0))) + assert np.allclose(unique, x1), f"{x0} -> {list(unique)} != {list(x1)}" + do_test( (0,99), (0.,0.)) + do_test( (-1, 10, 20), (1, 180-10.,180+20)) + do_test( (10,300), (10,300)) + do_test( (190,300), (170.,300.+180-360)) + do_test( (170,300), (170.,300.)) + do_test( (170,720), (170.,0.)) + +def test_rot_from_spherical(): + assert np.allclose( rot_from_spherical( (0,0)).as_matrix(), rot.identity().as_matrix()) + assert np.allclose( rot_from_spherical( (90,0), True).apply( (0,0,1)), (1.0,0,0)) + assert np.allclose( rot_from_spherical( (0,90), True).apply( (0,0,1)), (0.0,0,1.0)) + assert np.allclose( rot_from_spherical( (0,90), True).apply( (1.0,0,0)), (0.0,1.0,0.0)) + assert np.allclose( rot_from_spherical( (1.0,45,45), True).apply( (0.0,0,1.0)), (0.5,0.5,np.sqrt(2)/2)) + down = rot_from_spherical( (180,0), True) + print( "down+2", down, down*rot_from_spherical( (2,0), True)) + +def test_rot_from_vectors(): + def do_check( vec1:Sequence, vec2:Sequence): + v1 = np.array(vec1, float) + v2 = np.array(vec2, float) + r = rot_from_vectors( v1, v2) + v = r.apply(v1) + assert np.allclose( v2, v), f"{r.as_matrix} does not turn {v1} into {v2}" + + do_check( (1,0,0), (-1,0,0)) + return + do_check( (1,0,0), (0,1,0)) + rng = np.random.default_rng(12345) + for i in range(100): + v1 = normalized( rng.random(3)) + do_check( v1, -v1) # opposite vectors + do_check( v1, v1) # identical vectors + for i in range(1000): + do_check( normalized( rng.random(3)), normalized( rng.random(3))) + +def test_euler_rot_spherical(): + """Test euler rotations. + Note: We use XYZ + (roll, pitch, yaw) convention Tait-Brian.""" + _re = rot.from_euler( 'zyx', (20,40,60), degrees=True) # extrinsic rotation + _ri = rot.from_euler( 'XYZ', (60,40,20), degrees=True) # intrinsic rotation + assert np.allclose(_re.as_matrix(), _ri.as_matrix()), "Rotation matrices for extrinsic == intrisic+reversed" + _re_inv = rot.from_euler( 'xyz', (-60,-40,-20), degrees=True) + assert np.allclose(_re.as_matrix(), _re_inv.as_matrix().transpose()), "_re_inv is inverse to _re" + assert np.allclose(_re.as_matrix() @ _re_inv.as_matrix(), rot.identity(3).as_matrix()), "_re_inv is the inverse." + + assert np.allclose( rot.from_euler('XYZ', (90,0,0), degrees=True).apply( (1,0,0)), (1,0,0)), "Roll invariant x" + assert np.allclose( rot.from_euler('XYZ', (90,0,0), degrees=True).apply( (0,1,0)), (0,0,1)), "Roll y(SB) -> z(down)" + assert np.allclose( rot.from_euler('XYZ', (90,0,0), degrees=True).apply( (0,0,-1)), (0,1,0)), "Roll -z(up) -> y(SB)" + assert np.allclose( rot.from_euler('XYZ', (0,90,0), degrees=True).apply( (1,0,0)), (0,0,-1)), "Pitch x(FW) -> -z(up)" + assert np.allclose( rot.from_euler('XYZ', (0,90,0), degrees=True).apply( (0,1,0)), (0,1,0)), "Pitch invariant y" + assert np.allclose( rot.from_euler('XYZ', (0,90,0), degrees=True).apply( (0,0,1)), (1,0,0)), "Pitch z(down) -> x(FW)" + assert np.allclose( rot.from_euler('XYZ', (0,0,90), degrees=True).apply( (1,0,0)), (0,1,0)), "Yaw x(FW) -> y(SB)" + assert np.allclose( rot.from_euler('XYZ', (0,0,90), degrees=True).apply( (0,1,0)), (-1,0,0)), "Yaw y(SB) -> -x(BW)" + assert np.allclose( rot.from_euler('XYZ', (0,0,90), degrees=True).apply( (0,0,1)), (0,0,1)), "Yaw invariant z" + assert np.allclose( np.degrees(euler_rot_spherical( (90,0,0), (90,90), degrees=True)), (0,0)), "Roll y -> z" + assert np.allclose( np.degrees(euler_rot_spherical( (0,90,0), (0,0), degrees=True)), (90,0)), "Pitch z -> x" + assert np.allclose( np.degrees(euler_rot_spherical( (0,0,90), (90,0), degrees=True)), (90,90)), "Yaw x -> y" + +def test_euler_rot(): + """Test general issues about 3D rotations.""" + _rot = rot.from_euler('XYZ', (90,0,0), degrees=True) # roll 90 deg + assert np.allclose( _rot.apply( (0,0,1)), (0,-1,0)), "z -> -y" + _rot2 = rot.from_euler('XYZ', (90,0,0), degrees=True) * _rot # another 90 deg in same direction + assert np.allclose( _rot2.apply( (0,0,1)), (0,0,-1)), "z -> -z" + assert np.allclose( _rot2.apply( (0,0,1)), rot.from_euler('XYZ', (180,0,0), degrees=True).apply( (0,0,1))), ( + "Angles added") + _rot2 = _rot.from_euler('XYZ', (0,90,0), degrees=True) * _rot # + pitch 90 deg + print(_rot2.as_euler( seq='XYZ', degrees=True)) + print(rot.from_euler('XYZ', (90,90,0), degrees=True).as_euler( seq='XYZ', degrees=True)) + _rot3 = rot.from_euler('XYZ', (0,0,90), degrees=True) * _rot2 # +yaw 90 deg + assert np.allclose( _rot3.apply((1,0,0)), (0,0,-1)) + assert np.allclose( _rot3.apply((0,1,0)), (0,1,0)) + assert np.allclose( _rot3.apply((0,0,1)), (1,0,0)) + assert np.allclose( np.cross(_rot3.apply((1,0,0)), _rot3.apply((0,1,0))), _rot3.apply((0,0,1))), "Still right-hand" + + def init_model_variables(): """Define model and a few variables for various tests""" mod = DummyModel("MyModel") @@ -639,6 +728,9 @@ def test_on_set(): mod.dirty_do() arrays_equal(mod.np2, (0.9 * 0.9 * 4, 0.9 * 7, 0.9 * 8)) +def test_normalized(): + assert np.allclose( normalized( (1,0,0)), (1,0,0)) + assert np.allclose( normalized( (1,1,1)), np.array( (1,1,1), float)/np.sqrt(3)) if __name__ == "__main__": retcode = pytest.main(["-rP -s -v", __file__]) @@ -655,3 +747,9 @@ def test_on_set(): # test_set() # test_on_set() # test_xml() + # test_spherical_unique() + # test_rot_from_spherical() + # test_rot_from_vectors() + # test_euler_rot_spherical() + # test_euler_rot() + # test_normalized() diff --git a/tests/test_variable_naming.py b/tests/test_variable_naming.py index 6aec782..652a967 100644 --- a/tests/test_variable_naming.py +++ b/tests/test_variable_naming.py @@ -28,6 +28,7 @@ def test_single(): test_cases = [ + # to-parse expected msg ("vehicle.engine.speed", ("vehicle.engine", "speed", [], 0), ""), ("resistor12.u", ("resistor12", "u", [], 0), ""), ("v_min", (None, "v_min", [], 0), ""), From 2da53953d644a655d961a429e9cd1ea98fb2e31d Mon Sep 17 00:00:00 2001 From: Eisinger Date: Tue, 2 Dec 2025 14:23:10 +0100 Subject: [PATCH 14/29] General updates in connection with crane-fmu --- docs/_ext/get_from_code.py | 2 +- examples/DrivingForce.fmu | Bin 0 -> 792775 bytes examples/HarmonicOscillator.fmu | Bin 0 -> 792357 bytes examples/HarmonicOscillator6D.fmu | Bin 0 -> 796106 bytes examples/oscillator.py | 2 +- examples/oscillator_6d.py | 114 +++++++ examples/oscillator_xd.py | 145 +++++++++ pyproject.toml | 19 +- src/component_model/model.py | 11 +- src/component_model/utils/analysis.py | 56 ++++ src/component_model/utils/transform.py | 217 +++++++++++++ src/component_model/variable.py | 312 ++++--------------- tests/conftest.py | 2 +- tests/test_oscillator.py | 59 ++-- tests/test_oscillator_6dof_fmu.py | 46 ++- tests/test_oscillator_fmu.py | 59 +++- tests/test_oscillator_xd.py | 409 +++++++++++++++++++++++++ tests/test_transform.py | 179 +++++++++++ tests/test_variable.py | 237 +++++++------- 19 files changed, 1439 insertions(+), 430 deletions(-) create mode 100644 examples/DrivingForce.fmu create mode 100644 examples/HarmonicOscillator.fmu create mode 100644 examples/HarmonicOscillator6D.fmu create mode 100644 examples/oscillator_6d.py create mode 100644 examples/oscillator_xd.py create mode 100644 src/component_model/utils/analysis.py create mode 100644 src/component_model/utils/transform.py create mode 100644 tests/test_oscillator_xd.py create mode 100644 tests/test_transform.py diff --git a/docs/_ext/get_from_code.py b/docs/_ext/get_from_code.py index 37ac467..182d8f5 100644 --- a/docs/_ext/get_from_code.py +++ b/docs/_ext/get_from_code.py @@ -51,7 +51,7 @@ def run(self): par = nodes.paragraph(text=text) if self.typ is None: # need to parse the extracted text parNode = nodes.paragraph(text="") # use an empty paragraph node as parent - self.state.nested_parse(par, 0, parNode) # here the content of the retrieved text is parsed + self.state.nested_parse(par, 0, parNode) # type: ignore #here the content of the retrieved text is parsed else: # use the text as is parNode = par return [parNode] diff --git a/examples/DrivingForce.fmu b/examples/DrivingForce.fmu new file mode 100644 index 0000000000000000000000000000000000000000..acd506733e385c2dd82c919924fb05325644fef3 GIT binary patch literal 792775 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

^-ZM?N9x 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

4q8{0rG0HU@#38nKuy~5k9e$8QH>A|I~WXuYPq8 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

ShR(hFqBTmCy{LA*gyn-~o&mjyss*Q&NXo4h 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 U&NYovx$)o22#9)eHU;hef8tX2NB{r; literal 0 HcmV?d00001 diff --git a/examples/HarmonicOscillator.fmu b/examples/HarmonicOscillator.fmu new file mode 100644 index 0000000000000000000000000000000000000000..48465d89df2313a85fd0248331eee2188de80cfe GIT binary patch literal 792357 zcmeFa&u?5=mgiSM54=bt4H%e}?Zr#VZUwWbkd)ayGc6ReODR!RM5TVT$W&DgM-vP( zf>e}(5mfw;#8e7s%*uclUKudpcH94fR|dTF!VCWcUK{9@H->j!dEw9Zocrs&ctKLt zSz{Y8t5RgdyYJj{&pp5Hx#yny>^Fb*PyXr8^z&cM{?)<%_V$1OAOF37|7SnbfBzxh z&4%;oa&|nN-=5BoN8@pSF`e~J&X(Qti_zKnbhfC*)6>(@5J( zwlBix(0us5e|A0|&bt}y*z#!_h_Dd)!&$W*$I(3Uv(Hr4L)&yL6a z`Mmmde|9#VjE*yuj-k0#SQ#VFTVKvvx=XMjg4OoC&O8P zTz&npdKExc1+GwOFgzJehJ$J}u}(*|PPeMz`^9iF;Q7gPRy}(BeRbYHe%;qpbcLx<`?tD@T{614KL=^f-i<|mi@(OI;kFY7MshX~nBRl92qNRaJGqPH)V&DyFJBXHExk3bavAd&6PXAJ3=N;crK;N9V)As9*i!)naiz zzjyogaMFDjzwJ(Er?>U#b|hpkkNTU#UPS%L)BT-$3}7^`_`il{<5ibiP=+a(FJ_Db zUVAltCn`I+Y>1=DVmLeLAA{3mP@T_)=l$8x=ybFkjR$&3lmKcXRT{uzIA0VfiPt-u z)w@?iAlTc$n<5W$gMG4`0f|}WE#vz+-7F^q;EF7dn3}6 zv#MjG*xUx=d*B2)rzbWk0dK}Qs5;@pnVwIEr{mG-2$A1Z53kWUB8z?&(=oC%VZIaC z;jO>CS3Nx!qa$Rw0dJmdRio~(Tb=dakIt57)sh!y%+X|FL>Y|c=VN#W247U~!$cym zku%}5s@Fpr7rkC5<1inNPqy;M*Z0Eo*f_VUZns;TvhdAz)tI;f#PPL3#Cfq9+AKhv z-K(sBm<2XLu*D!5$wZdej0O;{Xz`xeDY)*B8pqq~!&p0zFTZ^Kt}l70Few4Q%|bX# z#dce!MeJcyBaLiA{B9UkMZY>3zJo9*iT-R*G2gS%Py%fFG?Pg%WC^x6qRz^yexo3k z+6{*DmZVw8nO>@f}3}&OZ5-IS2qmTQO>WE*{$z*uE7!H~p_LqxS)7kdM z-e`Dw0{(+)XEf*a;jG#6?QkabxxI0x`&IK9Ox<6O7ro(ow7YcsV*A4el$kB|`LuD* zMW1h`=i!UHHdlNzJl`Ar5SrZGsx}f+@s_yhV}n9*DvPg!5fS||W%F$M{3W>CK>fOS zuS8u;_fcmCFE#t=K`I4L$>Z$RjM z0KhKoik=2GecP{#aXG-_7J$dA0HbC{WAgteQs@cj@e0th7NBZxSepKZR`@GY^8sU1 z;tAy03dr{*$V?SYfRUm8)yDfKPg~au*!R|8crI57eGa|Or}L5dKf&M_axx3e_mBI_ zd4D`wTx@Slmy7e|qS*j5iVHgOCI~1Vz!IC-Y$j7N>W{ZK@JWwXfcPxEFV*r2CEk7- zCEhkMSVM{V`4Dx{B*X2M0sPw}#M?^=aq?+|I9VaY?u2=a$`m~>T`xg|FXUg&>!EQdkCXYB5OwPOg*{pxj>B!mQ|C?KGze}4AcR1(E5gAN- z(!fD494}`xxV$H(4<=n|V0hk}qoTt%t7daOkmBu4slEC1Y>2KmH!)OT1$zu;qj~fWHdi3NjZFvM;Byu^n-O_Yf__aNi=-Hy`j9^ zz%R@l#-M+~N4_*!mxpbuQw#y?hWT)jyn3lGS&w^0BrRN)=K}%I@SZ;0fz@zB-s=Gi z$g#|ckUfO}D5>Ku)&cPBSy)I4n)#;sLqDeCSvDgm2brBZx9t1lgVD?Cwzt6x_Ag(T zU&3%5jQE|+#Cb-zk^08NJ{@+x-h7!!?u+N{XmT>`oNVld!82UR4$j)^?GM)R>yKO2 zarpa~zt6(oXZ-y>{QaK4--f^6^7l#j`{d(BVtF^bS{JgGf?&Sz%{(|By&hIw`d^+G zu8b|Ts_wc8_Tk)c`oU_I7+D;Qp&A~exvTzyrMB^$d5~IE2DfZHJXus&htEHImcl5% zd-HD&{+Ivse}4Dx5+38i+>Bg!T3EY|hFNMz}v1m~jK?9li|?iTkwawOeP3s~A=E>aE$3unxmr zE@aDafxBV3(;DH?D>0O!J|sgo;uq%^c%u`=htzD27!nYI*GH$5>1-HcGtEY!n@4A( zuO393E3YYk_+&Vd6@e1}{A3vJbeVYD=tpZs3=EdiTv%Fkg5#C+`-M6?Kg7E7F0{kMvKSlg}Ys6UsbcUxw5%DmFW zY|DY6QPii;y98l$f|enSH@EMLg;r3q0XG+&;h5%Hbf5`YOKU(f9J5ksvVv!kUA77! z90SzN#uJHLKMvawlf1F=4ziihVj!?pCxDRSe>OZjVtv!=6}alHfN!n&0SFdKY&f0| zn{V|;^I^5~{&;9BIdq#ej(h7Lc>AL$BQ+tJ!c@r3e>YmZG9@mds6NzsM1@rr?i7_H z(dbonN=q)3e1DAX94IJcBhOr0|QW;_X)PS1`;6ZMla@M>eIf9zv?;bioFIJkD}#d0E2#`Z@7Xa79ZUk}nh z5jsJsW2WTl7j`oKVL9^oXJ$S>{|pWizs4!);G?*1f#}SL{~cvE%zx2-e!vS|lYy59 zdLjJn{2}1Dg?OXH3_5v+3nd=)8ernGi8x~Zfc|sE7o4bjNp<@J(|t$N={T?rN+r|%tJf$Ni4*75hXfsOQPI9uE|k(Yvd{4J9(e zizma7Brcs`knl%XV{FAicKhc9mfsidG7_FJuooXhxsixOdC6az$kg92kgm347{M%B zx6a_gk^b_;H9CxPn0qsc+%zX54(^NXG#I4ycxhfmKq&mWf!#4fAPUKkith$}>I{%v z$!(h&x$f&HBbbN4u)uYO41jx4Z*Er~T-sb^#IH4Q`ynjg;Tq%cxwl0p+*_Hj#suOj zhsNKQjWY#sspFLU9OpUDjWU!6S3+rJoXdee6i_Fw|H}RCv_ASMDq@<({j9(P0nwZH zwCIpYEcHY64}bf2k*io`Bp&^Tzx}Vb8qe~$pG8ill`%vei1D-@1-JaHkApwQdm0wu z!#IWcH@*m=(}aQV)flX_Aai@n8UkPJ?PxZgDCnu!93BVWUO!m?Tq!<|-}Tu)>$?vi zKPgW7(RyM;rQJr1;#oT}E*+l}DJb{`c%x#DM)9fc zt;}LoY;J0pQ#a}6of5aWblh=v=sgm!>^z0wjANR8`W~KU+(jI^u%NcJbv0PpZrPj- zA+#3Jg>s6IeTpBUhIoU%3pYePxt7HRoKA>+)O2p(`!G{#EDlz@7Zxu27`#amox ztCvn3mK1eFCjny+uL$SF6%JFDJ04Zr7k6V*Jen2e@{ZkCB1?<{EntlvE)vMw0 z>s}I$Ucmq{o)lc))TVX4hu0~me4mv~bcNio6aYW%#b5*5UcxKXQquSKl2woWwz_4( zh=n#UgHM$Qj>l6jYtvx2vJaRY7Yiq9!IEPGV#Jbo4R{3=wo3exC$k1587x353{|J> zWUI;pSOWk*Q!wPZ`I5mLb=xrsYXQ&qD7ui}spTQ^F?|=81sY#HU{<(WA2J$>gB4zJ z>gIy9W7Q^`SJ<2O#>=;$`YgKfx^uw%HjtVPUlVPA6~6}87@_3b@#rW%c*e)@1eK+2p@5_(agh?YMF!NH zmQC@9$3u@m?xduEm3Ku|RHJ7C{(m`uTc>6Bgc+D? z2GZCb?|#*L%>K-Gy{FIicb`7lTb++U9qd3YrLjh%!a}ZT?z$guGEh|v6IF$T#V~F5 z5nocqV!@v{;RJqN&5Yru_}3F_^-!qwRM)bo`yjIIiI5gJVFM=eIPE=jEf)VV zWg96>!$%i1LHFcIOVg41@z6^Ou*Rqp9^+ova~Uc)gMk=WI1!PvC4)8~*%6ck&Z~E< z+D>!al|Ikh6}2r8d86uC>hqkagZ{4J@f5GweEoksGVdT`Kqssuj@d0^K@hvBIO6j7=-mUnprHhgl8dilY>&HvW-?W`*Ad(20 zz@vT77tq&s0i#6FKdYA|JQp)~_UYPlWm)@M60PqoA3n3qtDH(Z%qgs=1oxLmxSD6T zpX@*R{?_iVzrCHBgJ0U-6>uM}?GL9URE>8;3fHW9H=Vr}5n|qj?Zv^(XCneoe6Ik6 zbFC0RTh1575=%}~zd9Wuw-v!8)@sHBF&kL#ADWOICX85uEwv{f7HI)HU%RnaG$z`y zz-{p^zpQ-Lw2b5})~Rf7vbmEIY7<%I0mjJyw6e!Em+bNQ>Y5!Owt*S1MnO)s{!AOS zX|<-b+rtR+P`E9}!|?P=Hei(E?7R{)2IO6^H@4WMwe9&Ns9V)+Ihh!LLF|G$X+lsM zQH zGnIMqMv&g5K8L72GO-YBV=WQ%#;y__xA z9x;WRNXy%i5?h$uS)P?fqPDX9NL|+cr=^Y7B)x073)aMV2PzP<2IABMj`;SO3br&cbWuXd%x#ZL zDyk@Ii4dBXSx5aTj^@L$y@Gqycj3DQa-}d@q9G<&c@C&f5!+9-*wguD4awaTTYO=r znT2on9^U%;)`KxSFT>QGqHH?rU=tTaz2T501H)j{jKH$Chs_j@PL!p4JUzddjZR;o zfw7XA#QA$Cu&pX_3(D3;6JbQ>rlunyG-$*mj(}(->@XD}j~gAze}d+4DGVnKWrued zao`{0P^oTF!Dy(ZT|p?Mq>(XPb@0_VQnKwU;p+A9;u5h* zB(yO8lj*dghYELfJQ@fNo0F#6_NDw|5;A{y?(tU2;mu?`Wi46b3*2ZHXcgj1CJ!@E zXK|V?Wlo_42qvPT(=RYyI;jJZvSP*0?VpnJW&;k4M`C)Eny5jf^t@%5ePmVY#f+ts z2DK?!_Z{@V&@$3URsBWx>+W4T^bvKt@a$lEFDXZH|E}3zg1}&|gIFT1yB_B4IL8qX zvh)JETy8nZpBr+VYu4wC4&ErvW~N%h^39jWuPk@O{U_6!Vj32&P!ZCU5@1~iScU5y zvqG4{G4CC~aW7$!0}?R2U+8g-^AzI8q}FR`_8#5bX8#_B4l6IGL)@bgv7D__+oUzi zHbDiZMkvSfaiRQ3x8E%v1^M$geYZM@L4k9U?8}=4{wVU-?wJ6Iav>d5CrP9iR{Jn+ z^Y&}oNHi`-p^>5=eR|Q+k70B@XR$c`qGw@$P=Cgsjp956I|9Y0f-tGEPZI6?kG3XZ zj%XA9YHO4zO|$fOTf;DKGz)*VH4?pOpR6a)CZse7l^G6MX{kyaXx6};F%!kCl6;mkZ`2P zPA|q>3V+-G%`{jKBOF39Jnpsu$%I=6B()&<%FYZmguN-@g)T#wicguAHUqB9XySh~ zo<_HOJL8NN|2q(0SFNE;cO;V>#&HTfWqB zKYhEfflYd3MeCX+`lLb8n1V@ddf{m&eWS3AdGk%U#rPzJO_JEwNTscVuKxK8j)A=z zzAq^ecuD7BVcJLE-T&1dQ*4&9=qsw87(v~RrtW-t-q~n8Reqkk&d_R5ZXFo`8?B*b z5%yKyot+H`J*|(*cENE`plGmrnu6#|X^PyNk}8ycB^MO0)g~`xf5YYZuwkROwq}Ila`qY7hmSroJ^*}zN3R`NB~73NQuFEqAIu53XC5?@ zZiiaUpw5CqVka(>keq7&{9N# z(Q6ZdqZ#-THY{i9tIpuqvLph@$41xo+vG!I=iXpSK>!$yXd_87J*cbPB?+Cgdz5e6 zaZqo+tZzu_-LwAewIS~NB19B!Qa}I@v1#z)#3fNG#F3=xUh9mPayyDtSz*E8A5Jj| zImZd>JUkZ@>vWh92nWSD@8UvpGjz6(1Qn~qCw6?v12swpXCWxHV(Vn2iwq$X4Qr4h zpI!w+`81X~t8giAVcR@nx_C94E>B;jqr73z(Q!j|ZO>rDvi;oz#Fr;01xB@rHOUPu zT2iZ_-adIUMMV8*oy{C8_+nl0cgTgFcS;#mTMU*p%_iJN#CKh>j_AMMybD0lvQrMG zJeijV7V-^r1g;cGvl$#Vu`?$acdg&CTS48B1~(Wfx)ZapsjyJE1kiS;I9p=D8!M4u z;SOa)x;fK8GVEe<_H~lY`!qf=17>z=)iQY0?cFO1I@AE0QYqE5LQ{R|- zy@@pj5?Y0zXz(~4J`3EcFAV`~@!Xu4y2-I;GNsY$@uW2ihxo3uT;rE9suT&adg3Tb z>-D@Zl5iUFjgyV<;VgHhUIeax(%4D2Irl@4LiuBHc&V@^UI8GGYP|xWf!w6+>Y4o! z`|S9O+F_%nK_qf;CyQQ033y9XfQ4U1E3ZX|i!$pv}W>5p)*isE8tG!o_TL4&EylEwXZqZ#s}Z|V>^XQ4`QSM4%%RQ?Zry#!$`v?`=FFMk zu9t1Wjxg=_0qqO?^*pFBZ&rZP(Y~ir+0_Si{ZZQ18W}z|$5w@^P(G=#ZQCh-pP+J5G3U>tt5J9+m=$fOrYr`WlRUYN-CGd~ zW~@4ORN2ej$4*6MD`UnEXCzJ27(236N67N#Z?*SV?8MaaUZNi@^02w0btv@;3jawM zQOj*=FuFaa5XSvp`pQ_x9RH*7klj z+qOHA5ueu&C~OXb$NaO>=_}N#OBku(cJA~y!wYvWeyWbzEG=is8w)lMkLdJ}3xKLl z@j-@cbhKpUm*@8_Y=N1R;f(I(N~}*)aiE6sx%p7M4b5z0J9C$s2qqR_4=xr0u(tN% zeFr?TVX2e@V`&OP?pVY8J()(P^(=42(=5g*&e7C~n=)PmN3Eu3ELHM+=U2OX`#aB* zy0dqDy1M}~XDf35oFR}h8**gYzV#0^pVD|%{NROB*sT!}0kN`nOoTm92TgkYyrN2r zd*LxuDhK&u026NwO8ySN^|wALl4ItCbcNQ zfiajrWiyzn_GU!!gf>yUc%)AGX{I|24@IaFE=8|}60a#kTt1OPc07iLoB)M%NgMbi zIcdUjocGey{4!WbP6w!Unkk#2a!!K7R)AZbU1F7bUcM$D>8i+OeE}}Uwn+{Fuhvod z=q;*+ltWn2K`Y6|=rtxd-W5k^-ib~%y>!h1HTSP;XUuJ*ytyn86hzQG@wG1r$hbun z2URc_9xYEf{{L|I`Tp;F51$q)0PH?ojZ;nfi)rBPoZiwhofeE*nU~shT9a}**;&v2 zxBA+uzEnX>eudJQjX7pl9f^Q8CZt=9oTi++D}8Jv58FbpuDc}`TxuBWFJ|=|HV#xn!X5B2wJX5xF(%hWrtm#@B&}RHM2;V7t=`} zEg}}-7px8%ngNkAgOhivbW`*5LO9HJmH3`B=!+$Cky|TYJ*)Qpp!xCoN&dDvyI61t zb5WhYx|l0e60$ExKDpWMyIk2BrAZuaj;=D*EfXW?5md> zg$|By#owH`yyU4`;Z%bV3aBHXckcmq&4?pRFuGIy0wLwiUnGbBif;7sWh9HhhgN_r z*CD$CteMsE5zCwwp?2Jv`QVX|kg|EoO(Q7kLLL%)TAr2s zii|u975kOH6!YE@J+>~Y4(ftEA`Y`S~o1<9EIgdr#v1|o+6-=j6v$A9FBFv6Qxrt2Os@mDfp(tL&{iG zC~fKYHBAyuisr-DSqkCLII@68gNfo(RmZjuez=aeooeoAP`aQJKpD&Nt34O&`H1P^XWZe$7#KWCC4l&KI*GNH@~K71W^95y6bazY=bQf*c3 z8(1DzgB&%7Op~$y>z!)v>7)JM-haMR?e102o`u%=I zi|6;B?Ek)c`l!19S{etDZiuqqY0^*>}4;G}wLe;Jfc1?mqcd z^({b1!>+#DeZ0F*5BpE8_W*Zy2Oy8C$2-p-{F-j=f4lqL?*8u^*`kkj_n)Y@M^B$u z_p4|3pYQKJ`2M^5&#PzOKY#XgZwJI4(#MnCCy$=f<<8@sC;Ob-dBS(q&I>+Nd%wQ_ z-FND@j_&>M8RT;fqk8c4+3%n4{_5BJ)vup^_i%?Vzuf`n``>=IM0-_B+xrPi0Ze$-qCYma-aV{&{21d_QBI9`_K7Ed4s3V_Y<7o?(Xeu zRrjCo?ul-Xo~_1%_Xg(YEq-=6GwedhEwcGk^(jtdU?)8$ZK8 z*?5rjPv>~E`p}8St(*RsOy800dod(-_OWu$B^XY$ZgDQM+*eRPErV`$p9Xoi9DQ)v z+H;njsJ~>Y@l!nV;a|yVN~u@5_A?$Pb^~+Sld+3Fkt&UZgF9F|3Z#Um`rbkf`lFyY zHao1fq~g!@c2M`pcL~*=%evEcpsy}AOj`}1G;^6*M5G!N!dx317p3qt>Ch@*z0q*n z(*U~f$mSUKjWXA(K3B9>8iCbsS_2fX4K{+OkyO?AT$#Fln99F zn24@+dSEM42GS7Z^p!lI*PvAxxB1YhkP=WaJ8G<-@^RdL4-gv53xPID{$n5;fEBD0 zDSwK|a9ZAA&DHV~AF}73YKzBkVbGnIzPUz->?~A~lI0ie)FekvY+-;{fy)Cj8H&kc zXio%8VzmAg9XrJ-?QJ^ms_}PJmXBu_H@amfHP5w&mqi5To^`PH1Rg3-vD-_|>6Nwt z8UI!#fXs`7rqnqY&N)RSS3CH=wtBl|(Ew_%J0q7|`^6VeEN6EQ2hG#;jF<{h*i~kD zb@2LYcORumoVK>a!pBQGMK|CT^uD4G)z{)~!$m<5D^5n4k*LuI|ynp|m!*axPCNkW%;S+;|o@x!k zj@CdM@O-lOBzN^}nHuj@I1!}y6gP^cvlhAFz5toK9=zZO>2tdysxJFtIzO?@ z&~A1)RUeB)fUqn@SQ;6FnFFJVf~Ljd7G2ZEj#Wo2T%zx68V;rBA$Sy^2EynQOQ)DN z<1#Gg-*Eo9K-4GSOXf5GAp&M5pHtjaJ|RjLLiR=}jllb~$;leJzPu$v-3YeED20dH z93$2o^tePYASzW|pGYQLPEA8e*W38^O$1SU7PYtN$q7mCZd1OWHb7MWuH@hwu(#^f za!AO8*QWTKS2Yl=cT}}PpfbYN#Ez38TeYhCda`A$H~q|$;h=A8eVQ#Sw4{b1)h|@L z`}{E_$M@nPS5O>lLj{)@`&gTCZSNIAek}l{g=@TDsxrZ%FSUTleE{XcUk~b15gl72 zHSff?Z&w?^(VcHl;x>NJjT&`1t^o)W)jfMnM>6a}DEF+!KGgBr2q#}g65qQ~ zJumL`aD76tb`#fn4^XKbdXs1kd}@z1eGETBV8rF)mSHDvu51T{rc19;C=yLF3lSdm zg6dSsGOKyesd0aED(ziKobhy+P{Ba|)_7On5s;uHinFdh*GVH#yrTyO&BD?a(k=0~ zaW-UEZ|bLpspW7oqy|a7B};(ldzZi=j^kCwtkLAL($~4&B%GyabLh)AWUO-=loS2MT81=|k#paoPgQrqEoqeWv9!g3=F zI}%(=x5czaDax=BY__U5wM#)--8WmK?d->zl?AKTytv;z{!T?dzH;ODFy9&3r7xv& zay@CMKj@z;G-`3-bj)30=1iMaAIU*R9IMU;q|QW-il_G~JCBd$VG@Jk4TC9x`iioM za@_Kt+^wdglPh$pH0O>dV8)SeRvIHSX@*+?*@fAj$I>|~@9HEpuW`$Z zy{V61%s;L+^YS%J!9Ll@_CjJN&Pc=yn__v;%K$utNcSg$eTV{_NV0}xrL)ouizUi~ zRAiD@%0tt|YsCy=18g)U=in@9WzpidIsFf6RYsIM*94L0CBhGpqN_oB!;J*hKd%Ce z0l2FnhL#y*_+k}iJlcCT%+NB!3}39mOrb4_*A-+JH|%DhqfJ7Rs~ z+CsMxCGz>I8ML5(cwLz=rB5cW#GlF~k-DrGU@erZWXW<<0Amsq^`8~0a3JPUkX&xc z+mkLEMj)VaP_(yc=d_0b-;4p>Ox5_xhV{Ceic4jJ*#k*<%S+_KJee*y3jdf;^X%d= zcWsZ`WRz-ue!3Lpj!O9Sz^xpC)s9Z{mPFWccF{Kt>xV|9KdOKiPqq$y)D;X0N zRJmu3+S1sf*u=eR?;tqZqqpWH&{4;ln;56MFnP1(NK$_4e6fn{chl1c2}INV(9{PI zY`E2QLC8U@qp&6~Tx^jsi6hQr)bsdLFmHPUHQWd&6Y*T%$eEn`lP9j%^FV5P3I&?2 zWAl>ah*g6_^>y)8RA$UtvliQW;a-`1`8TnQds&R2KL0V#M#c1v3G6lF?)0dX-m;t2 z!1h(##jSqvSXS*tND-QUQ}Q>4n6h+QHk(#ip0BRXM~U5_++@GD`18EUZ!HnNSL8IeE=42AjMkP1C!vW@l{G(su*Sm1s2E zXN-aetZ9ogA@{dcGl-;RSGaK&=ukL^7h=|Q3R2lomO{0()QziD2{zTitKTHY?Q;5G z%~=GRs8&cVhjT}Zhq;~LKs=ePaE}GW zp49PqG8IN_Kmf;=S|OV}6J2k@MLP8@Q;eC5nDP#~d@-UfDywleF9|IwZ6bwNc@1*& zjlNAyYv*3);0{;4ae5(43~Qy#PJdbS+|j22;ec7 ziL7@0@e6OUUJk9u^MWvcTI!csO0-bQ=#B6*GBxLp9!b=tW`}Y>+LQy4hZz>#W0@!P0gp?ntOmWQ^aaz*k%0!d9@;lM{|E1djY8 zfUH|?#Limy@`^{WPH`Qv)5rq;m)N1eAyFXgrIwyuAYv;?U42gJ+Bj*^Kh%oE2wUXTyX0N!G4uxSr~f?quA)=}6s76(%63dB}1e3Sg=7yso1PH8#qE^RWXw3l$cl znue`ZbsSp

UJ^cATJqTwBhAO@jf#z%=t&-P_!2=Gmy#jOQnsqH+K1Xuz3QYJ-MHy53QwYYxbcnYm9HB0`MS`>?DIw8sI$yjTVkjizwFfAiiXuXF0csBn+2~ zn>Zwdy!ACZ53}j}o7!5x3Ev!RvurphE=*{E$!(s+YwBx_r98+AAUAW#1x&XTXp|=4 zxQOwl9@|GTW2__XI^sG=Wd^y*2p4^pJ!$k;S>dZd|3vm(<7Ea4k&6|aBS-uMx-1h+ zVx!SF90Yycxxy0&-a?!p__VX@ye@45`=rZ+@hBn-HEpvxg2~qRm0wMqRfxG>FFg)@ z{v_S|)2UI>A*#`G_CCdVS??kUdh`)~DK1_;Iw-!m;*X$D6(*Y$S2J1L!d3lKTn5(FVLN%?;$8J8LkcLel;D#pL+aF?Go&DEG1UXfDI(TM&VR zb*L4aqUwZCIcxE3ID6)2EUtL{syvX(JO9;iaep${8!pJMGh=W?Z+j5JisxUaePp+X zmNRY}f0>YLI=XDzDzKg{<4h_IxKaTq&_8R@09& ze~RS`04a0o);lYs^g$c=D^V?J>4FgqAqHvTE$y}0rEd-GvkOl$`4y)|<4tVQd=h=X zDJbmRRQpJ@cg30`H}C~ChhwUnz`v=RF{mgr(xHiY)D5V}_oZjuHQX|7ImbTQ-tpS7_x*eWifVMq;T73Kc9TdlA5Z)IQf*XD zWs_;{nc>QmMvqwq0lQNmlZH%8$^~)U3F26gLLIbqoSeXW9J)?xe27eqqZRnh2><99y&1DRQWngM8 zCeJo@)>wPl0!^~L>aes#)qq`#)wDDb8ydnDF*beBj};^@6axslLhy~`h?{d@9CvA3@nDCmp8b zPkSanQx=XQtUY!jA--)pketIARa%REmXcLwQ;AJd~ zy3dCvWRKCIj^uJylgrSTN;czp=S2uqGJ?=m!ns_QvP8iV@FwCQ#VUa$>N0#U4l7$EmRJ9oCY{m%(we>WD@5w;)ZNO_7G z_kQ*D;FiZ11$cOzfqk6--`)C)&9d54%~~?@Ttc^g0zC5(KKhIk<`5nZzEv>T=uX?W zV#Ip8P#@W%`(&RIa+HCuPpqy?AU2KB*9>s+lB~8OyOdufmyN8mjF4n>WDJYt&Zqx) zuc_?Xi>MLu(onK6)?`tl6Ka|LCKSNd)({2s3N;etYgIqihqe9T?d&6m!C+hs#(Y;O z8C;-R1+zT92;3Fs{W;~>wc3oexc}%L{BRZnsdpu}W~OzFW{&+41%r%4d_rEyH~z( z{~fQLlKfG1m7w3w?l9SJ8-rGhksgd%2NTENa*QX!X1o!-z)OW)qSK}{^InUAZH1=Z zxgE5Qnafot)<>5%M-H-+Mg|? z2HnHrKj~XLRxUYL`?(o8GqI-OOxqW6Co6vtiu8hd6-OK<0Ml%-R&c-Ku(XL?bQ}N}FgpN3!8=JN?>vI$D&V zkgbq}LQs^&Qo)$)MX*l|j%sb)g_dD)QHO1zNeKcO2DFiPtzTN` z>Ng~ng7**x{*aY8kUTm-t$dqt>C91*;0P)Q?c?cO+BE&B zLQ>j6S}2zCD*hkb&BmRB9VyYUJm7?zB3=xt z09#XfHO+RZVynwjtG+4jPq{|x|2JtTP5V!wotP4{rc`$0Y++kU4K3zx$yc17V~Fi9 z`7|it_@4PnT1M38wN6lJLj6c5gS+&giwtT_GC=`}?Ujqb^ui@xhgbpImJQqw)E)F( zoQNkT%yGx}icdg5ZkZnG&*{1RGM!UcZ0~dWv($}3p!_MYkLb&|zT5(YM!<(cMe9vN zUkabQV72Cp^0!Kz0`>}JN_XrAn^Nd^Zc`{6a3#?!xK&{`|9B5WB)`C}Oz>``z{N0E znT2*W_Ohgin+bq*%uIM!tL8$zU1}~`x-wmPoz7MbiMqSWkhJuuSFY3HiiJ^sS6G;q z?xg*%K}(v|Estz3wZ`ZX-_UH3xu@D-ts9VE!2=!=fB|C4Rq{v69S}kfYG|?SLet7C z;eoE$(#GgYC<#|Z+PSaW1aJX$!?6`FpN~1^oLRc2unlWGptdvT@O*Mi`TG-UvKIr7 z0%{%EH9z?>6J;AiKILaeg+km) z1p53ltJ^}6jJ`SktYdPSZvQGYQMe^O)1n}J?PofD^H_Ud;q9=UyjA_pa;RILtrvWw zq6s@hTh({06DiiKn4TCXyvl3w{zoT;J*AHdl>bQs$UI#GLN|?^}V2I_sW6QC?i?YAxusAn^xc<;Z z-A3UQ4HgfKiLI&8SAzQaXE!P;IpC*KeVffr=`sBSzljG_Rht}V+X^KuJzg3(THGWK z)}M3pvR*tHve9XGuit_^+M2a@eU4F$kY71#Qz9?FOq>-*^dbm%7=(D+5{i8Yexc8C z`IXtp9CeS8$2jIO`T^@qVsh~$+wPv_8TNWXg?;&DIHqhnl%-Uoe8wtqUS zhxgrtQyqpxti7}dD*yAcd_@!o&s6mt<5a6$p~ZCJ!zo9=t?%Gx3qTFl!7pnuM(G!C+iRq#b4#^%D}=5$vXOQdko zXUG#*!x~L!z)@3u$qqm6z-xn*yYhmjcGr=4Lb**IPok)zPXqvm+Wf8cL;lsxMWvfo z@%)8D?x1h(nOEqJ`k71-s|Vx3K$PdDwDDHQH@rvq({mSPr0zj!ws z`jjl-Au^eqbGp^DrQcr_F6(h(iRQeWHVJ3Ln_+nKDcJYl_D7b4L8z2^@H&GY=Xjrs zvWO-8VaYa#)qbS4I3H!Bin|uScm*bku^Kr-)P(0ViXJ)RUf^Gd&rU|COAdUEw&WsB zBja>xCY`(t`%JD$F#IN##?0*a7)^vhY4^hD)scRp?d-_40iJ$oEO>~)spZ-tX<6bm zrU|9bf{ zyliVH4Jf>1tI3oq<%`gAb2pD!R|U)o0xr+u=(j3LHx+I#(R&Q}S)cfx%&Rt--95Ce zW-UfkLeIHIbPmBR>9W*K!%26)yz7vCo1fOXqYO*{^^>02TBvyOWy~(7ujbj1Vi1C{ zChD>tG()rQ8jg+`LIKo|*U~1yYqT+2>EsS{@m5Zi0&T5kX4!W#&7Tliv$PR^vv8AN z7kG_6B2#sBle>i{A|DpLZ5MxA&&KdY_uF-BlE+kHPb^4tn$Lx;;?tT&89;+;y_1#; z>(!kRo5riq*SrvB;6VOc_BlUp5i&9gf3y>^2DW6=V>BnJh5bp-ZEsEJTIC0%^1Ylw zRh<65=R&(xU8KK$2!DxOQKaiE{hm&BFsr~l0xG3~L8Px6OvtF}ATL%yMvNLD)7Mvl z{9zSj99jcp`nnBd9nU7+qx^P^CL$xG*{ZB>siFLK@Y9+f3q@jpVG8D zxkWg5EK%jW7{@SAx@cWi7M}6#uoPhmu8!>!*c)*Tb(JMc);nDJn4D#IzCoZ?Y(}t! z<=zFxr>qDQsPYXyyHe_|cfwLXNNOY}Nn=?_76uX% zE_C!0y7creQ&OQ1L-9aFZJ0piU8h}uVwwa^E6r`aQ}{XhKGI}6{?T~e&?I#W)o5D` zsb=c(n35u?X^988({lI4__bAC@btO3))t_4RxutlM4JQ`z44Otha5pN4>*WuKCi7M z?YqJ`%~)A<`Dxch<8@6_zlc7|7O{%+AbdzN3Pc=T zHRA~f;kjH@&s|k}M2aI+rm8K*QXgxxM}1bWMc_qQAFjQ%EshKm{G;Bjuax11u*zn> z8Lll81%OraQM}k=dUAEAUx%c}=#?~-_jG=)Q}%v~NVtP0+RnD2hSwX4n}sa?j}Z{7 zQfPyQ(ynpRJha-A5Y=9s1M)K-p)6@R7$kw_edV+H^&}Ah&_$s~qdy)Y#Z?{&+nlo-_RLK;-BuP#Y@pHQ*ljOD z>cl^aUYhL{JnOl~oG9Ppo^Vcla2r)=Z*-@~c}RAwb3HR{dK6*6MwPeJB}2xbl8=2d zZQ7)$g--}Xi=Bg=;Ui8%6XX1pJ)FY$M1R?q=PQ}Z(dL#*V34Z2ft9aTgz<(VXAQhaG;&6WQ+k)mHUa&|%ys+Pc{0I!;`BvcmDi%EL9> zO2#IQZ6@l~ZT>5ua{x}y1zv69B{|@*z43JejuMM{?+HwIZrz1P?v`0I{rC3t;?k!- z=xNKFZ%4y-wguP0t9rZr&uG&BVRW|r7k^1s5vSj`%bx5K)GaC$fqBGyQ%n{AaAMnJe@U`fVroj!MKVP^G(m{1jiq~&>cFzdsf z>@k&M*=TbzUd~_jxE+wKv+U*6R(QWrrC!Qi;!3}@Ez3glleEgM-J}%9eYkk*maRb0 z?e#sM(u-k8U^VL<3>-3U2C+l+3bS0LUe4Z6FWO;lU-R9~ zV!}bnwRBzg z{8k5y6J=Ycc?!#3^V9si1%}-ZULal@y}MEQaHSvY*$asrh3lw=7a2pjry3@)kDOih z-+nv(y``Cje>N`9F1t{Q_i&Kk|Gy^x=HS8S|NVcl`0sx9GyV6UT>5@=t4a8>yB-`SVXJDI-e->aVd zi@X0kWPCN#+Eh1b@rcSB;kl*ilg(wj`_c}?y;M>xJZ?#8lOw7X@m4x4OM)Auxj!ED zi4!GES992OBXS(=--P<{gCJ34<^4bz<-w69 z6LTT!^s&<#+!PclvxOC&;b)#Jv&}INE}k%1UIQ^=)*}!tLm$|ax>=-%MEo@SesC1M z88}z&+IM!?o|{?~#qRYG^=B7i87>ZvjKd4qdyd11M^+BUctYkAU57b3uD?~Z|NOHD zAzQZI#NwF;-sM)+?RItNv|^;;JAGsssS65beeTlG|2#eMfht*1dCVjx+6ru#1ju%E zD7C7*=vUKsY0pb%e5Xq;iYEWnNY5h6*14?2l(Z`k#<<#+%(iq55x{-4S>M`tGPNQ| zR4^n@;E2RBJURnu!AeCiJ$ftBuw=$FJk_1`ERg>kaIOWvR6Q-Ite?q{tSna8-Zf`R zeGA%qO6kN}Uu(=MOXEv>!Q5y+tV4H4I%2LNDgVsjB>-ueX~8*%j&W3_teE&wRl7m1 zEoD4D<#rYi3n1}U7RhuW-sP(ysZ*JzRx-eQ?dPT}l-pT?s>Qf}9r8&X~Oh2Bb0h0AnN?p$}NZX)tuH zUllzPj8G@Y&Oa&tO=`|6(NuWae_k2mb?w^-W7dn4IU&tS5f97J3bfV4hndSS*cWuW z+FHSZW^;ig1H=F{v!3O>A~KmzoA+mIm{>!$E9y*xp{Bg&>65DWpz@e=#DWV`fOwN-r+pC5#tml& z{1hSy(@-FYpbnY$bFJ1aGtvt%q|VxR6KZL~QIUyC=88gdmJ#VRN<*>8ug$_I!*NIodiAvS3^~;$vAEV6^i(Q_+3>XH zV@lGtCB8(d5;Za9 zl||xhH%J`##Fdae%6PWKA`+4v9~EI`NoV>R9l_LN(%B+TCO-`^Ve95Q-2{VV5p$(v zZC}aLR*cQ_q-4qcj4P^2$7YIQYN+?gNWs?+q8SXLI0{EYiMmEL4op*149xn~yS|li z%R}`1(`H$HQ#kRiBh5$BVHhMmOrNa-eG|3BDW$5Z%vuURRZwz=!a8jRrC7fkRoz`i z>F!C$#V(VCZK8_7o5c_;{lF0LpXM1tGy;cNnbl^#aq2$GW4wjk;rhRLEpT(3;XION zrL)+=Vt&5eV5U4wPSX5mWLTpB2q|h#oLe$f^%)KyOU^W?s3BB$Kx!tfjP$^&BW)4N z=YDpjQ6m=8(nha))rW{wDg5f9y;#f}2C7~=@io)1rWR^!e#>VY8x<5uzj`L8`)|Su zE|{hIsMIYpRTO-9Lp+t&LN;w{ilbi2Q`nd_J=D)-v*#_84P>b_Z)BO|x)Gd|e`I2o z45d5em?a}$AZJ_Izm>VysV~=)KJ!}FcH>uT$sy!82o-Ih_#sJ_3RCXw-gHzbPI({4 zM~KVkRJOX+$VOn~cJl~<&j;_LT+KOQ770-Pc9u4>@n^VT)-6_tNf8SarZ~R2Cd*fk zeTCOnIxTWi!HSvf%Z;7bi++1Cptyw8syo&Lie=QU0PBf*GDLL(C>jDQ98bB&?FZDO z%`>z$<&S%V^7zksgq%B6;#9fNYHcr;x&JSGJ=)a4$koCH^)jhY)6p~}+E z`{SYAb1cgxZQM{-8hs#U(AAvW{(jyz9$Dx$ zTXi;(c67< zaD=}OHlk^lhH7IlP8-fiBLE}yB&#eC8@=y%N z=6SbzJUV@4zUE;~qz~(z`pa<)4rGv^$Qsy+0Vz&vLo`0Ig|X~c9TvDy8;$6TVW4-# z@-^5BC`$fYUR2vAjSs^b>Ckk>VOlKmnWeslnE}--)Fh{0Te0?I#7WB}NJs&HFj5{C-7`tU8p=0b#bywba$EMFP ze%aH`Z)k?sX?s)TLM*x*9zx4tvcp~n$h|N_iYZLI@-|1*u)46X^V}-yL!`cOle9vC zWdh%XKA`%RIlzQOvD}D?l7%RcrS*#3ulfsld^Q~`vmqM1`6~Fo$#d)6Mf1S@oj1iS zQ8rNe2wl()qtaIi zP0)+>p9BsD>mSWK{mtqZ)!pH(zcSV$D^+)5+hqaxMVUY9sA%Zn{UapVVig4xQx0N| znnX(wwVi=hWRt0jj5MTpwGZ`^r+n2BOwZSZnA|q8Oy52#L{@L<#~>$70d;HPsr)X( zgR)HsNu*Fmj0{!Q7rk_lVNrFw&^c{JOIx%*>XWoQc}1vx9U@l`fsNel{<_z%KMsMp z)o+aGy4GIHUbn!}UHs!CO1^tj?%|NU=^wwxbi6DNWGW=7FUxEfSG~(_Bj6PgbHi>y zt%7J_pdC4Ks^HXFZso1;cF#j&?ipUi-oN=fp(_p?58bPJ1D>M(O!$h{Q97jH8UqFyq>bUXvA?WcJz zvCB{89#CjuQ^qVQ*El}cR>2!-^4v$^P^pz>q0;0+Pc#|K6;`gzc?*p>KwOkZEap|u zmkcyLF0ZkTlkSt~17q&}qrG(%52zZ!TQnCpxY^9hf3p|E#2;w*J{y9Ya_Z+BD{~n< zZ2BB)~I5gF(g5~Z0);l$^87X+k_%uiA>tq`rxZ9v_oR+#IQa&zL2 z?DFe8&^)b1>lT5u?{$+kA+8Tc+$8+*_J>6+XznfY1_MTfc7D~hxsTGS;cde0ZLgQF z#D!vmsl1o0Q0XFksE@(hm70^5>0`aEQ7mRwm%QcNB>Le_yu`~;iM&L$f| zwz5z=vRSw+VeRrq>E4*lc+U$|QpSWtO&@H*t15rC6*dbR`sIP$0##n1q#H}eBO);o zvFhfVn_|H{1`o>F?IOmGnYz2I7`zgnno$0|Ke>Po zgm*J_3O0eii}@CdHa^-{Aj*1LAnh;HDIKmkJwe|zzT}<&zV*^JMtTI4UBTK~uMv{9 zfH4WOKfkS)YZ(EVFv5ebl)_1FtgXAuqcZTJBRPS|fg>s{MKma-CjyZqUjnG0Pr2gP z+fl5<#Yjfc(mb>}zOk|2G-a)|Hs#=h3Xs@&W7cyX{$yxivp8$_!&)D~38R*!6N&lD# z-N1^ySXi7Dy{bCRuY*{po@-Ui)~+J)Rud@oiyb8$hkLtpPtimH`97QF2rQdg3Jz|@ zBZB!2nPisws_UjSqY!D>hjvbDFXv3v9fLwq$8H@VZJ}e}tx%FEKA@%<`#@fjmkosH z9A2~RK5ciHj}f=xppRYz>+8lWrfwx z*jzO|#xk79gdc;mi`N)wlmquLJUdB}3nv9r*s&RstowbkmVIXWVKzC9s4MZwc1~-0 zC~8cVW|QuS$OcMkpkbr;SP}SQr}q&?(loX4o&IV7l*}F7MxtQ zY%XDPC>XOtEjkHxDe7UyF(!jCmn7}vsMRi|GGf|%=xUB@j;AkR$4Kc=1uGrMS&ZoQ z2i!}dVvx15g<}R`Xd&R7_?e*&>?%6=1+k+PnAYn6&bS-u88^SVE`ak_wEIbflF9OF z=aD6YAKNL2N&W0y|JrSoDIAaPi>ZP}O50+2C{GzVvS%p97^1PD-r+d(h?5TpQGvHl z3weu>yR+%BLYoVlF6k7$uio+`?O*3jZ!Zs~D2DN^ZOn(0@u&+`9E~;o0i-_@48j3a zbhj-($F^XJ@8w?9qnR!$mIr2g9Dxdn0#Q51aaj9+d=xLF+0vFYI)9YV$x{<~yWvn7 zyo3@NtCl7wJrD-rqK#lB?ZY^aqJv3P)o~TVNOiwccnkToubWdF@=>Ay0GxVn ztWg1W^asmbL1%DK+RMwS6NmnamQA6Fi%5I1nAI%u;bM2yLa7cZBt@bqhn;Qua-F2g z3UBzyT}Q&q)yINr4?-eunmP=e157`T0 z9;{Mb*nh!&(7hT=tTs!0sBT4!$z6H#PTeZAos~^8ca<(Ur*}9xzZD@^wWVTU;?MZZ zWd<)x{vIoEU99wGiBh%t6+w`h1g};Y=LokEEHqJOy!tcw&K3_3Ez`twB!Os+BI(YA zwyru!H?RU1v7caCqJ*p}Mla71b86LCDEGCmI}iB3Cj{2jcN9`2do){I9C9r87#SVS&+_{L zDGeg95M6n_h$z|6n>0(;KlSqU?uoVY7Jk9yQ*6QV$%v#zO=Fs55`wvNGEBCrHRhTp z!Sd#VGkF+BhUg0@SJ?ogod!zFE2mDAp!3e+v#kL)%7wXeW@#k9xdxzO!XHR**-t7V zXc{IB>t=39Ga*KURCSD2%%zHn;?iE%HD-!dMwkTn5@91gV8MCdo&5Tlp_J zP2Hw0`phs3u>`#rz1HL$*=l<^TIjS-L1o-)yIN8Wio-7+jT#d7B z=vZH1v3BFtT85`De{!a&#I8mK*|TzbnUTzGj3u)8Sa-1UvMK+d8=&cU$y$x0gehxK zZR#B)F3#Sph(xq_%<4dV4rx-bO6X`sDasLS_KY&`Rf{RbHtkcvIRJZ-t`SiG0hq1h z%wx7Yk9|XGvO{-sLeppA*9+%ImH%nI#J{_ zGdpYcsC4Q#Q}gDRo)+!*3x+KxF}GK~5*r8)cLVr-l{aUc@v3nrK(+A4F45jBIOPR> z#dngppFf5$zq3WuLO4j#V3^`2A0={>*^8>9;Gv3qpA_pI>yH{c@2SIcCiwy(`xBXR zUw{?B)FLA3>cM9gI6(=hT$csX7^S4uJ@qqH1;+0P4snheG?fBs!G>xU3K z_-c;bN;+0hQKLYPSrWA(-UPkVJ?&PVyIbmkh}MuuO=cWm@ksJHYwT8kX)cfzYmQuC zJQ00(6ze8aml@DaLlLIY^cUx0Lyg5%Yo5TDjnf1pASx%DwWL`SKZ`_RMeR>Wn(dEm z`?niT5&h_&{J24-?EgAu_Wh>;~(bg(-t#zvK*TWjGgpl zdtk!RL>CZ5z$o5$zeP;vvJCRLzB4UmWzj=jagz>LJJx^6u;#4=g{$qWI3mucT2K=w zh%g!lxbrFn&0uxPCClLlcgv29_}nF``JSCLmj#lu-qO{-Q*=cfQk_vGZSIM=t7B(1 z?N~{c!xsJq7l~i$-RMtM6ANF4NS*ltVY)?JqpVgprvD;3rO+r!d!?M{3e9(v+SlqS z``e8$sb~eE)S@0T1}U@nUKFJ$vdh}YG>shIph;xA5Vr{n>G_ ziY-6@a)41Pg%gY!pkmhreQa8b&+5KeTBTSkr%aWy)Kgjq`_pt7a3d|4%ho<{vl-3y zwk$5&28GyYCnHp#%)-O(%B&wlELxav60;v@m7@f7Nnc4>ok;n+BHCHVP(l#PwBW1Dz%iitDuW< zP@TWJxyppr;^UKUS!^PH72RC2xkQotPT4qCrYJaA7$la9_4kdRw0f|j0U4db{)Q8F z5Vk|HpcE0?F8JjKB%oe?&7zSW*>$D@F8t&cdx8bB67l&yJS#G3MZw@8pm1IZkv^vb_Nprn~ zM_<*4;;ym;A^utSn2TzW=5Z-nYkpKU*e|pN#|X`v4$)(f3qJK@m79-#wVC*aPuQS^ zD--DJf8?j}Y=XRy9;s@Q8xog^TF;0@MgwU_-(~ujBOC#Ihfs?^=5%!@pl}9!a^<$I zCPro?yj4)*riSZ9T#{}sxs+-0T{1A%v^0|brmq;S0XF^BwYEJrfoJ{M@vDoDGr5w5 z8EYYA68I}XtBFDO{vqnCgIjlt9I=*6qk4O3>^yu~eMG4Ug4fJeZU|mklWV40#!Z_r zG&~Bf^Sxt<{5GqFn}Wl0lP+&&*cz064AG(&C2#WLhYa*%FMa?*^9Zs)fPw^wjm32L zqmw@vh78@0)vDIuY{C6#g(J0!m>>sA)n}Idn_eDTLwD<~_+vwJB^E~0pQ``_4BhF- zmw^+wg>Hjk=@&3JFb3?Ro?&y|4%^DOM$iogieCqdRGf|uBgh4r>EUEU>y*@wywTz+ zy~1C85nW1O4Hs4-cBtQ>)6kQunTb_Gn(-BVrL0P89xuKBP{3-NaP?}$wdgt=GoJQ~ zXpA1mA9%y$d=2-;X8P?At#$M$Z2jk%|J`EBBYJwgW+v3H{FJDm+l6PP``A3F68a29 zYGTV4QBSyMK8Ilg$H7~fwOd{`Bx^4gA7l`TbGBI~Yh^oX4IP>1g(1W2rVb(m_Bv9b ziHw#8u*$h{BJ5`3IQ%qLvmL2~PkemE6k39Rgsa(cRjrfiL=OCnb7MXYqKDoH&r_qG zdN!7xYDNB8(v7*&&+FK*-EFe%yGpjKRk*{Q(enI%%QR3XDa4Oj7HF@|5ogR)LZX1}lz@Wvs0lX`Q_UsONZaN0BhcPp-KHJHTH%mR*`{Q$BpEGOwy;E` zTQM!`I%W$uqjjAw9j4qb%IP(xzCP7akHvjV^8n;-a=P8(4sv2crix)ve_ew})CN&n^noq=c+EsrUU8(5ekVLNsBfyfew-duasgM#qxa6TV{d68 zlA5%6R);It#SAb6U$&<~m11)+db#P&G3zc5*oVhp)hzaG5Ta&raFX+$ebrH_YD2S1 z!mORIZoQqq80KI5l%Ytpjo+bLbE|_C7lS5s^>MD&-f$nm3+x3s4U0(YEiECnAqo9} z$yr+OF|@M}ADeXp*4GU9Q}k%`agm6d3F#&Ro121<0?#!CLQXAef9LF+$eqo8*6=Y! zPnj$C3Wc;<83!eLm3K<|kM0~Ghr48|AoMUkB`AVdVb%{WhmWz(1rKmSq!>^r4RN2F z=tqQRhnA-=lfXU5h_C|ouZWIo6{ZT@zl_T-IZM8bBs&2moLvGhIfYI7W&Tbz#Zk+3 zev0P>BtHrq%9F8CaVf@wux~;C>a`#&2VCe2$RFI39;Z3{S|ZVCL5g5c1$0eSwcA@ ztMT6uqszLVa;!bpp;&phz5021W%s?v~Rh%CfXR1SkU4ztG&WQOe zCaB(-24L`P6$~T#Dz=-)P;YN@*f!UJ2KJo5%-hkFGt$mUcu;jDs~dHoOu5wXH7BnT zBG3q&1rPXDtl#xOKS2RCdH^+?s&@83FXe0wF>EY;K?j98a`3T^440;k8@)5GVocU! zE?is6Vua+IkxUkC?GAw;zNCmGB?b;rAnH^UkT`v=JP1spLO2o>b~(4ITciN!OjW_+ zdTouPl+e-GFvAU$z(ik4myNP%mZIm$=4OK!*F`U0$KZprT9w+?*5N_LP18;w(ue~a zl&eybaw@+~wQeaV+Z)MsNYa;hNL1u@3sv3vhAQ0M9k0t>FjdoN+qypOKL!xwSGCW*-2J1PnJ1EK3D7T2)=OuVV`PtQ{&2D6xgTetslE*nZka6Wj~ja z*dnrPlth7(Si5i+kY9Ia>8=p879p#@{bzNtYS~Ymz|NrxmhiFh=$gRtt@dUVc)jt}%5#j#JZ3@d}Ii+-nB(;{>(7 zU5n<&&ANb0PK0cl1Zgh)4%c{?2*e=hTdbpjj7$z>p7<#`7qP4FOoRq%Wbsl1zpRsci%O^%{+o8u+@xDK)n z!Q9ge^Fe*rIZs`!ui`!FplkLP7HPSsU8F!~dOb+LZeZcHI1JM#=9e2F<7|poaM&%l znYtYwke*)rqo2k9QLMt3Vw-yHdNvXVR9DkG+nDB3Mv&^gAk}apeTWg2FN7X9!{RzW z6s0Eo>^Z~Q)p72(Y1YRS=J7mTW!$i}lZpEX^Ib@l`a&+yCZ3FV)0R#*W2Q=xky-3; zDMKZtD7qb<4bQkB43(tA9Hd9Zj7L(#6x_2bTxGF}ZKnHU^Hvn4S5|a0^)X~ZVKj7i zt%vh8K;de)Hx?n)0;=U&xOTu%gqX4V$rV!-IHT}lf@3fk9xYEP{kXTYzrXYRP8sFj z3WulW9}hk-wI8X#@lExi&0RLNkezHQKo^!`H1CC_3Vc!5hc-Kf6Pf2Rh7MyuN0wIJ ziH8q2o+u40Cd-5XpsWV1u#l(2!mnM85;+TA<}Xv=GKhfsjTxe{`u=VKUh03qRd8Z? z<4#wRat>7fz!uS&|9p=!4^uN~ud5Dy{@QE`=j-fnwh3of!LTipIyW7XKO49Q8d5O| zaD){&8Sb|SWSHuz;oK%vOIcMr{rE`b5{HcjVIYmqq*)Majk?mi;P0?aZqt(BzP2{JvRpN?*6=~2e-c81<0O0c>*mf$gC}>kyi`ZVjO}Q}l3V(-bFN8X_ zY;gnQBd$oC>o&VpnKw7ctV#>SvTqL#cwG}H@G{oOiEKzD%c2IQ*@U|k!!S0b*bU5M zJvCUx@HUH^Y(uz!C88gHM>>?XhvKtOkte;wL(2$8G zey*d5C8_P;%!4es;^oymAv9_zq|vPHWZq1Ih(pouOIT!tr|c|ZfiXvvc^27Z>*NTm z7!yV--(k^=qnCZw@UM?QRA7qS1?fD;9@L7|w5>pZUl%&yWj@Z}fpL9hq z5qDeUFY#Q#$BA{EsiQIU@a}b3p|Jg;#~R5^UDvUW6igGo!ponI5VO_Z$y%{mZC*gI zxkP#6cI8l!4aYoF{z;CAO{{6HCr?I3OSza*6F)Pg+F5|(3d%`W&mfo|7)(GBXOq{|~ z4QQN1N)J&rLVIA9a^?MKQSMB5>4z5g3Za5%DeCOh3%h3WYg=e>Ei%%_wRw}aO}n&4 z2VbjeAH%hK_vR%6tCMZ+NS%klu!jB%^Y!vtVRT)aCIymME zCB*Vz@u;*17HZn+TCoIYp30697LkNdlcNkK#JICW--9|@l)B_oF+|tX$dZM;)Yy7n zo-}=K#nh7y(U>`T$)2k!8q5@nHy3|6xIL%hG3{6%K&Tv^K#O-GmieqwQEY@>F*XX*@TO5*aR@dS-PArGyPR}bnwdc6Bp zZMlVAtM?!V2*O3hO#6$ha@G?KUAsnjK~PGw>6@0y#&p}1afaNbyzpJcBw+(BP21*W z_=<@;(5cB=T&3HqDkyGRYrABphd0Qdnyd%GYh41AIVHbiJ}*m``jk=Ef}^o zzK$)osZ#z|z`IsW>Uet2#h(HkfBx+-`C2!-A<7XeCmmipBTs+3Xw=bPBN^-alLBzQ z_M)VY}s)=9w+k%pYl{kx@qgScSEfD3Kv?H)i;*vo8 z)Y7(|hV6q&h?sx1{`pDo;_7)+U2i$;`ATL+mTWDP@P%r$~|!J7cm3#qe4{yJ9~`S2QHz2Mkbe;>angVdCsRN@yRVge*nHLA->pZ{d4I z3Ong7Yv5Z7BRK?_K^hg8M82ZNh}RjiJxpQX#P^)GVk1H?aV?;iw-8rqqUxop+?+Sv zAF|GM#V?MH5|F@eJX}mC89DP;^2cBx%7YsuwD{o3vexjM&%~!XC9(0C_=^bOcGI@b zxl_PDS)-M13ajnS5O!QXBwp~ee-@@1#tM57FoEioxQ-%&v~?5WhT`!jqf_OX+Rm3; zkZ@14Z)uPV0uehL0NXn2zqh$>ZWbU{nx?q;rMW(`%i#O-4#3flu_dDqA1qf?O}{E5 z1DZhtQR(^CCW$%e^^#wF%(9tgT#>_S*#0wWosES%1#&6#1IDfft*P-cyKjgo}!WLj3W0iQ(YRTOuaq0#D7F zc>*G6Em1(dh(YwgCvKRNSheA&B<_3w-llq$c6Cggi7bVis-odWGD7Wot7#X^C{NziXe zW|Yf-S^x_Hy|^A;OxS*RCu6mv2v7yyor0IKp<1u;vmzifuGAY(@G^%Fx(g~ZH5~q8C(Ll=9 zojA^LEJtN5xk5%+v+i5u{&vQ2-MP8gt9}QXqzeJTq-9lAzM;R@CGf9*dsVG+H^L`u z0MYuAjUSl6c{+mQ9l(F?%=l)tgsSkMDh z>98cD|2KQ@110Bm)d{v^C-KNJGD}Fpz>qH`L04Pdr9V!B({ej%tL0RKWa~(>6Veu? zR(G{jad%f!e_Coc%>_k!=5=ij1MymvsN4u1Cuzx6Na!m z5Lh;mV}@k@og_e_{r&E}@89=*)oPiL%~_~K?yB#5@4ox)yYIgH?z{KCDzpU(DOrlG zK(tOfq|DJjWZ8$r#F6)qj^~Gc3EFB98s>Q5ro-xmEnKl>+$BP5E7qMI2K<{c#wj|} zsMF}pH=sU4=t7ZH#!OiJV<))F@g}f9vJ$RNS2fkLjKJHSFR-6O=5>**sZJR#V6|1P za{(#JF_$;7JeLwx2|6t&?Qn*2#C)k>ZJ(ok)&6_V<#tCnRi7gigUWmCI~KX4s#Y5v zUqMNty!6La;Qx{MtE$k2nk|O64M1Ki-OZ#wPJcz}e z0q~PZtP4!*~6MuN3$yz}V0S;&oY{v8jA?=!O`zZBlz zAQy%SW~?US0~U!n?+Z4iHlr=I2TbX52iC&_A+bG??*&nv&KD(APM*es6efA^gzG*0 zDVw%&xxHxtOG)64tj+8X7d1%$iR9ywFd4fRmXbc*$b_Uk5TcMQU&>kRD!t61RSPhM zyoiN|g^F4Fd11w+4xIciQ z!jFmk!Js^+yQ?B4d&j(d?uv&X)&aFhIE#<+O;{!HEjO5nE_AA^6#_z3s-m^n zd{d(8#=|hYB?iNQ8>}riQz#X27Y2`fnlSUKM}YVOIqg#H>39>ZW?=0zVD~PM&1F(} zxh9+|A_b4&EkfA38|JthqSzo~Hw2L2sK~~B8i`0)KSVmi4Ae!3R^a=G>pC9646j}8 z#>rBXIp!yZapG_VrP9N!uh5KwQIO3;R#}2`f9%M<4V^(H)brZJ5%C-6J#KBg|As^j zDHY^;P*iXPh4a}_iynjP64SlW)6Hw@YGp~OJ!4rml-iHRJ6GM8iF5usE_!FKYrPUj z7%V2QWZQQ_Tm2ry<3VCsBq6O{Z=wUEa!vG&ZsZ9^5h#O9w+Zph0t}Y~1g*fRxUBHajY_?Fx8C|O10k=xA>5`qX$1!+5WLT&QGIy7j0p;1TA za3=z-jJE%tznu&TO)M+8^XFy|p-YndX z3V|d0yKyY+utEI85Td);NOm z34v-f=EZ8SwS-&|;9iLZp1`YUM;KS3d3ANc2`g-pnE?t>gjlLghkTO0f@=+d4&?=LdKcV z8B>lM6n0Mf_LuG#(bN`0^MZ|x$zK)$C4#^!$FxL%JNoc%(KeKr+?<@WIk}1Eu=CjL zZE+fLYf{*A#K2R#+bk9?XDPbrM&kJFxCwsWri;by1$lyNpn~NKU1Gn`y&D-3aj@@t zanrDD@~ciRCQlW%Jk2E8*86gBU30u+naoYa0z25qJD&8V8I%x|<#55^+klNFo)j zst$srCW=>FERTVxCH@SUEpFRsOEi}p4d#S4==FoRDMX6;RR37V5tb>dK2?0VS#;6z zk?^iW(P+HFMb1#vqn*Npegc{-HLZvB&W)LS8FmZD{&K?zby6Jv%Xds^UKEG7lD8g& zu%2j6p7m(8NHl~zi$bZ9-EEH1Y&ff;-4*g^*g6n#y3f-w1dcFLwK5@BIUD2Ib?^YsKcEFvixJx z!{#ApoitF$A%?PE7{5~7%S?n0JVv#8SsmoUaf8ij_=1mfsJAPS(5(_$&&J#(Afre_9ZwuVyXuo24LjKFZB3+$4R0@&6DYCj0%WCM=7a(y*SQAj;k zS}jM|Macb9iAZ?JU-gvYhs=SN=nDHiz2k@gv1Q=tKW04-`Wr07eS+Mu#e~cOt<$n; zfv#W|Qjp0wN0B53wsHi9^X_Ww81+WvTCGOK#@$+R!BI_X*d5BACSq;D=BEvNl^kA# zX3wCBa4~Sy3EIx!7PmBys|v+Ju(sCjpd&P50T)Egp20GLJ+@D|L@CGPp@d$ic4V76 z4jR!XEJxF503-P)LMik>HuTUEH{nLrt4wA%Q0(!xXr{}geBz@A@E6uPK9Y&cXCx#a zfKmDLv~1G(fHRLz_}8L9DqC}*K~8otSYXB>KRK@ZTIRudqY;W|@TeIMKz8=cU8LQ{ z4coJ@k6NbPF3gq8ip4oYjw!PZ;Z7N_gE{8mDtY%Ex7px&=jB#!L-U)5^z0&ikgr^F zMA%DZhcvgmz2dMT%|j#?x+pE)%+bbco~egHY3aQ0036I91r#sFcHuY zrQ~Mei>R0ymRUI>EZgkTdn;sBDlyYl(wPunBS8TCY!->a#})P+>}wr}3>tjvKFIqF zCEygViDTY3lqYe7cJd~=*LGjlitmYV5cn5qqkSfR&KNN?36SUocSd9bp|p-`^mZ72 zp0kM>9RU4<8ap_w8hY)x-c^hq4Xe791D6}FRPhmP#5)%=#$`?@jzxF9rqZ<5Uqg1; zW|i_uW|>3G!4Ryrifw8`(LDkMyG4uk-UGSG!+~Mr%&oXsjkv55fr6YXfX~m2J%r>x zeoo3^n;%rnXk{D5>)an_s?b9@=NL{7$m?Qh18k|nLzCtCjSA+6f+dvk>|pHx=#vr| zG6%7qu$YBa98cWEp=nv@j_q{H$*NJLc`RvV^9~b8nk*Pf92Rwm;BW(Ypb?rWjJni! zV_q;oTLxNK$Hz)oK-V^?4pqNgMXOu9K&j0}PG+cQ0!6QsrKBLTmrnqs$#JQI=1W69 zG3bf{OiYRa^mzvBa37UE}Ezft`@*KgU6+*G&cFZ*u86T1w-uYb3FPar9D1cj|N zNGw+Of&~H~ITzy#!8?Q9*XW39Jy_yyWa=%O2tju?#t>+_u}fEA z3-nn7BX43c-5`AJKx}%4L3McdenE?5w>xDLvDnV%ZV_Y zCx779+g?4w&(!Is-uZW5vG$W+`EN!>_-`D~utRS5J8(AVtKr)Ea;RGz~xWbf4PpjTLUFnP`YYaD3qPpYcQR;8ibzX8E(u7 z?WbI6qRNw+^8yVDI~7eD&N%m|&A)sf{3e?Q35~1*N5ayP`D=gR(ZVT>+k#;R<4pAV z#-Jd`4bjvTh=wM^ge~;asxust6q=h@jf3H2bQX?XZ5SdrNj;R9a?Ff};Ui$~bj_v- zvq=BK1(?EYGM{R8`^yLs4l|Tyk-Yu#@#Y57UuegwxFU3!)YUbbH$v+hB=W`^S4;>B zmgGc^^!mF=Y#cq$cUuddqlL|rLgvb2AsyJZ+XHnEV!u+(rh3RMbg<##wnT|=p}{AC z34t?-<7GR>g_VNl30h95teTd7Ln3Z_Wh)Q z=vT;h&TMVM!QsZGJS%~1hjLk9ZZ}`pEL*bMq@*27HbXmr?DX8}HkrhRC5)O>mazcv z+~b7$%o53v;5o6T1lsD5U`r+7tx67%kR0_nO^MclsM2Ue)Ac6ARj7*z6DY>b)lkAv z28br5lgg|+6Q~U%heSyTA68{%14o2U%B>0{pZkCa3@#AgF(8_Xi<){9V3R2C;fEoj zB3i`>CK?GqNY^g^9#!!Y{|z1W??UtTt)vWyNjio%vMQ3SfjyEbMW(L0bT+hO@>Xcr z6K(30^}*T@Q5I^Nj=-U%AhDq>nB+C|~FHbES3xaf@r! zgC(GIcoC)X$GUe{A6wivwwI{21vB*_6A3|^BG4~((P4Q5jZ`VP9Zu|%Fc=4C-xOH2 zS9Rr)!nfFRiMHhEV&l~=R?4@MyL1W_DNvPxYp@O=xpSp(@PahUsjv z!u0Un2isNcU2Pnz05pENS4c^avsofDeA3Va^zsr)0DxjlbkYm+*xw<9O7YX#oDxh* z0JxcN!tA}F@Q9G~+3_5-8n$EVID#t!t2jT>o72#aTzM(0q`3s*85{$2^_R>zxOgSh zg?EtR%Y_N5v7RBZnRxlY0%nRo4c^d;mltv30*#@M%bC-73{!^H7No1ab-h3Ydk$Vj zR}(BCr67?aD=QI-R%kouakn<2_;`i`7Op)dO1;sXCk1E=@!(;Ef}-zz+$e^k5`Kr} z3+ORvfC2DLq-FyWXdZWd>6-%ixYDTf6-6rcYhOq1WR1w6xr(2Fsms+K3+n--Zmrq}))>@ybRq zvZDqMn;4Spe!9Us=S0z?y7tZ2GL1n z`#oJ!+!ZQOS48Jz%ltstx?x2lj!d})QHVnh_NO`45|~kxM9RQee+gq$A{{-nP>NW9 z?E(J=C`esMc#-T*$Hzr%$fDCsBWP4HM@SaXE-+9R5yNa^>ultR$r2?riz{N1rpTt9q#%pvC>@l( z;RUat%Av%kW{TrikdnHFp8^p%`5H@PXpKmryg(@0VEC4Pgj56Nl0!}{c0L8*J(VJH z0hT6EUcZ(WCQP9p$%i7u)^Tm+TV)NtJ)+#~;Lrx_rlc!KS|S#abQ53;sM1gga|G&iueFeOF+<^Z!Kfk%AVIb^Nc zwi=D4J{)i0S&*59D>}j3waY#0T!3KdHr;Q1yxcnPA0Xpj4-ODr_kcc75dH3F$5-uM z?DtyB?s<2^*XUvQy@aDS#fzRsSXb2&_aMSmX$+({h^zTTOG{f3ID64oM=K*e#6=+I zs-MJ950&eY)tLp-ywGSoJKo|~oU}R>06t|@6vyG(iFUOzJVkIr_(~1-%ohGSC_xas@U{JUnykc;n%dXJ#LM==6B@APSvkp2f5Z5}=nY<_EBwOafHub(X9Wa&MM(Jr@*^^l$#3#Rizc3f;iu}yz-Nqc&#gcS7Iv#H zA@aUFMU!ttH$yGB6wrWL*@I0Fv^nT9^uzSvS(GUG{#G8NdtR#y(B#>gSOB{;rV3%k zQf#EV?xb@EAY{JfU_c)cjh>WK`%!``@zrM5+H2Lg!+axpNuhj7B;X~ImurTc!!_iT zs@m-=z(Cu1G$%m*iU6RRZXn{L$ zax~dVxKB^TKs8ha{}Dx#C8m_Y^p_O0LU`6(7Gm(;E}qQIc_PrfBnLB_ zTMBWHq>dKnRdZ4{ai@@VHwg=bI5a~yBO?=}XS7zQKng*Xi+=;l?)i7IlEWAiXr;Mw zzS-$o=c1X6S?r3nx;np5J1Rg5taNCNcjXAK)2_0ZG|CeE0F>`Cn`bRfCvJ6J-Bx%0 zJQN5h0a8DeN(90q66?6}7C~^%g=1PB`=o7FjfcSd5CThB-e7qEHt+{g`U6uUC6A+V zi+b&%v>dx53)w?VWq^H%p#@zm^wzIgHh`O8xvhAADafWmZ%?LLMkHxGvlrwDSV)$2 z`1Zgo>1Lf@#uZ)3nbGxhK}-XBh=F5pgJwV{Po#7b=hbd?EDWVFG3w1Nh&H)^z|=xL z>@ikQP0-@9rH$#z$-#(7YyczbvcO*ng$Y>JQx2_bS4MTfjt8xin*so? z!yvI~RmK@wcql*vLQGPY8lwD=-({0|!vQ8S@`}xn%}Q|`uyx|n;RQZBiU9;G+7Mo8kx4&?*=uQ8iQH73a6FSu8lB`R z_Vj?{p{hB;GhxN3L6;nadqN1}lss4yeKl=?u&e+$1EKl=_M`285w3YJ0i^i9|@$ z7oHHP<)0>h>zO+RI19L;Kf0V0`X*R8gUp_6dJ%i4en)(65kU*(n#kZ9=SApYa%V9J z!x$_C*g>0=+Bi~yca;>9`+3{`n!gQRT5__`QVBCQ>045Lb&0E4H z(Y_4TOw5{K3E)u$4!oKR4VgD=ra{c8Oxo%rgO5eVSS5%{98^K5SQAC4HZlU2EmZlR zP@BJM92gvPH7^4JEniKbhT`a*WIU5}2*O>yY2Q1LOmdWYB@IH{+`N)fo*FD}ZF8ZvcRKV?SiCS@^n1M}WQ zGeLL?(4n zeU_(%Twb}m6^H*N47Iv;h^1+?2I<19u>D!As?vD}!JtKVxw<1wSVACq_$;}ViKk?>zuE%G18>{fsAt=lihsbaDiHoDv&Q@kLY zqi=?=glf^q(+ z5s(;BA|CP90c>wz^pP3Y4YYF$E}Mzj)6T%7M*$_1tJmay{TPd{YDR>y&B`0PK2T?VdLteSL4;5hvFfGd!-R5hw+~57|u!;ZQG3Tg6#7u z4xQ$saL&lyZtnhzNjI>cby`F+Ab}aufe7VmDwxt9(gcJeM2K!}wF}Gb^VQM2xy(Vb zQ&!S*feVo`o6~94{?LVrg)`4}7$C@gR1KXunuKzngkWfm9<#0&MRYiF@V100G8Ts^ zTN5&7x)#NH8cigd^2PG313oXMo&b_0FbT0)g`ZHZl!#ggTR;$#W^Ki*1_tYJDah#` zt~CF$4}L8zN7)ru-r_2Y%g#eLFcpX6?dnDYK*!4}VpKi2bO2lp{*_}-g)V6y!aS80 zw?{W>@(|k#C~wzb{$Mjkhu_t?vbNYf-@gzxrTJ3scjffstu<9`{5iO=tQfO|W8oSj z-NM*07hT{wsa8wpl7s917JE`IFj(Gk>kdZS4-H1zNfg{|c>^A0BN5ScoH>ah;jnsGOs3@GM|U`A=n*H_5^&03e~l60RF8 z^h?aVmL8C2txaqX(Rd3I39YbjB0nOXJ>O15HLz+-vKG!A5`mx#NC>tYrx_+$*r5Un zL(#mKRAsAfG;45KX)pJYk!p;WmgXi_VkJ(NMA(POx{XExIc39G+vRt1jx5&A(U{Pp|XFDh$r(GL5<7C0RMzK8>WN@V7>vb7WTYiXbhS z?C@J;hloo2;7~C)Me2zi+!shRN>zo!=wP@2u#!ILQpVD0q$rEfGc6NJD;mY3GBU1b zH5#B?bF?l>lVdQXUSeEkjvqvPYZLCR#9Swl_Up_|+BUJ==sv4Z)2~gQ2RpLXSMJX5j~!-i zZDMP*3t)D+i~~A`46$l7);1RA5fa^K#uB=@grh?H*=vhJN(D7# z;<&QWPBk_zC2$evESRb~Qh~)i$ff)c5tNdRQHaP3Am%NSgEGSGWojlb5G`q%%)o!H zRS8z6nXFoo$O6@eYw)J6NdDKL9uFNz=H{3_Ija}s(ACk4xM8h z%eV)*$;Y0<#i!w3qr8MJ?9U=51MMoVKykJ6Tti((0$6JBxJQh30jYdrHOF4H#pPv0 z=-xzyZu{oSFK&QRWmQVV0?`b-QsBmzT;&{T%!iugG++aco~34 z+DKu9uz>Rp9$-&ziRNjI(08#`RlLUf^~jkAC+>wjE7VRfrdTYLVBac&#!SGX2Cz8$ zv$ku!HVuHi4g9X+j22#hs~4tRqXmd578opSDqyab^N8V0Me~lzW*qz?a6p#XNasR4 zBGyDn?I9`oAdRG@?R^$V^z z&dVhXz@`CPSZ-}aUFU0|k-er4oT^nq99*^xXC%fd;ii?S=+@MxqCfZM$+LO5`esW1 zcx-pXHt8-qAOs)td1P_`&#D=)grKTu*Xbf+L=LLD7lc=eQ@q&vpu^&RBCp)(o_l6;%Ime8hKtVTCXAC`qm#LbfG~0nXq@EwLLJxK^C;FDyZ0N$Gh5`d;o^XVUTz=c zoQlHkdPDX&Rdi150>R0{1fOhK#I81#xk9qAB5U|stA`BvPhb}*W5JH+OY*n8G^xJC z^~{JDg5v>Ldxc%xMb{$+8PUTXE0r-wm(qMq-81f~y~&6yzp)7~p&f`xub#;3N539* zx&8I$qmqotviHxr9D;fqgk5idz;z8(m%xkVW(QX_<33Z#(+u;)LH`wQGV)MXefg$h z#`<492){~%eIjMz0eB(y_yy||m=@|YHnBoZ6sbd1AtXr21n3Hm8@ToDmose>SbW5Z4=e10megXlIAV=fe}Jfa-Vx0qA|(OlIFS! z#~`Saf)xFtI4(+=F8tZXPv#v2 z_z?K3C(^>Kpl6*A4M`n>)JJ+zhcrEzZHk8%dlEwxl(u4r20|`3#u`_!CrW=tMrVZR zF3CFD?n7mz77T%mltWM&@m3wo;*e)z)0I|to%z9&#|}OG!1sfqo0|gGcb;eS$XEa$ zE#va^H54QS3kl<=FL>DpF8P?u+;m7^V_=~+!(M)7ybmy8a zS##i}8Exmq*;yr12tJ2GkE8N5_}a&4-ec?ir1SkTasCm$*6rl8$K#Ou_`C?C};d+^`Av zjNk3W7y{>Q^wiN&%E#eIar5HOt<8^Tci;WE?A;C1%um0)Eqzs$>t zOizf#_q|o5v8nOzyt3!Vz}1$c0#vT$s;gg08zxGcxtTo9gU2Nj^LR#&DmqgVBtN7s zh`!js`jodti-nk}C^YwzGah!qFQfZM$u*m9J%lLWG3rvvnQXv@NH#NQ1N_ISLc2{D zCtRc!?!oA;!4cWATf-%sciSK@(dhW-c=p46I07+M#?)wKG`1TM5IPi)_kFr3O*A^V zv{B#3QHA=F21r4P9xTiAE9V#IGdQ{%8(qZeFS7tkM*uc=aFP=-JU|Md!1`#7K#7hO z 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 %a|I6?Yw^WuTqtVxUMF^FH;M zXde};?OR-9+Hs@I1PU8`_pxl-q{Ii~gc8ff?J=v#$V$=Vj;tmSGs^O@z9e*gXX(58 zWL|9K#B{PDH-3{Pq@1zk6M8O_8%`{VWn3F8v7VR3oB!sqiwM}A0a;vdXRyL^BgdVb z?OarD87By@$}(8bov2bP`XYW!Uz?5Qc7yCaUhd=;vUo!{Qf?3l>D6r1OZ&-1b)FpL z(KF8imYzH=BukV*`EQhuEjD08NH#*KgkE*d9YZ~b$wja)pX>gGl&@MN zUs$m!m8)oiEPIwPBTy?FIN77<)uU2b)|?eg9<#s9I=_&sV)H+gvp*uL>#XCxm^dkl z^dUU&b>ypWx4+e5DAdT4-}Cebd(Fhz^5$P3Rw*q zAG*U4eh~buMEY9DT$R&K&FF;_P-ws9DIvR>w>-_5x8NO+$e%5%NwItSnjg>8+Xi}` zc8WO&l}Dn0;KNc)5`fOlraSZu&Oj`?TnM;S&H_fI&Ko#iW@9H@B2{ziS5k9im;f`! z-no$ea#<1*`vFb|b0LgS2;sx<4<}30U^%ay19Awa7>Lc{GTJOAz;b`<7|zL!($Kjb zlLvVa=H=YvKFFkY)(BSvCDWa&HCqfMo*<7I{eqkq%Ghw!P{vV1_L-pwDeN~zI82Fu zP%q-}KVfv91zblon@r?A5#}10F9=@Od@H@bPb z!QGJ}J~}s+iXez{mshx1UC1C<1s&Xu3=H{8_`tM%LohZp7@Ny^KUEn1XtM^qgfm%c zUg1=s39{_HT$ztJIYY!V)V6Px`OX!YuMFQ{Lfae+aTSmRSCr*qa_l&3`MW3y-;@QW z-il3zxJRhW$9C3=pWcs6Fk-&_Rp-%6*8p?;(9SYataCc_(1p3v`U`mVI%nY1GF5*5 zSV|7mjg*ztcXH+itmRf(Z-dk6l&mNTcYoHCJm^C$Bb!?nLsfh zGX^HAGGd@$UBdfMG4)RC#i&^HVt5c%NZ$3cwKbzj+Hq&gx?>D4*izy7&>krTNK5WqyBA^81;5-{lDny{cqJ z|7Tdr@}wtSBayr$+}6R!(Yt_fSQ3I?AS{qEY=lphd6p(oa8Y>$5so4S(Y0LdOE#SW zvR_GfU*#iYr^0fhHUvGJ$jLxYi9wI6N}7{#`QDd`vBOZQJS6IwGC{Sfs{Yt7C6?bR zFd=Yz1N-wfVWXA0{&XJBBBSFh`!P{!n;W)1Ztd4qy|+vzjpJu(qmQ;=RWh$>8-9_Q zg1QOKBWwt1ez1+>WxQzAC;UhdoEs2*o^=f?iZ1%Qv)e9E$B!jzpq(~ZhwXyqu!_P7i5S{JvBRY@t622s+}$_EC06_q6=v8BSJbk4!e_a(Uliryp_wTs$Uw#x&|Ag-B-X}> z&V|1f{{oRI^PiX6sD55}a`njK$Uatl!bD1xICYe0n#X;PkMx5|9v4c*|Kh?oGKVnE zVi6QYF~FT!6uq)%tBqZpx2(6ceSi62S&<|MkX*NVfz-sf%roylzmH70?^UhWmr=)} z@h_PrCL8#|fI7N*TLow*nZ2w}a_yuHcLX7Og@aQij-ric79(Gr=Tb-~(G=j!R>E70 z?fgOxau521gB1YOTlT@lVox8f$%~w1iuurYC$nIBMUB`qCm@NUOT@5wDx9gmE>sEc z$A2NiX5|jSDNp}5{a^{argSFUTioX!)Q6Mp$R%C9s@`atTb>u*$NBVLDbZMp^lD3d zA5)@ED70S6vN`&*z^+etZorp4;xS%?kf(53ztX`(YDqF9b^Y%>Y3BN06pm^ z0sC`H*2S8?%`DLcCiW|7O{c8Io||y?#TvOm7toq5o}0ep6)P#LXnGwM9Sge(WX79B z5rS|<9FjuekwXVTZnnb(?I5x$nO4CzaniAu_(?M1JNABD0RFs*4}WY zlNYzi3t2<)tM_w3Pq$@M{6C2LWJp?d0y%UzTZ9N=kLdUAx@meQ5+##bfOU;wjU6P8 zPis~NJ;S#V@^bcs&NG?Q?uwbVj-2*q=A1R;Z+QC#8aLB z>)P(77OyDBJd{mf-$q{&0F4N(Ci)(PCSs?rcIdNe^A;|Hr_Opux`*SebIqW;U(?STY#9?9-S{grbs>jgRy6HJnW*c{vnJa+RW= z{3mmC$cHlIazu2ESXsx5PTVRLj3k!x$VRDm^~gDe{J2!@Q~4k*0|xEC24WYM24a_+ zfY`}^r$zU^e5P2$uO+E{H30fQ3wP9@-8bQD0_uirTSVgb3dr2sKsAB#gQ5O)fD$poV?3?6xXYY z6tAQVRB~wp0xqk8ADSA*v%i6IJYOd7V6d0Yv0}e#l0L_NrxQB%yM^XScruxb&Cdh! z6EC{!?;?&4zmv`_4#eem_{M~HDzn4h0c}=VE_;S(Jk2!>6NDoqSPAvWD4k#?rs3g2 zS$m5i1&47R1n{72qP`5OyLO^D%RS~#3|Yc%$LLy|N9RWll)(f_2|2R|M0e&#&~<1| zJv1;5bpj^gC#Xp{dK=eS%R$B#Jd0m?7The|foIlQ1Z4#lqEZvKl~aDjY-Y!|nCG-{*}Hm9qGuk4 zg&8sIACTul=k8zfZ+dyol5byUFv1{Hrbqm2y8ngJ6H#+U7dtJiO1wV_Q)=W*sM%9O zbXbvi5iaS4t98lHH?BZpJREQUfr%EYG-DNe2K91=8dz=YNi6&YbI!x(=Fm#NM~KF1 zL{7x)EpjMD3kJTS9H|xtB~sD2VxX!Kg-9cib}#3>hqQs0X97+8xl49Ar+qCz?cTn- z45;EvP?`EsSK1SN)oQbPz}I*KqflWGNu)^awUan=QJ9H2(?VI#N(Fu+a^SDlxI92q zKfM5Azi90?A50*;l1plX^X`bp17)lv!!nNxzW$Jinpz8^0- zSY>p~KOs>|ShP>+GnI-(-FBWjPmgPpy~LzD}!d?q_n% zOWxGRZP=x_j?vO<#0arWUh0=sYd9xa zo7KM*J{8%i@j6y}+1$_M&etD1_FVx{2akoF%EB9=LSg%mSER`&0B_=Cu~g@PGd8Cz~u>9nNXM zlj97|>38FE{X@4`E7DCHT7!@tilFQixw9B2y!BR&OmMJQ^0S5mSf!;8=dXbKa? zgm)=4sEc)(%x}Gpbd|eGm$|F7O_9{gLFVI(6`JWnU-Fy}bu{cINpCeHQ|uhcT9=jpM@G4HL&*GyCOx16S)?lsv^0)dA z^0!`dFyXCawXcR-I;VwoD3U%MYjvTP`3UQE<3LZ>1&>^6X+-S=e8YcVV$L760KQOJ;2JN6*Z=&J&0p2X^$s z1}*P#7M?EvT#r+aL6pgmS3Zs=EO*Z)db!?ciqTiQUZ*Pm91+J~=@k)toe*UGC z5K0u))vkK?`OcltA?t+cp!mZ|cCb9%!K2b0Ji>GkDl8-AZ}mZijZk5#uTNa_-Sl-l z6KI_?^C!~w{n!ezs)QPyQ_qqmIn-!HpKGOCa#=ORTy#JT-B6WR!h0D{m|5A2q==$? z1Sk*Mb!Y}UvStF2Z+3{2U)LyT7PoeDP`+N4MLu&n7sC97oGm41)tUT+io~ViJ>P=i zMc<_9=Xab8CDHr7>@No8+1?LH5E`xBw=eMlQ)s-Z@w-9eUebQ&7#g2KqiAOGoR7|6 z<@7&QA}ES`uci@J+`Rd094*V93Uc2-W(MA5J%d6GL5URadXyF7i?;w@1ps)>^$k`$ zQg21CpDv>lsdrZ2DwA-lg4BY+*dG>CUM~|{vG@a|Ii>i6z(A<0F8L7{R zzu4FbAU{zDMkyrM#*XlOK(cIYnrZhEK7|FGd=o+@L-<4+&+RRet8L~i9yBbHIS#jM(v@ZSHNke!RXQDXa!1x|2b)47+E2Fd( z*zGMF`8Q4eaFKsZ=d2U2b?E9Re`$vs?#-T0rkY9WYS=RACifc+lE zt=#Ep`ClqE)2q>}H1Q^K%wjYB3uP9Kve;a~Do-C;t`-Gc_eA;uv74y!d-TNl)@( zB`=(@p9^8Q!r~t}UxPAmS|M@>^_Yu_?`p~_@wGOVWr~I0zf8yRI}i((V{@_2c`HUs zRV+Nsh=rK6)k@=yaQHRyh6;z@r^8r*Ash~h74D;=VPR4<+|*4ptkcpSCo)#Pa#cZh z-fToe7VZmuyA}=8=rK8W^N1EV|bXT51ToF4-K7G zGHW_VLj3D^AV~s9!hQIVQuMLJviCg=>9Q|kSHk;9Mlp5;TY2;+ zkkqe4Wi+oiH1+FH%?%|pex=NYbx7h+#EU*=ZGmKNry@f#gZK;?HCOByOr;zaXYD(B zOAjPEDD)=GMmbFPeMcYMEa?3%lno6JndOfWGEPwu3>7FsM->L65tOxUy9q z?$#4Lc9IR5{3A^t$^E35IyFEY6CTf`4h(99Iv8{8iLi+EKJc^-fSj+jz>cY)2gK?G zefTpbD4|D^bM$`^UsH(P01;<<`-bymY*~^nfualY<8z%0FUMAT( z4EIDoKE4j^eiFmyPW!Gf{OOZ|;gXxAUwdH#2MJCvBsh_(K?M4nTc47YqZmNi{T2*7 z^l3H*u0ZlkVc-w|8Vnq+DT9HdNGk^Dt>m{3!=LkIM`iYQ+Qt8MZ*wI1LhbEgEz$=j zY^)-etX67Q_i{cPB}H7a+7Ij`H>Z_wAU#l&a7epeZwmgHqA6K#QXJ(S2P9{W8Zw0%;vJNNi2Jr?dUARs+= z7n3C@ zlw#O#^N{i#%L zhW%1abszTmKm4wS{gB6G*w2{Xec0cp$1?2Rkdm>+Ct{);>?*DLl z?Dc|9P?wLs#Px!ksy;UE9VUBZ<)%KG=h|!NLcME&Z}La3V#byEK88J}Fgg@~2BT+b z%0fL@M=Dld#u6fVEYrSnxa!HIQPLcNlIBRcPM2InJPlbG7yXDJh3$JvCv|L3Zq2YD zl)0Uym3r_|!TFkb(lvP0Mf6cT&d8bx>5$oj5>z?Gk5TETIK@Y1V{L@mrWDECbN_NLc;D0g z%WcbLH93GUlEN!|V-%0O+`pW`@(s)Qt^6pH5>9dbBFZ{2GBu8@i z>q9~$U!pj)qBn5%F-z^U-ddR4-yQ@WpbybA#=SGyb|t(&)GSAMS*4OrgJz78t*^!2 zSMDi9JDb#RNX^+LL|up-ZAJaX#a;cx3a`>>z}OwRExMAklhszuh>{sU>9#>GSD1S_ z=lZ06S3I44ts$_6-EWY)ou8N;(D?f^sVluduKR;j<4P~p`jFZmU)9t8_!u_uocDsg zB#E&Ye88hCurj;@hRSW|7m;p9voa*Sv5dm-qo;Z7VV8VYwk*>jJKebVCHYap?0QgA z-DH`60WDXS*-Dv!^~Xm zTP_xsZv3+HJc;M68dM<6w(r@JE2*(uBtjmk^SLQROb72!z zwakh?nlwzZdvoMD)rYF7=ziu|Al6)LUiFsTx_GNR)x~ZpR?DRLm=b5n>lvfhpB^)M z3Ge^hN3XW8mL`=*XYKXQyAk=-CSh-gOTKiwy0^y+DfThx_Kan6NPEnyWP9XE+M||C zT*o}@o&NfaMb>MT_J3U#&r#_i7;BwXa~aQkypSl;!B+zk5OVyLQEsOnt( zB9p2p=>8|;QKJ3?GK_dGxhKW#8#VAOG%}Sw7&D&bNC{>U%QfTq1e(Y;WH1J| zNmHISB^}q!XV<1YhwAX!P0|Ig8R}x^2Nj%M`L?$UxhenQcq2ShFv3j|*TAQh{)$)&f}n|BWN|WY5iSHa%7*dm9p|*){vwc8!?*cv^l9xU}hyr}>vWr_6s| zrjVNdyhLfGgza&3&>7|5voN~7km`R2wm<)Uw`_*{S0v$+vq2x1p zV9CyJ=X2dfxl@HUc!RTd0xDB?`~W75JmQ=bq@+cQ>`1A z-8x<0Qtey(%PdSDu*>$Hy8HGG*wgm)&uZV5S7%`_yM1}t?Td*TU4>)iD_kb=W!eb- zl=-)tA-;qndxXSkpe>EWj;qp*gdU-+vK*Q`@9r#2CctD*GzmTOCGKcabou5zyKVFz zDY8e6eiU4$8@-QdbS6D6`9)SM*JO`(E!PYww%C+HHIPNRd5i@1vkK z-QIn^t-Zg!E33T=_PD*r?!LXf_O!kES?!&Ax#BmIUh}eB*(qwz^!(2-5B~Z8?iPzz zP-Ks=csY1WWAUZSv{h;TuH!$vG=s}uaAy`S$7SL&Hts3rI=8QK*Q56l4tB$=o~Fni zwRjnrOSkyI>=x7BjJ7`f^Q^XBt#ZA)cAB;>mbRw((;3~{IVxw}+@^^( zu~%K1gI-zhd*H9zf0otm=l{5;^XvM%cZ=t9D6&U*J{!!Y@%-3yZF(lX)@Qf!o@{!} z<*)DjVz*6uk|KN5w58xN-Lz8EG$q=e@%h_DS@=Bl(LKfIvAb_{uRU#aepaKWUZVKy zxxL%($ZGG{C41W5>;GrBNIr)mdxYe(L2DYxk4^iQ_KwJI@5nuF?@M>>w!Ocm$R4%# z9x$42Z-HrVCcQ4cJqxS96ZKq*zoLw5&{;>(iE6{42Ie8SR~ar;E#qlvJPOT@|j(e;!4?4VU7JTXzh4F{B<8rV;wb zi=@$+?XCZ57Cx`()?U3EhdIOtKi};rKTDBsYguYD`wX~CH|k(*RC>8e+83amHZH2r z5hc9G103fVgA(48NhQ1<{^d}ns{mivayE&5`&jPUqL^{^nQAt-P;g$_mxibLX4&FU zs(oZ}zVoji=|hS0ISKUm;dAAc^BPZ7GbjCB_`K?oeIwt?@ZXkl$FY>cP4oKVjzewy z!#|UT-O9fT>ZekJYKpkDUuo{V;IKuuw>Xtr`BdtrOH93RI6tPmyVbo~lBxgP{rq+1 zCH7sb=tET-KH2atH^s8&TKF^bEbXYZJ2b+}EAL?EjaRc^eBU@F^ZYIQo)*Rrs#Q#? zU--z7J+UHGuqI^JPakdXyLPla;6E zeSLQaKiR%r^#APL)W4%zH-3-qrv5d%d9QweWPEmk z-%GpUmqV67DGU_~VtnrD20y#~v1I>t0so%l`yTNZ6;HA&M#cB7>m^gdyiUr7$;d7e&5GNf+_P9!-MM&*UjOWVe{*QTGb z?dZJE=eAP#Q7QJOt|j5XTT)BNM9c{7qdA-^Hiv|Jv)MLBRX)1NQI*<8tf#4#Jz1`+ zYvhOY4Y5kzIFr_LVxD18_zh#3I)mD}IC*IPcTE{{x_^*n?8Tt)1CnA;xU}ePQmw;| z!HV$MOtXo-I3Z${`frSabIwa2x($RlFHY)y8Z|?Ei#q?F?@L$RpXY5i%?cJy((`kYIv8js5!nTi<-e16Cv#mXTq(5)W{E{-D5cqIZP5R0;8ZhX_AQc zduD}dz@cZT^T|U~bI8T#`&sx*)9a*ICNf=mg?)@DgM^TsNKP)D7VzA#1!I71?7>0p zKjlYJJDc_;37Ks(>7EF=j5p359T}z{tMAXC;@<<#JxP6pq>wK7eTXC{sh9>Hlu59Y zYQ8Ii{puFM?Ex1`tGOrlDqZ0aU=a*zL`iFLu2twCw5Jg9U+GP13=;ZvcOur6-Tb5c zgqC?HAVyFHfDdVDgCWF!gs&nvrWi%7N zUu4QBM?*Y@UKi7pqxXbxn#v3QTJe#Awxj2IeH?oZfWvsbHeuytbA7S)m$|;-fDFWbdagX=BG%=fEn8Cj^W5?h4M5@h zTzxC%<%{m3Q#b$s11vixs5OyRBvwt;e7*U)HDHg= zL+fE*jxq_a(c+i5Lj4K`V_XX2mUU$b+@GnRcN4(dOO{93dC$o=E+1hXlOq&=#zj)lM6vd<}Y<7e6J zwbkV%v-=XKxAwI1l37Ed?Y;ap|D5qb&>meu?^cNu{UNRfBl>n9!h{sO^3Jg4eT&21 zNAtTdBBX|3&6nZ3@a;`TuM^jJmJT1FF$2o;#fNc)`0a-9B}^`l(_J@-;P$kRNMH0A zQ5>R9!d-q{9?sFtLz*MYAQ*chL1(FqK$+`k;fnHQoQ(@AHEhTi;-NAgTJ3b=Jz${( zK54Y#Q;LJ}*%g`kA-it1IJ1h^ZLo+_voFpNHi)47n?Bg~*mWP^n5LAMp9G2evp<*f zKez1hpP0a$>fG`m+_9UBSR0&$ebnQKpLhTj5)a_s6W^c#pE#`rVkZ>Z;B2@sk?<_V z{a?!EnJXDzTN$#aCcOYoTG{k`2ffIP6h4;U^_)f!4fSyjlWqj<^M1Xi?)X8-Owf1WH!8 zW*pd#uXPSG?LN|s177!mjc2^@zKjJP7{?#)j)ikOfFHx)f;vijDZV|VL2~7G0rgVu|b}cRv z{CJw9un4Q>dyzhMcBIUoSdX=GT}`Cyx>vciPlN7vF450Ve9yAa48$i@Sg}c$s4tpx z6wLVwCLF12#+yKkz_NRT#{2O$Pk5I)=F1MCJc83EiIFboe#9v%lHpWzuMWkpuh4Sw z5rD3{gaX8+=?EVfuuE_qA%4hR^lxykd=~y5yZ#dYvs-(%L$J9=h%cdfn+ zk8iqucAlph0X^Nlx>OWeO^p$#fngu%TZ=M*BlXo+KFs0bFGLJz zNpx-FT=&T)YO;Id-ueL&LHjCHvdk}b%MynnFtjc&QdDJd`Mc!R^KuXuUbhP$N;m5SF> zN^GAh#ei`OUWDRx`QrMUHsG$F_M`!#eF0!1d~x?8^6}{@&|%AML+rR4Cy+g`R&m;+@82u)H-bp!d+-9CgGnrEKwjSc$;H&`2Zl~g1 zecW%v$pxn@F0bO&lTg`ysS8V2DGYJmsmq42t6COZ{Dd`+fI=nQT!XqSOp@E1oGf$> z;Z>4Dao%Gr-lxvVknEy!r!#G~c;t3VUenxbQZp(4X};xp5~2SR^`&^h_B6BOpzP@B z=BO$@F04Z@&U7b=8Gki?z2=H8AzTr4^20|)cl3&!-#+{rS!`L8=cEMD6{CT}73B+8 z%3FEIv5oY2$d2$K_$X|WdIa@Bd$npW6W-a^kyW!J(zoeans`TB*B?%7>-wDlc7@xk zQZSSKwc`Hb40Z3isGv1iA=hhu8hrSaN_^xl`EfSQc z`BAd0I6)@xr~KQUiRU00tgXitN5AxX7Odi>-@avGTSIm4SqczPq3x#seBf&>++iBB>MS0i&iJ|4{`GYH7$$zFmiwk?&VK1zy5;7 zq^UaV!@rZ|x7ERt1Q(ofk%Tu3)WoIw$OIA!?yv_2JWU3}w!$7hAi6cbW~0w@N2>OF z0P;W~hN4UJhc!K$w{bIF4WB^3xE{(zC@W%bpk|SRE`Ojn(i;!IiLO;un|k`7mJd8j zM^536p@mwIqj*waTItwnnxwRoDxDwKTxb`7y|X1K7qt*hVZ{XEU5 z@Z$7=p628D^|8pde94*w;pGRAF*{%3L4;d-ba|j)4TyRQXmU)=2DQ zP0}?Xe4mrJMOX&?tfTskz)^s+B;gi?*?O9nN_F-t0sAHAm!D}6nC5%QO4CxOrJqd7 zi+BQYyMd#B6Q2)3xt6&pEg31=yvBH-_ zc3qi`X(3_uqIXgvLw*hcQS$Cx6u#P@CGDf;#yb}!Fk9kE{KqZzMv{)n$Ya+$yB zx#iTHN7&%#+%j+z;6Q)sNfCyQ8CIsX!98R2b+t7M9A4p69P5(#5xHwc85G* zwqYjzCfZ%b(0KWY-BK=e5Rhv|P7sOZhoN{wzPn!nO89J+i5e4sQ^Gr-;ZAr@nrT)K z8i+el1Hrr*EN@QR7J3aH7XTde)V2rWkz!>lUDrxx>gmdW zy;0+E9xpAL7>d`^qFU`$I7EB;)1KODLKJR_3=5c0v_w03yX!b9ev}k9@}IQ-;Oz2y zXOuUsSP@bR{}^Psq$9ht6ww%&Rq1;Y?wWj_r3u91+HaDaYbxK>GKKvwJN~Av#4}ps z&W6PH9!j4~X#|8a1Rnel9mx}usR{J3U9IFiyw)C;gfzk)0QJ4(JlPYRkNCv@xw5^o zGMLpsq2|C)<+|bhAw)rW9^pyTQ#I^K&y-G1jq@ngCZ#;hs1Nh1Hr3gSR*|R^-j5O& z`kG|b+An&RzKD*)|H@We_AYmdtoVQyUQ(>Ef0z@t-nA`-`QbxpyAa|SyO2hQrjHp~ zxF&MY7`wKkc+Ak+HN}y>i532uO*0$q&KuoW^R+=X_@i`Qj*=WmGUfn zeG_f|m_7(w)X>j!GDOr1SAveNjjZ8+U)G;5oFDm6xhxwX$qACgp5a~=w-A3Ho}Zt0 z+y(i0m+*Jyh530m@%I{k@=pHx{lNTz%wHt!vN_nTtUlle``2PzMh zA^Rk}gRT*lWqc)*qrQ@n?K9jZM~$!IQYk2DgaKXug?kqhsCc{vDkhTBv*nBFM1aVl z5?+DpgfE9$hzk_7mx+KS;uP%kv#^CO_rm{W!;0ti(*sk*JT>D-sOGZ>5hXqg7w`};!F5H5!spzoi6D6r@tVM& z6n{Hed>6%n2%k-~OE&s0vm%O4e#jon-f*=nRctgNl+ATePRn_?uwz zLn|YQNS{lXpoc?sHKdXOiPekb?+xl%~ z!tgYv3DTx`w>*jsv~$;D=5pM7K@mKNaz1Bi5MJ#5$L=xp{Z{pz*zNoC&G&;--_OeU zUimHj9WN&@z3 zqG6g4#KD@j*+-3z7otg%2vf@mN=ckJwD^JeLA^yIbQfFE_QI;XPSB8NJtNy_{+q|; zHi@WN)9$%3UtWl=Uzp(ZvGMZW1c|nyYfv1moqdS9V_s>!lp zX71wUJ7+y0+O|um-?3s-N<$c85P?eoNo&xOfwEVezO;m>(Jbe{cfJ}0Q;tu__qP|G zl!@=*F1|Ng(Jt`))giM9jkQ^N^6%5|dVjJ=Ro$G;FTDc%q9$_g18TaCpYvqk$BtL^ zyBk58s5IshuN#6F2U5=3Kw3G$5T({*hg)ha8*81**t^K@CjCY40HYlwrUV1jlmK`9 zC5$o}iN@}4wlloEnImuZmv69c!u)_Q%}V|kdGD^|E`2cmh%_2+5EGyAnJU~hsBqh~ zRYiwi;%ZOoj+Dcj7{>rB*fKC}CZIv!YJc%8vHyG+{K&~ztnMvd`OK@2BJ%2XeYK5O zDA~M`Ir`NmeKm(yn6vU~1A?41-qXB_>0#EsVYC^^`~XiBk4vntp*acf&-9C}eDTQY zy8CIOTKwF4Jq4=PgyPTLNsd_$&^VhZ7@th1m&B^;+j-))oKs`dC;x%DV4pj5J3STs&j{F)I8KRF z2WtKnuD9$6OY+5^*Lcx|NI;Vz zDdR=Yf_A=iIyvvJwJ*LO2NgsTygx9N3ojQx611;mlNsS)D*`oL|5l)8o#&RHqDFve zglv>DhU7M0+Wjo`+{?(zaVYI0ubbnKA!HKY??`c&x`0l}Ay*qs3fLD`qlWbb>}xGd zz>?dGNh>qBRobGgXu2E^r}N5pz zk}tJB17a8TrCcSrVPdeT4__e7s7!eGx`-O%6B^xcF+VqeHU#b4jHtOH;XP1_Kv<7; zidyG3sk-Wo<~GYy_g2FDw`n|WZ{ex-GwE<8As0DD*w8-my1e$0*TD9Uk@z0FPt02{Kgiif*(+0Vvkp$i%`$ldB$ifYG&o0U{C0hkSHo z29gNtVvq!;rwoXGrC3ES+K@P>n1i=Ur{)Ybsrh}bD{@Obi?0|$;#q)wjc36xcunOjTZakDR!he-neoxUQzonk9jc^ceAj#=={xh;F2IEi2t6%Uak_uBZ4NS?*foKsmLUP51+jvD(pcyj49vY-A zYJkL@$Bq;6^XikwBIePIC!`nc+AVOhNKlqpQ@uQ=rI9_XX^7SopeBRKB|wq7+O#ft z%0sHJx8etP-YjRj(Wl;Fe;cb?!V!Bz!u#PQ1Owy(jY#P3Bje-Sc)29u{ajvhyuU=k z6a?ePPC#lUM#jJ;$-HEgf`Je5L0iIm(+}uEyyy{;U}JS{?RBRkbe%w!ygUkMRxIL! zrjM8f$?@c0ly$|IS>Zi^l2S|Rj{#|K(i%<5D@uI6ljD{5T zDK2Mc3Gd)!j}D>hDq1g^Xx__5#Q8CuG9XOM*8LSs^FW1ar{2VWBCdR5cMLq;d@o=U z-d|s+RZrDEwkYV>6K%YTJ<-9xW)v2>qp(;=m-EZCpRKZ~fygu-!2Y;{s9gY50j`kbO#y3C( zl!;v?>p|Rmvu+4@OI$wQ1dV&{=96``pgo)Dm4Galj98=zdTW(IupZ5KJ~~qJ>J~G> z?sV3yhabdvDGy(7)Q5%sWgwk3p=g*?6Ze+uPpHYc90ML?v6m8FiId@qU@Sp9b?65g z?09Y+inc=bgQ~^>Z68YEn1r7MxtsY{Cdf1bTWra+MC^$?YHn}Q==M@ToQ}1( zlu2^{P{A)hm6}97~|J5gj+A1+Op=Cj4FIG#{^&4ZF-AM;a%- zD;H(f^!zNTDH00CN9LzCiMhb~xxj!^voi7+YktqbW+<@mA^xTL!PtFTP3(ys+6NtC z&mtXT+Sn$YU%5qArT7Dqe$u2{CH>V2!pY*(CH*UtZ%O)H^6Q$ndz!Zi$f**jx@L#x z)_3H|4JTvAx60Fym(w#!1me&CVJ-hsrM%_NSysVDMmK*pvdAK(t$2%26)M0uD%!Ze zqNzGBJit@Z%audA`a@BytGaqq(Hx}`^Tb`>=$CyXA?xW8S6+JJxGR1*%Nkk|IkRa_ z@x>8@X8cxhZY^9u2z9t7HVY}I$xic0|K{OnW-Ldgk4*C8$mrU;Tt(_Q814#_zM zvoDKU-F}Pka;lu?K;@a54HNFFEv+jN++tSX3^+pCrbrXXLFl_(0mJat2<0qNT!YV^ zuo&O>9BcVIsCjmyN7+|K@1Db{5?`mnQMXK_`!zfM#8~BdZdohBubD&PGoKQjF*_aq zF|vO|mpX~L^+x!JX{qxr(J$dO%;MNato_YB&qN8OBJU~JvqOue^;mW$qlpi~dNOGD zGB-l)4u4{!Y!oK?qu1x10(TERw-wJ?r2{S89EZ5A13X0pce>k!uzbF$FYSAeD6w;8sO;t^?CU$n)#9 zkbp*d)9?Ff=IQ0yhhxgM59<=%&!iUQE)}7Sfm_0R#5m2Yv}%?|yg@ruZ`tFaBDOCg zpjDfCi5$||plb(po>Y9*ZD@MjN#}rost!J&&czKf)9~0h>&k=<#>Slr7d3jQJ3{~U^ zH?TH0B>abbk*iC%j)#L&E#M^e5U4c}pqT4bk_InL8qJ$;$~)GMBs* z=EJiR-X&*$8`f8tw{6b(uO+dL8i{C&g6|f+T(Q&OJBbg2@7Lu!BTm0|SrQ`=#ka5P z&}7+!_mAmvk7Sn177Npulky1M%z6)|YcV2fKmydb zS|SM&SBsydnpjjnj&*$c2+B8(yX<%v1f@)|GeZ!R=p35M8_Dqtw?glLvB*WjBSh(9 z*|+FBsqhn;Aihi}ItB9!c(ZfN`>;r4-OAKKYemAlKOYD*09;vYP#)O1IAy&zKibYh8;q&CCnoFv<tO8K>1mZ0#=LDIfV=u*8PhyzZqa3<-7&2#pJQ_mgIWST z934KRXf;QN*~g9d@J1qSpP(hSd6teY!JZ6(X?V32onzEHEStDZGfyEqPt>s-3Ql1B zME0!~t05?2p!jT%3?$-Ok%$LM90$;%`KCPk45lO9R|$#ZxY5%S-rkZG8{U}9*?ZHZ z+`;`+%0{nkM63N27|;y?TT!zeKqgyOMy~U(nCyGVm)rmQbH0EC#14(Cc<->ea&P5* zKvHkzd7d2jF6n78tF+xWm=hs&@I6ro{+X|Zm~pAlsI0?sV`}g&B^MpP=NlRvu}2#S9Gy=-wGZPr+n@~cj}DElPo6f4-8@M*HuK3T_%80Ob*D@DNX{M|4 ztw-hSo3hgVX!Y=tfzo0uCPd3Amn*B!*I_KMi%uaG8(z$P^-5B$!w0kvADHK?lV%w8 zu?s7WK=AkI#0;AWp5M-TPR_UJhxK)$17E@Uk`?{2nHH$A&o8l}&ot4JJTYC&$9Bq^ zy2dKlkX(3W2<0`MsAv3`#z&13A>_sUI(xa_-o!vSUE08H!hccE_UU@MefV|x<`w~* zYfXUp`H@&w_~f5C@{cMV_=hd3u#jr&<;e6m( zvB$Jg#8Lc_$QZ-9^>F!g7Zhdgvy{29hnOksYMb)iN-|MdVIp$*^e)b1X2PB*dlc|= z)U57;d2ul5s>Kz|`)FiT39tKSjK2(?#HKA5NncZ!jFRgrB*pEn8rw@r1_NZtpx)zK=dH zSiyXCFL{yz>qe4@kL3h$d1SC<_jH1|MT*#Cr_=k$VH6P%KF^;Rkhd7=Wvnp?u8GyH zx0iQ5%PTq9vV5&_aLV9Rb37TAEQXbdBD)j6JWiCy3K?J+tzM9Up(eZ%AB1ilEmu(v zm#Zk#J%18jY-&w-ugs~jD)6suG+}qLJ~Eb>7i7}$!5Ma zluEVj+VZX<-MYy*Etb!dOzI_ghGjplH=#sQ;9WL{5dOd{=e`z3NO}al-QUm%RSj8e)}wNU^BiV2pSx^d8tG$054F0JW(o+yYDKsTTWo}L%+a7GK+ zFJwY+wJJi<4O~)Xal4f&L|b(pIlk5^#@Wp%(+V$vpHcQCd z(mt~ag7%=QHar3po&&0J6Cbiil_E0+5H*Hu5ObKCj@d^CV&|aL_=4ggd+$K}^dAe? z?c?`6**5L72eUpW{I*D8HrEGUW;qh`*f%eN=_+h@2~5vVm(xZ+ zjKYM*K+TGXhx>q$erB+-(_WusnBj{-iqqI3>c;T-a+kY^l3~uVmD`9sC8Yl=2#YnY z+Rc{b1|y9}O)ji+gLB5qvfN-a!QxeEampJ*7+pQQ7)r?N@-nSgbDfkTZkE_f%AD}I z%R7*{op0zgJ^t?1Wivrg*w@)#gzT-l^Xb&k1pVMig-Ls5a(adPZLe$W6)s#N7xLRA ze0;qyhl$N=JT2dYQHlC?KYlIIn?LGjLQ@=#eYmjdjV|~Uxet;nVihFT&l*}ceP*E* zt7F0B)f{~_e!85u4%t|TMZg9A=qLGMk3X8o_ee-(zFg5Y(r%A#EjYcv)BGSg+R*(% zcqQnok(qu@p4c-FY47@;G?}Z=@*R4zBHl-?1tapVS5@1X_~~VM7??d>Y^V2AI28FP zJW+dEB>XjmwdSe%rc~bSg9>SSi0d|DE=Pku_i6pT;KLL){p%vn6p_fYQmQZA3dYXr zLaM|WfN0XdnD}!Po&^(WK*ZF6gl{uy8eV{X>kqP|T9+QNSJdGOV1O6?)n_cV$PxjMQ93GX>)L0)`n$SWb_1&)=n;HZk~#@Hny8<*q9-yS(04D8Qpb%L)j*FU~|>l^!tq2^oFzJ|fQ) z(a|TTzt##MD>g((K~lsA>zVOr$>+ zhW@7tK})zme6yu)xO$rJB{SizfbcjOYgxpToR|1m%PsqvCLTqQ_O;HuCgEjk$?Qmk z$EX6>J-CxIJLdKO$KJbuM_FA9|9jrKkb4FQm?+Xgp^c)LLR3W5L_vdsM#L7c43HpL z4AYPxwzemVo+4>HX{$Z7Rg2bIwAPbjtwn3?peU%djn-WJRi#strR zp?0rO*2K?rm{IJ_-9^%4YNV36(0jFw-ZF^U}Ok=#Uq>EO+xs)C6S#R$BymRL( z*)5YJXW5I0x$Hu)9QgCmCWBM;#{0WMb@u7-FD1;?!e~v6BD>{xtAUCzUDRGHv=`}x z$AFSJX*^#?WuCiYm}jmS77RUbs71abB4%owrx#8W_KfSKF|laUkL|Io&rOx`yZZ*V z3YJev&gBAqZiZsu%6IOdT=_mcw~sPeWYp$#_LpBV1>pw0U}5zAHy&Qi@DS_ZyrbV* z`>_UnD{*dyo5p?f%;UUs`{rz%T=Vc8ZoTr1Yu~^QvCZY})Q8QpM(1K}epd?*LjA@( zJM^49JM`S{Nj!nv@B|oYJkDR{KTx!Gy=)x3GykfKGJL;-MLEr3mn6Ne zf!jWP@*scRemt(Qaq_Vb&pDPizU3)kds}OQ*-+sT{;7TL@%{2IJgFT-^RdkIVw&if zhORUFGJj*EV8}?rRiEpVSZLApty-w_ybNfrxs7(HHwPD%$c&Oan*SlTlWLU$R+S_5ARN+q5V`Y@6ME@vI&*n zU%C6FEXlouT@6#8Z3wf>_|@kR)X!`0d93{Yl49Cj2gS8#Tts>(%$UgTexdyC^U}Mc zx{+~SxUPp&ENEYTCy`t@jV-@BY-})!&2(wg@9z}> zTt82)2irf{(0TDnt|%B;8DU?ikYycmF|F!yZQW$5g)pD1(ib?rI+-sT?w)by5E#m# zpVaWz6=w94`NK^e=KJD$AuqrG$Rzr2V;L>YYT2%m`BXQT9xIis)b%QQD4u<)rQ!^qK-d@#g6&fU+<%6(`I&j>U8=Z2{~M*Ux~ zWon!`Z^Ql4CRE!Bd5xiw#aqK=TtF$c?r{6Q7Z*RNm!nq2?Ph5CGesU@ovW#@+X}tw)krekS*E*S37`2YY z_mB4f?5P14^$k}=FC+l^lpt2~M@W6v{6rS)ojbWTe*!Ph@-fii7=-of3cf7b%_3ijwbLFo>E4m;0eOuu}e}85>%l9)`B_-sO?YrwY zyfC5d(0X~#FS>gjWwYT$ez@_0hu&Wk-|^x_Ukp9;_qN9#dh=ILqWA!c6ZkI0L+@NX z@90O{O6SZB$?L`2A$Yx*@14MSz5Kp3&rpQ7KR9p03xjRXY>l-2g11!pnU#t{CfgO| zExT5f&s4i)Jbk@<4R8LpZC_FTOd8X?-@-3tV(X8)w_rtV__>$zHti|Nim>sun2*qi*oyL5O9x9@qYdHb*wS6Tow?-bpOs%NKZ7~%=)QHlV)s_VRGv|iwYALQ zVpZnuD~v(r%FdmgPa7+1*J=X~?rwR#zI+YC#_RLCR>m8qzTFW1i+i70lHMi>clKtJ z?%Q2UGp;hpHyr)jiw_pD@=rcYHSCk-z2Po-4vTMmoD$DONtj`%h3_>Wx$eL8EmC<9 zmae|Iyjlv1m3xg$Lp5A8(2bMQjT!fU(J=KHnd-vqyFAynQ;K~4)W_z`47Yz&-aa(% zlhF<$g)>9#V`(!oAx12l;3g=J!mw zvPSIv*I{!0i$1@u&u{8;x#o`6=dt>HC}&<|%KY#H z6nFvMdd^zD!^88qZG}WPYX|B}#bz&``Cz3n&u4;VHEZ6GIJ* znm6dgx9Iat`h2rK-=fc3_4zh^zFnX1(B~e`L+_LBE|-PyjJU3nu@crV2T(t}uP04M zZ)L_ZZ?hKrY3BC@G^2rHmd&(P$?t;B`k8ub@rg_+&TXT%rOA;?8N=pwR9Ua9~Uyfr5jE^mwT zOD&=BJ%hB~h6A_H5AT{kgKf-5&KbB)mxSLgD;us_Z+{GBUgudj#VAQ{XAANPwi;yI z>^dQmkteo0=SC*4c&ktQI(NTE>fe5LPI%XxD^H2Ejh^56bN#lk>{n%e&1JQn@mI4{ zVa{hv;ITR>7gju8Z*@WM`Lm--s}2cNd%R)$_V|jox9{209(u50`y=szA}L7MkGrCe zeOeO9{vNP&s?67AL9egp<&sp&IddRoPNhs@hcq&U3Ph=)V0V@_?%b6*nfwtQ;X@;%GABUjw@|97@mH#6 zJd!z-x@05i+O(@r?Z?_P^s}I%G$ymc5Ti2Z5!n5Tr=K~6n9PZsY^3-iYjjxvnG0*X zG50P@OWE%l$kb^!+sDh;K}|d?_w!f??&Q4zI->r58zTl?S+6CBs)X8DGO(W3r~UgI zR}f?>&S(>tdwjITflFX!_Wk}Io=aY!LT#;!tL?I<~0m0ob4`tBwsg7g1(Nvw!wqLN}-O$AsW>y)Gsu><9 zVo(-o9j8 zF&?its=l&)5nsqF|KaHsTsc=V!FV9jQ(tVe=~CM8naS#N5-Oq{#=vZUQ^u7Knljq-Y~~C`yBSYXQ^i;O zs(n<$7sf4|<0dOtjwcbLHa*J+#hz?0$^4byrQY;d=5>(EFm1|?V;hb+0Ac#fv+biY z-EvjWu*N4lOG}8(tR+IyYABz>_1gnDBHhaLE4hYkc)M@E^xJ(hVc7Axn(y=;#q7~m zEvNB33EWnc9osVdGCA*#W_I&ThrFI4TJmC>G@^34Cf7*vivdDb(ur)l=h{#|h3$T@ zN#4z*5pBX2-Rz28((y-QaZ?|kNv-1(KpT!Z`~*s%{K|)5?Tk)=kl{6RE<)TNIW6;_ zRCVTfIY}F%TJM-9sPvnl%yp(W9h7tSTx=9yYuGKmex zOcbZvkH{2L5SfKEq|76nc1FX*%Rx@A!QB~8)@}NE<_u#Jd zYn#OUtLiGR;`@_VH5Ax=_&OPu{z?_mq|1*Q_eOo7{!N|__&xT!j^D$@&Bb*rxGGjt zPBN@?Ee`E2V7@+tuHPqDyYg(p9SBok`XV;-Z5+|R4awAAe#KXO*4dPcVaY1~2<-9e zC5(S^b$cv4?u5~xGW_TZO+32gQ#wt(H>zd!jB6%YmP64Ed&2Frhf~z1iScPbJjJ#yY#udhB-l8}mBf(f26m7>ua9DNHU9WOIZ@ zp+{%8r!m0G6SA%qeTvs@D#+L$=lz#Q%5S>tFD-4&t1o+PRZH`{;wL&Yx(QwO`bo_# z?Zv<7YS>eLQ|G(w=T8|tZcll`&S+u^Rop@TcSiRoNax*iOghGTulkr|EKqHej!6mH zH`?>=dsI^5;>iuXu_Axylz(gc+KM@y(ZlKjo}AegnsZfjGQr)Wh}(|*Np92c<78oIP}n>6XJhQF5IG^g{) zZIdC6&RnXM=5(?&jxL`p!&?9QJEP|j5;t~6KS@YhD#Q33f_#{=V(mu|`t!~xA4zho zg9z@7PUTF05uyFcH_X5Q4uyJoq(0G5+^g>(z?x9L;XN_P+AuLWHak{+<*SgL(Z3uR zs5|>N>?+t9{VhTEmep0!UlZcyQlf3yhFyuB(T5u4K77^i z_7e>UmR6wJa8+q3Xm)5Fm(ef$1oHg@jKOhh$7zEk)IWA@A?WZIVd2HOldEN1Sves>n&IA`%$}dTAGTbqG z&QAw^Bj=&HsPkEQ3i9dZ&dC~O0SBhC(qZgX_#g?6}?r;s52T!^i|Qd#|Xs)Lil1h zJEIGTkz{v9Pa+h;($47VHnfd#Q#E`lEwjcv(cBpcNy^#y&eGE-iQ)N?go~tHN-uSh zC{|y<2+S9l*@fM(W2W4T;aOp>5t)s-pQkha{dX`rgYRCXUijSak%MM~m}j;!CdSrXP&f8|Dk8J@)+(%`}|NotMb zl&S+SlG@%g?j&q>_ECK939veD2aqHWknvuIBuGkiMvl3slm&QOK^Oa^_v#7fy2-s1 z7LR2~nelU6QkIoxHgG3QKY|ji`3McE+$LdQ=F^&6!kSxJe(VE zxN0utcOw+rW*?XE^(Nf4pwiIS3O$z>tSFJ~{fUMdglFLDjP344b}StTF(aEAt2*w# zL~olY4PP$x-|mI{m5?kU#9yvwWTJ{UY&d3l6D88l$B~LjNjDmVbQ0&t#%EJjl;UKHtjDkj$HMEo0_dr@PkZuFlhK8$p+0NFRHqETiMG zud2`I>vNqx|1}};vvv3r`aGy(Ueo6{^!Y7)en+2;{JC1Phsw;#>C&D!#(w&&@L)Rv}RRgtK-q+h`j&Aza z5ZxjCYqI<;LSJF&I(CU)Ni=l6+&On9A7Q3~N;fpme4mDx%p{SKnlZmbXFS1vgXmY> zIAp(0=(eo=UZJ1jGnS%(yyox7(lx?=BbSz~>Vecva*eiWQ1}r(DJuFy{@vS1G&jhS znf5GUe;aEkVy?Ak3WmWpGEopO^8_Mf|;2sU;V)R#y2!ee!I3IpZOle znv-QOzm!iv{gxsBcgm-c`$}>@)s>_vpN7s&;t&2k3RC+h^$q=XJ3g5D9ID^WO;Rxf zwb$GFHZ=8FH;^C>d%EH-+uk8!ui9!iiE)NsBldOuOM5f)z!iK?Lxd&ovZZs&$F_gUvva}XOj7*MEgtbl+neM3kq1o9IMa+; z@>uR%I@5Y~|M=OB4WH%nU@^XlTflm-YyYzntY-DI2s4=?q*R$9=St~N&yQuk%KU8K z8|b3em!GfSzMebtUS>oDexCV@{1OB|L(({J!#m~c%DDf`!_P?G!{t$TgH*ph#N#FB zxtm=20hrf8~WnmmG2l%M2$U;TC@aH9*dTkr(mJ9d@WC8C*coU4mW z>)EPYP?MPw=(^Opx;iUZI%(5Q_8Q7WCwPzRjSKx#P-Len%n-K zr30JxS2xt|cFtX_b3ywX@43F9F7vx{OjXwnwZBjEV-wQ;77w*QmQzR`wU?{^Q2V2N zbK2i9PVLHE_D;4uN$S)gr(FB{HJ8G}IdwC?ubEqZ#wp4#Ddl&?*G4Ko`i1Vl4f*Bw zwF$2Ll9XRcm)p*{(i(ODefn+B)nw*dXPfd%4wc`>t*h&Y$}dUzB{T2t9Ntx_Ue=uX zV@Vf(#EtPB*vh9DN1blSBTx zH3dUdvY|6*kgv&vAFb~R>9XMaklfPGs~oq+$Q_R(W<65#H$4$H^5aeU<8iLM%x|n; znBQdLObjEBzt!S@h4a_aJ5C$kf4cwP{(pzdj1Tkw-d(y~bZrD>9^kD{<2mC^w=gc- zzE#{PwQlIT*5%i=X|piiz;~g&{Mq$N<%VWiuXI^6JxK2v7saFeqR}>8dKxf<%~>XEU#nzWoA>DNg{ z?)sE%Uv~ZQluaLggYd8UO4dK2U%F<9mXT)QDMxp1+9LFv%|rZZq3_Di&%M$x$VPQ$ z?(e*8c=a>TflbnHWPvfz=InQEN(tSRrDYpnU{RLt5qgeCOZrb;IFvs&6sg~Ae!E4! zA}ik^@;kEm)%8=G<=2S(OP24IH#=K?3AY3)=RN zSPt9oU)YaZ`+vs1WCQ!s%y^$~^vL$LAnRA!`}>IH+L!ugtR2|#rJ?$g{IlLI7-%Gq z)Q8kRbbO%ytGYgt&;u{68Y)j+zl*Z%xlZ_ZAuB0)<&zk?o=KSX>y|IOc8HWLW_ za~6+2l-`aeQ%kDz%#}36Waf<*jV0NeaEHB*)w%zE2eAwBzkQyH@{r%NO6)#HKW%l2 zY!g0LE&C(v-POhSj5}P0D)!i@=h6B{dNJC{F{oSyR_ zMgITBF;PBC&GGbu2H$J(7K=Ao+-C9F7N2PG!4{9VII#F7Yj>B$cUk;(i~rr?l*LJl zPqp}9i)UNxN;`h5@pFR3vn^h1@xNPqr^U}%9J$TtRa<C%4SnUu|)l#h+Rc%Pqdf;yWyEv~sUly1?o?e<#@ZF^+HXeOB)V zi{EqUS^TiYuUY)E#eEjPXt7IYqoqHe7eD>mCcg`9{nlB0h{fYAj#>Pt?-==~Eq=h_ z9*eKD_-czkZ*j`vb1j~4@evjuU~!qn%Hr2e7VIf7N2YJ ze2b5@xW?j%7Dp`3*mCK$_&$qox47Hl%Pme>e6GdETb!`i)k95jNN@2tyyg3bPUm4K zAGh(2E{Q9%MdYw!{BLn(pzS7?82fb(8hmEUr&`ur+>&Tszq~oswrWj_O=oe{@bc?N zu(NDwQ(|dr>(WaSEzK7vRyVg?(6&-ct!-;v)p9|iZOxj*>NPDFs19paooCYTztfbD z%icR9q$~E9FJ0T_|46drI*On zueP*u=}$8J9$UZNwmzGhS1fH`-KM5bt5FF`X3F#$T|O&Tt!|!m*kS6#1@jUs+FO=O zQ%thwlz1+O03{(k|#B{%~{pjtoch<&uMO5wQgzKs&&m`WO*vJ;-YqK z?nCXI(cHGMdFkqTEvfdltd+GNrbqIkc3OMe|4eWC2}@g+H?KaUtvU6N`A0%$v@LCG z=kacR%xhVW_2W?e?af} zHEUKkFKziB>z$#8600^#)vE<+mRhPV;@qgiDZ=hpM-`K|p>e1BpvmV?J9z4G=bU%x zxrcuCbBD}2W>QgUfl{l6%0-V;sWod?=~1xy0NZ|T``c>UqdLd*m+s%1@n*BdTpcU< zLGqg|-Q&fFiNDX%yS#Y0fPc!;{a$>6_*X1FXfY;u#z(0)EUlh5=}Wp2Ut;M>FMc1C zCR@73i)VvE&9-!d7yk_Mi!7b=;^WA#w)A>0Ui`np(i<%nKPCQ)mfq~i3-4x2Z}sH& z>{04=OK-DS9*XJ!MmmT%1t;;YL&VG5c-1FrKy!x?*ep|>Xay>H5WIx z+W6IKjk;Jh!&NFp({grF#H`Y35ijGEl~0j}7PVa4Uz=LGTn|X_)?#}p@j^>q7GOjh zN~axu%BFKMJc0F1S;yCto2=)&@TdF5>}B-9W3#;jC40!@xN`77mp%@&_~1PHkUZMS zIc!E{rH-?5M_TM?*{qer(Uk|8{+7Q!**rpg;$S1+JI(ZinNtm2^KnDF^-X$&_{Kww zyt49*8;xGg7c6eIb}UX>d_i*ybH;Mjo2Q?&`rTH)=hMUMuf1e#TQd!}-L1P?Th~Y% zX*|^WQ)BdcE$!Av|F>dS{%hT2Q*H9Gw7B8^4rrYqlzijY}HvZQZKW%a1qo!P%Jila%SdNXrwA3Yv2h7!yB^R}> zS+YdUUcP$i+O>&~opb)j&YODfv}%nH`k2~0&DiOkZPKfmZg8)~iDM0&I@02!4NlLp z^ic*UtbY6mLnjY6I9_XT+Qz4jv9yg(I6E_J{7kE7afj_s>M#>edzUP#b7VR`FVT3( zyo+Qxw#1+XYns|u>!B{S)LbtN$(^?BLWZg(tYhli+E`@B^z7T2ziKU3TGlqV>LK*7 zwR|SL`66v^THAUzth#hptZHdGK}uA7xoGVLYWY$YFKb#a5xTu?#i4FITDNNLs%5ye zdd&r_ZTMxL=Cy0uTbDPRT*!KXmd;XaNz2lU#PQFrU3-Z9w;Zx|)#sqkS=GVP(Apt{M7v}>E&+EYuK*E6!Ox~O?*x=`n6#@wO! zJfjPm+g7fT`3jZh)+IxUv!-3svV`K$g)4q(fqBk3&8PJ9oy^)>zpCv6%wZ3Kf7~4T zG#PDLvxEUKrOs(uBa7LQvZKh&MXOqPQHlvpnU26z*QGYy0Xv7jTpW#D2r-a)k~xT za`bZ|M%2&hENNS_WZ5Ncwuhf{1|3j$-=-56oT;5UWADB^)J;@Y-iZ&Nza#>a_+%~mgA?Kxa?s!4Bf z1V1Zh*z&Y`bryG=X3J@X&1asUo2_2LmW#t_TMwy`@>grpQ&!L69;=reL2t9w>#*hN zu(IWu7(uV{aFgB^Tb>Tr*z)YL<(Xd(o2_2Lrsr_aTw9(a=v5wJ((ADKbGXK~mn|ds zx!LM%v3d^IS-qYS^eT_E<>~6j;RVLeeyi8-;$8od45(NWztJpJ%}#zWaNk>eud=FmKInL|4{hZC0XuD2)W z(Nps1kLA&a=Fut?gd@?|D2qlZLyj^iHcEvwdz3-Ynv9Lo2tD*g9eqL27Hg#WCO#xc zXp032Nctd2A0+w)Yoz&x4ha(4VnJ4)koa%;77H?b6CZN4#UPypoqv;l#HOz?`8Vm2 zac$qx77McJ8-8HrEf!?>CO+h7i$O!_8~?*5y})2WM&HmOM_UYX`dTj(Sb2*l)uJO|GK=T;-!2YZLuJif6b@DwY1j4L71@huj?;t z^$n)}UH*-}qb&wGeZvo|yu~2PH}N4yTO9TCZ_TK$oA{8UEtUjb`3p9r?thTB zb?EvLF;o9Bp@jqs;@|dv(KqqB{}V5y;hT8r|C+WKG?c!!FCAX&Su9A)OUEZ(kftpL zIeo*Y!)w09Aj>!L^nXKJ9F2MTm;6E6^feaJrmtxk22A<}L%Q@0pAK)*HyG0L4NdfD!&!Z;NB=i`i-RG)i4Qs2;@EKYZ^}OsmHNY)%fHal z|84#?hBWC(#S0%~^RF?a<(qiw-_RCEqS^8{`eCbYF!gWs4Nd(U+Tw6lU+Xdc8@|QC z5Z}ay9BpxQnEc!Fj|j#)m%gUSzfIrZaAe4T!w)Rq;$Vnx;zN$MnAeA`eS;yb{{{zP z$2WAy(H4jI(zo(~!9hMhk2d3eY*fK02ESO0A)HnijYb@na5NGL=hH4;c#&{8_%MFH zywelia1ez4xB4GuKR-Q}uB07=xHDzTrJ%5=xTLhK+?1Z9%F2~qxMXQ*8;%lr(a*++ zewH%)Tsljc^i2BBqDkLTMn4ku=`5Ai_xVBCQI4NWJIY~~e z&`vkM{++z3f1zCc8DAXjsBHO|`p@c14g31Jw4;1|U*7b8zkUqY$s5WqA5-Ig`qD?8 zzb5}#TJkTHv*-H1lb8JG@^fiJ<)@#uFW%(p=hBAC)%WG4|L4|!E-jR+LsP#d{{@A9 z`xna9pOJR7qjK|~l{fY8myh_Gr45zqztpL-Z(2iEUdrFsH&m{^&zJho&3`T})NuLt z(~phHP2Z6!W$S*LG$yrUiE>zlBVck_RiZ$geX*m+^r zFOnzGcIg``o4!vwS!d7nf9Id%Uo^A)T-s3i>5GO*Px7CupGzAmSKpU6^S{p*Zk84* zTZh^F%eTLkT)L#x@8N#^oBGajbL+pT*wD_lt54JZg>w4F_bfk`Hk6+~Q{!2E)5Ej+xwN5j z^+ndnoBr?fg_EU)a(OWA+vM0!U;3!CXX-yoi>`0a^?xTX^`FbnrG?r%{j7cQCRaa~ z7AjxgmzVyZ%g?2YinBHB=RfPe(RQ@Se?g(64L4h!uKpe0^naf(;VdnoT>nh{XZ59q zv+|O@PYac=@AIYpbNRV+LE%4=|J?Ltf7%(d?VF6m&rdI2R#sA0Qc@&PQdCsnu!M^W3S#-Rix*x&EEf4Ne!jfZ z6Wv%O68vxVKg@o9dM;f_I}-3jaNhohU)HXFxUv-5g&lVK!V?gEUrzWw?PT)x9qrP0 zwCGFN(Sn_f*mCJRzVJSXpD!OTDJdu^D2NCYL?T1wVT9IX zBv_D?JLPNmf`qnMkl~y7f87uJ*ZnZv9{zPdOy9quELi#I{==;;eLw17_rvt~@vr+~ z(k}jWKg_K2n16IX`Dfh^OH7zhIia$$LZGsuBJQw+E8_8zeA>kePu3D2#?O~`dTt%^ zzt#UR`}yg)bo19gZatNc^Vd)LbiAOzK4D~$T}YYQlkrVjvsnwc@y@I--1@*^cb(wu zi@uRJ*j)!?`L2EB;zVU-yfPjy5D?$^?9vAwxz6uR{G4&|M*X0?L%*4lZWt_@n=hFcE3ns{?;w$Imf+-#nwbA_>NDesZ5UcH9* zGxW>ob+3f=d+YrcJGr`lQh|2#n)F;c4p%>8{G4gAiyy-OB!7nN=LJfRw$FR!#n)K> z>MU-sxY6Rp7K_^SQ&;HM7)uGi!6jhD>_AoU`TXIIoa{XjR@Ifr35;-ve4WQ{^uk&N z8}5+4B;h(h+ZDnde!A7@Y{3rSAaLxxuvp#Z@%toP6{x+^x0+(pS@Zpnbo^s6a?TfD zt{U6J9THpT^G5K&*%TX+hS=lV3wxRL<$9k9tF6fJErq@0T!hG{J$|nj78$YO4nIF) z*FXGpJMb^DZ=m=_gD;X37S2p$+|fvQxUv^p&bFVv*v~ooMyQ{BIh4enu78iWo^T&f zyG_B6&sKC6W1nwM_#!!BkxzO2 z4lgW%V#6JNx?4nd?Eqsxjuy8Uye}DRwPg`lR5DCA+fQHY`-hW}xaw!gKYYI3;fv&ig_H65{0zWw`4?Nxwx7P( z_YWr{agFFSVvlcy_#!!B;jHxd9V4VKww!H0eX;K!PDbL^Oa9?+&6DxvMLXm3KQes& z#g?<}r!V&X!^ue87Rf*M_!f#Uk`orrN{`<$Li%FM+4j>H`~Kl%ByN}FAO6-n8DCzs zGd@2teE!9jv+buZ_Wi@jNZg?0AA5Y;#TUs53umRr?-(I{vE^+0>5F~;a555C`5gI& zzco+Bmly4f&*!&LhHL*~%h~qR7yJI7s&|=C*$+^`JdtPFSeX*KYg+9A5KQ%wn_f6$G22`k({t_R(kx7 z5z-f1&bFVv*!K@7BXRwbfB3xn?~CMwg_H65{7_Ogkbj6RXWLI-?E6REuZEwe8i%nd zdSY{>$IDM!`0^g6%-7x6-hwReYC;WvM2X(!@iT!JMw#AA8<$-l9n8P zJ|$iM7(~wH)R&Xr;~MS|n~fgtAN6VGue0kvv8(NXw|KTB-W}r8HjmdQVe-6qgnaw< z{e0xxUymQd)qnK}cBK^ReBKB?Y{9M^`Rm0FUsp?*llZXN<8AfABH`>w*w0VCJwHDp zE9F`TG62^ z<><@$Vb_o4eMLXt`Tf|nebMd3w`#s~tOQ0lgj4777Zc7O+eB4tIop2v+P-I7WaK+8 zlF!6%x&Fl--+J*Ra)UAsrak^3;eWJD#FC^TY02UHCF%Ny^IhWlxTdY+bJTpx$QQ{8 z3um#%PZ1vO`dDl^+kX0D-#?s;#BD_<4WDl&`64-CX;Xb3{~f|3`5=m7(^>QVkaYaR z_hThx*KjcX9~kKn`J~5Bd128J8}9Ja6}!V7Vz0;JrM-8RgGvN=aAF=7I`Q;?(_=oR@LMH>CZ)5o) zIbq%Q`9W@XR1^L%U$iW?UCsIaNP7O^`|}QExgPs`lgk&$2@9v&<8LLLUnimc;PXu{UnD0iocN(xel_9Y(idCKwx7P(_YWr{aVybTk3GHt=8NQn zh12cvw-V0pGom22oNYgSvF{&FM&i09|M2fS5>p&+BpKrYQA~|8<^m+V2 z!o#I6ww!H0eX;K!PDaPQNd7-g{(+GW;Vky}DZ<00udwB8`{|2)|8O!A*MUwNKHr@2 zMRLNz>GSx5gojICY&qM0`eNTdoQ#h99r>S5{(+GW;Vky}DZ<00udwB8`{|2)|8O!A z*MUwNKHtFcMRLNz>GSx5gojICY&qM0`eNTdoQ#fpiTuwX|G-Fxa29*~6yf30SJ-m4 z{q)7Ye>fS5>p&+BpKtQ`A~|8<^m+V2!o#I6ww!H0eX;K!PDaQ5p8VI6e_*6TIEy`g zituphD{ML2e)?kHKb(xjb)b`m&o_d6k({t_`aJ$1;o;I3Th6whzS#EiOW z|G-Fxa29*~6yf30SJ-m4{q)7Ye>fS5>p&+BpKliVA~|8<^m+V2!o#I6ww!H0eX;K! zPDaQ5k^CP){(+GW;Vky}DZ<00udwB8`{|2)|8O!A*MUwNKHpICMRLNz>GSx5gojIC zY&qM0`eNTdoQ#gkkpCmeKQPiEoW&kLMR>UM6}FshKYg+9A5KQ%I?zeO-<&7o%ZqlO z#~&m-T>4_m+4j>H`~Kl%bld>>pGp3Kkq+T3_V_8n!=A4UFwkq+T3_V_8n!=Mk3;H?2SO?hC^r4B?h2_bq!lIG+qQ(%G34Wr8$;@O@S>a02di&k z7jd4|Qpqy*?7kmT7nBQw0~-m@w6^B;ZIheiGps5>TnZf%cai=uYBxG{Lvk0jOs=WX zcGB20a_dz$K7D6M=8`6YBo1A1+Zmi=`-PHLa#*`3gP?oP?JB>1ih{ zTr_#{3H2hgGB<9%#HDiM&XlH7fC_Mvs-V_M2+s` z&wS=HP0J`6Rd5{g;AyRG%O}sP*J;Od;?CA-SNd^_j9elo&d600S2GoR>y3|#FJ!-7 z($)0j^(3WwSF^`eL;W`!nS$%X>eu3fsl(|a^Ad6LO*|VnL*m{c&a3NMiId;gd5&K^ zw{k~Foa&{qN*UN%)yY_=lStE&v zN@99i>)O`klWRUM$s~#E&}FUTwBHi{K}TppiJH(fMom~WT1}{ps|jDO930KWNWP0q_f)z!lmd%aEXN_s8xM6&AP-|-W_ z7E!Y+tBS*_c-g4NSPdmf-=16-sDq|)IuhuK6zHV4M`H&Emq%plD^wJyqUo`ks;YqB z$r!8id@iCJD9o9w2z(m228a}l5!ce_1eXZC&luT*?v_F9RqN&!8O@f4V^0LZG&L)z zDv79)=>;kAMttc(?Vx)(JpgoDU(%7DU~)v|%l_KQ{y}78vx5sG`tD)X*pP}mVCp7P zAMmpu_&J(D6)+31ex}6FNkSVx8)X2VZ%Q*M1Mz2K#|G6(?;7Q&y%@EFmT~g^q&?)z zbvQZgBYg4gMdRBRc+17N?-BR`@HAk3>k!|5EVS`0DHB4{_?D81;p*7Jk$igtwS(U0 zHMwp@^?$@+@MzJ9UH&Qp7L$IAa+hLehuW=TH)EchO%;gF)kOr7Szna zFRDvb9S~Dv*T+=Z%27IA{GEx(p}fz9SYgvx0QU2KrRm&1mK5NmaDvwzw)luJ`AR^S#Vln4TmBOngpU0dqjxSe4$X)SrP8Ek_^o=unKZ zo*vYMiXy70HYR;F6=K~Ixm-pN@bTbr1!`Oy&@@VoLuTCc(fyjY4BqGhHM+J;jb2zf zSlnOOJ1P}RP7+%?N>y}GiR!(RZ%Kcj@qCW>$Pn()p)mz&3^HSCi`AHgW&I_o;^d@g zprUUSsm8mM`Yv#;)yt*HYo1J~G^$E3FB}}zA5BFXm6{ZfsQ8Z2D$*2JbwA-ZUx2M* zOYGN{Yr2QHZ7%Mq0wwf&K(9?RpB5WKI^@N-8!Dq6)RwBUg++q}{geS?&g4Z!D%w`8 z`hTp{)caY#%(dz7DADw3#LbjAYzaS0lfOJYT~7;x8h?Dc8Qq2GR)3GcT|l5m-(+NU z@Bq;&R1#Gs$Ky?JBpuLTCrv0&6W)lZeHIm~vZfNsp-A=LOTYXPzf)xW@M+T~3RTfU z@)M=pXah3Aya~MLiQ_qD*dB_JdZAksS?uH^FlJ#DD%QDY*atN>9;_n z=xUa|-P02|-cfK?5OhRlsUw1_NzxX_Vso?_bEj#GVw0w%MpqT6s-{v^Mc%3?v#Qo{ zDY2V43A+bzIt&QP;*9#iU#U9Vry4CzO{$Ej%BBL!JEr&@HT4N#HY&PI7md>NdgA6t z9O(()r+ppU4*DmJj;PUXQTkY+;katfyCM>~vP#VSrei!Zc( z;djl~mHIqzo%5@yP}5fucf0jV_&%MZBV%cvols?rvY}iS>2gU;DrKyGX_T(h8h(7K z0VtSf@|;8KeiKu@To=9txc2PRRTHVR+HtD$!{Nc`v8) z92CSxPb^UrX?GKw%GAU~rPzdS-FL9Ee|%bOZ4q0ya_RwsqBuoUS2Q}T znu1$YGPuIdTZPP9%w6;|nYYaNPkSEEZ-@c+p(^dvr$GDu{isYZuVL+s)mvlR(*WrGBxZtudtIfi2a1;+Zjjo8%j%mxaqg2Io(}ov` z?s=S+13_8$-HaI>G`G>Z%@@+o$NSQc_pL2Z`z{>cAChqa+vWIDKD{_)`fyAQ@=G|gX=uAJuh$Sw`EguO zcrxBtJ!C^&rql!=K0j+CH;w}KWQ>ghGv1d+RCycyXi-dcY*gw-V3D>%p?b9TpY=yM z`4s<0>$L;x2FBwt)6IA-ZQ?F;tAEUC2M`qLQ9gE}Y7MTYeM=coA30?}y6GOJF8^<( zernV8X`@q!4ti2IqmaY3Z?_`MarD$Nho$ORWzgha6hw7uvh58Kl$S59l=maz0&kXtCO_=|HvL zq65@^wUg9-p$W>&+GY;eh3)6iseXdM`#@Z;e^wr`h1q0UT26zU4hh}JHO+5_=raT9 zIjORNAXpbUBz9uKsYQne@nw|7{zYnk=Is5Mv-fA6wg1AZ!F~G8dZ?z6GB}FU$v~{6 z8<)2nkz|CMmXcxai3nXLbZ~(>n6_WaSYEo2^?EASsN2Cy#VUO(zk_xg<$$`BhfnLd zo_dZme#NKTxxS6{)G12c1Z=cAK5f=*g{<3HzoAz(-T2vuZ1oGAGC)vLgP+OBv1%Pn zQjMNcs;2DNPfcmtS50ZEQd4Rx)fDDeolE+pwEfD{(VxU=8W1UxeAZ2ubGoEST^Mw$ zYl2H7H->}JlZt68`_on`hMh-}VrLbnbwhU2+D=``N_9xEEHW<|To;)YnE7}g=Hv0q z#}#VAli9r`@n@^p`EO3!0Q#cjr{H`RwLj*$aWZA}(PMo(p_V!Q&RqSB=>LV&J6d0t zM?rmodNUk!sEdjNJzlrTbrU~ttk;Oed#@2oW2&@OUw@^g*G(D|Q)8Zrt0Ly_=pxpQ z38iKM)eBAC=FoYvJ^L&59AIQ=G}uwtQP|1l(HA^f-EWT{H5ly=H|l=BPl4L!sfZfG zd{|1Gi*nti#_?D(@>v^scWxtg**t|qM7rC&vVb-r>$RFQ0`(&8r}`)YX8>j=EEMSNy32xNeO}95;Ype|lZ;*?eviOCK%}gWzN}M8 z#p>nK=GHLvZ|2)#xsFRsniErV=nthYQ68*k<3}@x0XtBiq-4%w;j&jebztKay16>c z`<{)^=s=CWvvhD$RZLYq#dX?_0u>=&-N!Pw12v0CXMBMg{}Szkaj=+riLk~O-s8Yb z;Zu(JbdJ8_<^lQ&`wIICI|@6BbQHFH+0dx48nrB(((Peofm-=eiCWHFQo(!><+>p` zTd6MqX`L?lCtmFpPZ|=Bu0nTjJ{``p6RI4gDwpX|xL2=zlgvRABh2?P_H9P1o_&-m zt;E$cZJ86V^5R1U5mm6z%wT$a#HXI4$TF~A^kg0K25SJuO^FN7;x~D(nrbcxsc!;qugRzF{F|y`|E!2H zu;@v%$41qdeJ(n$=_jm9=_jMbZf%h&YK`e@&yr6vMvmh&0pMDrl&yhaGuy&i&g`4X zSX>J`d6dYkusmZ=c#Xn4Q+QWWprm(Pkvfh!{s_qvZ8%Pu$lO+?*Qs{Rxv*HL|3%bR zUd!nlqOb1{1c%bOtGE;%%QfKG(0ElcSk#|${f8*iDc64leh(Cs$hOL5j7dd!)6)MF zre>86sj^TIIWGtF)D1S50k7J&%VB8z4_J1-v&sQ#{EGBU}5-2F^;nw1` zK{>_mXY}bHiJp?pYG!b5bx}a+ zRK;jE>`RK=M%c+Nk-5Rt5oIR(l)7KkQg$sN^)a9u5#4S)TJNbgjT_oiU5_u53UHfw zl;5OP`)}f&sMYi7s+tmYKwFhMps7+Fz}okK*M`PqeSMxd%eL`@2-E;(4crr(6br73 z@l_J?5Mr!cB>u^GD&ywRx@D9~e1qFMz-&~-XP*vDFHqCbITW43>C9$~Uglaa_xeze zZI|0MuG^pc3hn=zkh;#=?f2-aag^zuX8)S9HtTQZ)%fC&nhX3?>)`laar8w`rfO!X z`q;`w^^w}!)ki`%4ZHVHcXps&qsvy$Bd`mo)_arnqxF2a-Jtrhx5<7+{n(ccGQ2Uz z_RuD$4RYF4Gc}~PGkI<&qLI_4K_Wth<{sFXW92s4if-bs?%;H1BgU9<<$}C^lra{c zB}?&8>9pSj>g&PQ$UTvgS!K)OUmtU6Mcw$jDi`g$y>P<#iM5H@2b>}Rn)ro9Zh9i7mL#p*=%_3EE0QL`C~i;j==4_)6#-S1$I{WAM2 z)b~bf+o!80u&!t-XMItsCM+Ck%X)0dAV2d>PKDXW+JZM?=LPJy%j`?v)c5)t_5IF8`*FW>U+#BSsPcZ14lA7F!YeL2+og;3xfS=n#%0EjaUA@+dHV~ zHI?-1(0O#2>mzKEy@t4|sx&xDBG zq=kH$lrM9M$owo=iJ__k0yXxv5OsH*+Bb`I4j*EU6FzSs&; zR%a4e4p1+2W3F|icA~NM>NCOD!ra%I#G0_Fg8d2h;a@9GiS9D?-Nmqd6V<3siOe7zS$@4T#@tm#JZGoJMPtb&MrDWV?m(;J7M z6>9vQxqdeIdZ&xtr^Dp$R%V$G%wGqhL;1VP>a7p%$?6TC-ZnqI?N;xB;9adZN$&BC zqkYlFo-})gQkJjyX}t+i^qjgsV6xm4T$a_7dqZP}?hT3FTk5{-?65XeM7zegH?SNrzRj$RwIL5l2hz_y;!pIaZEoCbra7-^`Bto2zfrr(WA?; z*IGPS*gtA$%`LWX7u!GJv=fMxC0ULgyoCX2X|SS$a6zr=3WC1`k@3uEdfn6(Q#IFz z)Iz|e%kyhuUavAf*6i!D0Cj(}_Xq=UhRk1f;)Wc)GkY~@n9f{wk z;z4Wdjz~QAiO9;L6Dc3Phg(LumZ~wW**)C#s8wIa=?kJ?wuR{?;0l8K<1&Yp4LgV3 zj;Nl)?ji7~=rLby7i{LMfl*})bZz`BSnOw!6JzTlt=z}6_qV0|UO_bZ7m=Tzv-XTE zx9jdw0>P!h=>25B?GX0crn295F#ByE9olabJGF@EeZXS~#0J^_)v{HF|6cy zVlR>7Ly9Op&RrtB`GF~)a>|D?^2%p3?CKjieMjWVdZm003f!7~_`SKGijM5f{Ze!a zqq49G9W_cF)kdGciE*iy`x`a14cUufol?4^P!(|hC%H7F{&QJK{Tek{ulV$b?Qhkw zkD~Xt<`F2lkS(qABUhR&Efp$eK58A+JL$ACbsFoMlV6Iexm@4vvqR>9QdPRh+@J0~ zEu_8+EXIVS@6)5lac}3v(SxindW+MAse)woxup9f?T0x16bOoBPpiA&(}n71!JTR} zdlcFAw0G~s>>Kx*D_Pr5-eCRys#0eHuZR!(vd8>VnR&jUtwhJKgZG~B@K5BjG^LEJ z+EL1yI$zf9DP;LM>0m3DW+ckqgD`?JrWqmKtjHEM4 z?FJTEyFMKn&+~-b`ybCeit&rW%)2aY`svS;O1MYCy$bryBz&GUegDDz`m1{PNsmi~ zD1N$J3E5G?y|H2?dt!qFO6~VD&%xNVecJA)nfog93e-H>@J#d9imm3c`H2n}+w@2vn%s5cWJyiAtYZ(h$xrZ7sppr79l*FR^dD~tsZW0=q~6+Yu(rds$!Pujl-!5T zK0hTo(jji%8d76#WebKmUXFEyFY{=#4`ud{M2B3cAHn|(FvaTh5dI5rtfl`-IC@)_ zFLVsr@e5@PV_x64l_ykFW0TJRW02YRyQ~SYBiQblHJUPFoLs~`=Gv&L7(*EqhE(A) z%0c!iTg_@eG%m{XVfU(Wp=~NgzC&^ki!v=;7?a1y_2IB4p3qa{(03Lpi5y&LroE(T)(6f&r2sxoP38-4qvYKbRCs*a{knOYVY(U zrh)k3e35S-%HiAZ$n!(^@(l<%e7TxfPRVJ$^JCjEen>m%C%#}c+iN5Eqm9UkclYIR z=~WX>4U?XHb0A-?E;_PbjijA_h=tUL_$1H$%HjNx`s)6GbhZtXj-)(Nn{amTxQcZE za?c|y@D6s`)(2{E1K&{m9qkIJ`2%AKAn!JG1N|1_W8hU7NP>Hi5m0{wTZl7$%l#!8 zyJZZOXG{{ifmaw?34a)-ob={L7@*phWRZq4SG83zcOk>2drMj58$0YkzaUq#8(nd z1B;;(z-Ht&LeE4l1r#9D2)&hX58(_@@EdIW8X073UL+pK43HkU;dj{R1NzYecSEmy z0Uv-q;tHN){|e}PmbAg|fER;j0u8`QAVb_%WGm5m=Na;XofNQ(a0l@#fixgnMzX0j z2*|fvd!ORn1>Q$#d^)V+;BKLR0nHd1|0QAK8=fr+rt;3aymv0&{F3ke$v5}pI~4MbCixzcd@D`9lO^A#lJ7>zx8me`cltYf@Z{UD z^4%VJ|3;xF-y)Ikg2*>^<-08M4N>`yrhLm)zUL|5G?niv%C{}$`;zjFNBK^Vd}~$a z82Ofp%*8Tq%bcmdxdT@4Wp0pfnCUqZ*^ZA>_S0!oKxzi{!!rtLAbB|9BM1YjBMHwW z45W^tZOtMKq>d&$8(koE4B=ym2YQy!{*%m8Kzu3v!Gc;w9MBC60{zSB3r*l=`sxb$ z(MsYkq_2I7J`Xgu(nr?92NG?_05$FC1IcyJ$Km_Mq_G|upyLwsf%Iq4|2%1RDE-`A z_vMTMb@&GK1F9ZhfjS@wbO2j`G|&&I<3lO|)B#DL1K0wjfqp=p03WCWx;Nkpco3Wh z_j(|7zojMYXcr!i7X86IJvE2)0C^Uz=L?hp;X0rZNCG``u?-BKh|EdU70}Uu&p;25 z26}K(`J^6Yd3)g#TG>r19I>2hu>|7BJ(?RzTjZlV=_iODV6d_yP3X3LlVtqDJ1= zO9H!)>uJJQpcm)|1_9Mf{(%Hg1JnVHKoUp+9Y8m*1?U0NKrhe_3<7Eey0=qaK%8(R zkR&Y6RyTeRKLD8{1#0e~>f!f*;}>A72bnwJ0o@-0J@DmOf?lw^htjhWe^&t);Ts_L zpVL6&r}6s-q;H|-Ze)q8X~kc{bw8wDfja0U(7g@28}R80@^U5lx)=E$;old~0lI<4 zjmQezhp%9{M=kdZ<6X#Hjn0p;$9pMh38(SzKZx%p{3Z0i4E_qfaX+{p=)IQm`YQNq z*!?=?{UCM6J?O^k&|x1q39TNY?tnp{@nQS{;yZ~0a^*YeXP{2qj1Ay7UklXB zUk}tN-{4y4I^wSn)TO{}z+ZujZs7jZH`zJ|CU4<>+l_&m1GEFXf!A*e)W^QXdtJco zz@(emTl_ZPAq4t?CvV{%)pxjWxRrOcxU(@GSPDFITcGCM9;hDz)!$?P47eXi+!3hx zz>k4_{wq+&0-puu^#tm>z;i(Ne{-AQ`+=HqSD^j|eEV*;dw&?H(tGh8co=B=5wHE+ zM}B@x+V=od77`5vZ$x@I%~&1>Oce z{V@3jns)~3dEl&Ff%*lo;1S9O82=bs>%eD!8mN1LF^>o8#GmsRNH4ebo}`?CPyQlM zi=HCSfcj;i<^pSg7lFl3^9U+X(?{6@zXawziyuHA@UiER0X_%Z06Yr(87Ti1Hv5jIP~|FJ+K3~@DIEy3Ecii@(;WR?3bZTfHQ$h zfNOwL2WYFn_knkSi7yB01mIlYv%s~$4}qtFSAo*qv=N{a*b4jv7z8eSB~Z5jZvl0$ za*GOh3YhpO?i~Pk0`CDQ{+TueJO@l3q>O<-0cZS$$3%hsUklXbz#oD6uhVvb@o(S@ z&=1UglR5&*-y*NTv%s-$2kKiu!8`OB;7K6%F8u*G6W9c#fs(({27qq@+kmHlL7?z& z_yWuYE(ERst^@7@eh$0>jCzmy0geC`0L?%La5L~D;5WeQK+)fM{}h-5oDXaUGQg4V zQ@21D@F4ImaM&K~0N((f07j`W-#Z9%UpcI91)c}Sg~I9-;4;wANC7thj{|=N4yp*NGl72xZULSL{y+ANq_(b0trWm zeA^JPoY;v?4ovQFtz~Iti`bHoOJkQ)S&pb0b;d?o3HYfzz1o|zA z9ggrI2k1&r59kX}{`edg06hWPcR~(34|Fr=_n-+!u?8I*f`4!aoi zCFq6|a@a|ea@dogB`4;vzksef31v4KWeA!-1!V$SeKN}U6yzgl)~P6a(8|+L7p5XF zKxdzhIs)=e%V8gb95$o{G(A6u-48mc0ObqHEkvGx{sry{pwB?{XXLQgLF3Ov`arjX-UBsG&tZQBtvV}*eFM7sY~*7F-h+;{ z=ddlHX_d$yP{j<$20C|U4to=%ROPTgf_&9E?0-N%oQ3k4jWz;$19Vyq>JI4S+8lNn z=uyyDpmWa2Vef-Zor5+8I(jbJALx*IkQ4MM=z#giYtY@Ge}U9M8LgX3f*hOeJpnroriy=-NLDjAt_6Ja_iZPF{_^2J}17 zUqIu32pK?kfL;Ub+kt!p^@94Zz}>KG@E&vr=uOb^KSG@Zc|qNvH$ca)&0#+Q-F79) z{3^%;`tj8{>^ab}>yUS#mqA~Gj=cu`H7Epn1XOix4!aa|7iipdxWoB+$O-xvsOrb) zpFp>Rz5$(a1L`Jd+Knhz(7!=NohTE~_ioBzwV;PU$K0I5=7QQmzXi?t3F;r{UC^<& zp#FlM1s!)QWCLvj?ROj6ALtcO#qFq@ptIMb4S`_f#Lfn-2E79M8>skB#1Hxjs2lVc z=vSckL0^Lo`YGf9O$E&WDIgzcHRvaxyFpKYUIx7b`V{nE(04Z=KS2ec>7WIm7SIns zouDqzHqhgsH$fkRz5muwz*sJB}UCPGFPRiR>gcnN49Qvs2iq>@<80?sPT{dw2P)fEBVL zR?JFRDem(rXJ@c8*>sG^XR``sXO(OQo5`wJHJim|vl>>*&S7)dTsDu*XXoNP_IWt- zzK|_qi`f#klr3Y+VLx>NyO3RkyHXTZ&m63QIhpn)x<=N-JnRy-0$-qOVP1TV&d)A| z-Chu1r)y;^*(!X!t{wJ%-)BEym$TLEhpdBL!Pc-Jv9(ZET*a)18yT6P_~p8c5J zz;1;4;3n8F{)FAaZe_Qz+u3?{2fLH~lx<)?!}9!Y*2OlmZnlYSX7{jru{iF*45OFz zv3|Cd4X|yvH|l=aC_cy@Vh^)N*w5K^_9%OdJ^b&4 z+rfU#e#2g1FS3`|%j~yon7zV&$6jTxvDeuf>`k_l{T}DL-o`yUe_-!oCbNsZ&pu!u zvX9uu>=SH8>}G#rpR&)`=j_kyFYK>u5BnSYg8iNSgZ-0z$^M7!WnZy>v469#*?-u7 z**7SD3EP%AxZYu`w2!o}G!7em`%4E%2TBJ?2TO-Yxzcx}L#6LZhe_X)4wuGD6Qm=g zBPE$HtBY0y>y3kr}R^4gY+}$F6nNm3kT`CrA^Xi z=^p7`X^Ye&-6!=*eNw-)RT_}CNrTe;(gV_i(nHe2(j(H(rR~zA(qq!&(i74zq$j0c zN<-39($ms2(zDX9r01mPr5)0*rQb*|NH0n+NiR#km4>BPq~A%eO0P+;OK(VTN;{?B zOK(YUOYca3klvNvlXgk(OCLxdN*_rdOP@%8ly*yhl0KC_lRlUJEd53LtF%Y@oAibB zcj+I}Kcz3F|B?1eUrGOx{w;ki{YUz*^bH!nB+GJ+JccZ#q+Q9enYEKh-D z(kb$(@@eu^`E+@jY=b3Nfm|pT$;EPsTq>8zPn7s`v|#qtt)sk}^HE}t)7AYUk7Bws8ma=q-3 z8)T>Kl2zF)H_A;g)x1PrAven{xMamA`{hgJfE<)Va;v;jUM085?eb;v_hH#}xx8Bb zA6Spb9p;V8XuD%m!FV-AwMbqQXZ0@lAo5Jk)M@+B|j%WFYl0lE&oP-L4HwwNq$-W ztvoEhBL7Z)Renu=U4BD;Q{E~6UVck{TYg9WgZ!@SZx1#3yzZ9PscM_w7YLp1YHnr~ z6_YATsuovFayB^vN+{s)go5@edobj(+m}>K5?|f)1@J82u038}Sy^3)cMXo9$Eo;& zp@8aWvB!zSZnKl`e1aL_KnUaUHpUAxf^UHxk=W1IM2#(}CBf>dXH<#k%fMP$$c0m! zK5sCjctUEx5kmS6Z&o^*?T|xR=xOmct4gTduZHtXlf?M{@=9gCmiAz4gVN#%Ih%}; zWq^&%9+iZ;_JtuwNVSK{+a4}BW&Wf|i-a&JZ`GyvLr7Yz5PFs?mD-0_Rr@5{j7s}r zrAl=>TAM?Ps2WJWR%v*Z(F>xkx^kH^N6Xc43`o4U6~Wq-m8uhYiYPpdUe%Req`@Ep zqm){`A*3VV@HVQ*hswpu?0{bh*-<`=8beBuvI0MO4rwA*Ow!ncZ4$3h6)~xAd!fi@ zRQSqeNkqxFN3tiilo%>v2I`KvEXe{-GkKc@wq^?{v#d>&HW+FhYN~zXh!V6G4?;(} zd}~qJ(Qk9v^yZLubcWoU+2U86O?qRWQAN#~x?=v^*W66~9{2>*Mzu}vn8aun-6vB( zBAd--Hhj28OejP!eg5%-nquv7I)dq^K5^)n0wAL^H&~`pl6uWrURgmCk&Fmov(qHS zMq=b!su7tIh_{`nT!~*lzO@XAFT%H5LlW24Z!0?zOOgE?S(0DCC!%J0zbxj7#V$0X z4JIl};@8%1CpY5r|90y};(Ge6D(<%a>P@7b1+}+Nx%R zXPbR#QLx<`an!I0WQlXSx`KIag#swkeyQp|~rDk5d6HlId8MivZs zi1_j?se1CUEFUeg4az630aEl#(w0d+k1L!{vBHf^TNVMR$LBbgn=voqc+sZA0FtQLnmWkWlRdM@Ik zXQVt!C`8hlV_=fVugxyF@-90)rkO1}UU8C3pIN3UW`nU;9xbo&ts!DtvNA=IV)fpJ zycq2x96xp4Ns2EjbY}B!WF};hRiYHHZ1DM-X_=xdX^3nBl45Xaz~R?6zvy*j@+%s% zmdD!v#z9EaRHhf1&`s*Se>Jup9atdvysF{|DGd(i3Yv0L^TU!vY;Hj|4adk-oHhf|uZO*QU1A5+a~XC&Crz3;qpCX6 zHfiX<{GeL*PRqBD;?lNV5ii$@MCu(96Thts-&92uk2grGOXH5Vg&Gnr*6?yM^DK0u z0lsOj1k@n*or5Xn0F*n_p;1N@EG^PvayL5~lW2lwX}KOm`TYMJPb~-1%<2j1%x@K%I^Rc5rh^%SEsB|8c9b)ryewH_+*4mk3M7jl|rNYPpBUFloFHEf|) znDW@`%3_$pkd({LM;9I$&L`O_NeO5VHKAgD;=_VHJlE&e zK()dfCU`4av89czYo0@fRfN!NBY$Ytu~5CVRrNYmZ48Whn~`l$p0!~qva!9I zBsEBYi@flu3+DAZ#FnwNUXh zF{4r-vN-i1qijTuay5FmOc#V_xJqNdw@PVfw=bFAj1kf0P>PEiZ0@r1hT=kpt+1$| zq|Bxk*$T_c95xsJFDpIUj>ViZhbkRg+=bd;-b>hB1c1^%T=J-3bf?V6d`Kv8h3n-DmHC@f_Egb(_)spHh3=J zRz?`jX*HHltHEr{v$?pGeTaWVcTx~zRmb<%A~iL#q|53&U7uo$ZAYzaxU})Ra(#9m zRgiJ!by3C0(?nalTAyiRHm{XbP;~^HP1;l>Dnk5-zl4gJ?~y*7#v=Mf`dq|gsT+^Z zk)+vYCIj@TRdfzO(9F#i;%8+>*@Sz>m<19U52!#g#h4p}jV{(qMbDe|x1D{MKWft? zlZ`aBTBfN!xFX6_{Nc`6TL_pk*9b4-7N|j59AOTwD}ZV-FbD-$ZB)iaY3Hc(hj}`( zL7#_@j%l&7#L?WULTlm)L1Eu0s$V$zM5^%7<(kxYv|TcnZIlEF(ml#A7a@`varkGn zB5OGmmFwRg+briFQWa~2h)Qf6KVd~#q}GipEejR;;o|rx`^-u+oqP5mzTI4Mefq#!cu2JL#d;<#8%)`-T4k%fjb{lVm-Oq zNh5t}U?!k8la0eUzoe|d>1=S8mXtZ2C@6Fly9-LN-YK-Hg(W4WuA;I6o2@Vb=i~ts zaxQch6_zy=mKQV>m8to*5_f5dBi~(SbGjYnj?#v*xSY)dOvt&Yq`aiu<#Lyn=d0x| zXSteR)KFfcI?IZS3k%E2n* z#N})zU}Db2n+$H%%v$&zGq`1uCid)VO69E%)b~U(N4anzm zS7B*+fivGxo{tWsq&UC9f!`(ZX9i{hCghAd?Jjd7o81j%g=KCVs&#&TLt$B2gTqx& zP#nMaG!rl}=Mr0qtF)n{ywIT*x@~GfNePCKGGszwQAtT*{8r^Q5il`ljL#+R@&dKA zu(YV$R^H%r7NZ--h*ePLDlLiMs@x_5YMd8)X`$=kyNxbIH$TQ?H9hUz=*hFSsKN^a zZZ({CTx9XXFC*aEI({huX?G$M^0Z^t*4z5t5q7SdJzi|qjj);*`#w=DjT&0guIpJY zcD>`p0X5E~Ng_4IV5zP?Y}ircRp8r^nM4iedwO+C&*WyYciQX-2EC3J?a&ZZ9*U;x zjK0ePi@?fRTu-a-sqP*>%_JHvA2e3N0dY4&4*;g`B+6E{S(h&#Rfx|-T7#m#5GIenC<9NjPYIzsJ1y(egj23_HJZD zvu15=%#G13l6kaLa#~#q;7&aJE0s$08Lk|JF;;LRLnGF?1PG@uX}f95<>2Wf4^=B7_(@(LF@^29wbtw7$f(KCO-~S4P>0r0L3{s5&%a zSu(obw|YG|>7%KcaSkq}V@Xh?iDRNTF50YWcVVU$MR=nnN@}A}(GxktCBree$MqYx z^o^X9S)>HO`DR>esr7H8(>Kab@&w>|Ar}r#2kA6y{CG!siXE{qHjS?GQHq49j1=CX zz=~Yr5LGLq-o(u%5l!@^EVng2$VGQl8NF3exr%eLGcH7A7|GRY@lIzp&*eP3xQ^SFF ztxMDsl5C@xm#v{xah7y+B+0JvA03JDcyNmv4ARNuk-p-uzYQfBy=FGNh;_Qz_&Rz? zZFm!BiEQ){L-An9BB3oFnH;|Z>VuI6z=mLSrW@N6=@!h0;HehL=mohU!N!xy5s8b? z%f(;1(H<3aEk;Go$N*XBxk$R$oW-b@;9y6bur)|#dN;3#8Zd({nnqSA=Wu1kf)p&Q z$?yQG?^v^L=()l%nXs5P8B;-7Qfpw7F`2M*H5ro$D^9a9CAZ2n8IuVsOOr8~uw676 zlL=cyvoR&NEi@UE2^+G+F)5{JezUL)#q|~bX6(M`w%l!6HJo!BG#FhI6h#ViHVZ}8DNt9P(TKlB)9*!&cuYNzjY=i#)Awn zNj_9qXJTR$vChPVS$+nXB)7n~&cwt3mjNcp$G!|ONp3=Korwu+dFxC}7{+ISNpc(b z3@}MCufy{0B zj0|OEzh`7z+P+5WTO_gLNz0TW64i*!5&cCh-Io3?mSQXNE?PTfH@q16***PLbO?jC zO!pA)p0=n7?r6PrI5@9+{2tI4AtIWjX#Y-!Kh%nEZNvzo;)Y7xG}P*k_Y&sqab9`c zv0lV}Iy#n!eO9zoqpd;0X)(SUaTT8KhpVCJiil{@;%ldj0VC!f`qL4%9QxxpS||N= z45JGD)o9I)cvBTtC7HGg!c}#&914fD(ymFJGBQ33dzOai7T7AV12PlWk&3vEE;75ltvEYlr3nw*`9y}k-%fyl_1}_tuni!!>ESh43GO-GZ6)O3%CRV8A z>y#LwOe{QNgfg*`h!raN(jZ1C6Lb9-p-fEPV}(jS8;=pn#Pm8=sO0nISfP?nfMbL* zF{6zU%ES~kR;c82&RC(6Pa+MWbk!CgWD`s-V+6^HHq1O96a8aG(qp1W%?KY8eQ<{O zSka7`FCP>Ad`8k^Vr0k+pX6gnX80r@eKNww#CVkvJ|;%E%iBU8od`yhH znc7gpY|iN@n;ZpVefBPx5(?X+E{SV1vCz zTYX25fTlHVF@hPCZP9N{>)WD&rJ!<)5zd^}EmlAul#H>DK*z{1O-PR^+mKky5-Zc< zGEA4B741U|@iplaC>G-;KvP>J2pMIa7cpv#EkwEptfFEYp_nwhi=~@1ypE-wjCvid zZ?s3h8>yr;yp0ZL&{G=xj5fH9s6!H}F^zHXR&TVx9rZGz%#86$w+9~eDoUj}>g@=v zX3Y0tm1L~9x)yWPySOi6Mi=RxBC1(qlp~a&##csV=cu|Kb!KjaD%23k2yN)dH!+XE zjqnuFi5gzSGS@S{jyBgbywT2j8*T2QUdB8ZXYhf}&|o8Oco9S6YIr+Z=}IrF)P?x6 z3cm(v*1X01xVb&1A?q?2z2HWDSgOHfn}7AA+8a^>wfZee{M#<`=!9GB;H0LlZu5jv z3NS06s!7B$2!wCk%)nR7R0lpOIr9AFVwk1TmCN`n73Akz0HyPz7 z^UFVC0XY`(Gpxyc^TQ;_MqJ)ZAKV(9M4`PlTcR*@go>Iz)+Ii|6AtLoGiD*~n~u$i zU)fB-$wF{p!kmX#dd|<_IS=nTgV>QT6QYT@6XblXxZ~x#(J#+=$Pqv1jlQYw3t3wI z)op$>nHJc-#u1*bW7j^vjv63r&G}Ui3q#mOidk;T{P|p>MQWhMUmKkgr%eXjg`$6i;;Dv#P<@>UE_diB_AMF|$A~ z=)p(fA%y9p-6VrDUyCB-YpD)mew=y?ypvUCsEvx+=1;NX!nh^AaAi2JgM*RO6{Z&> z*&2&9Ap(zA9OM(`3Mmy4)fH-OQKE^0*WwoIiHt?A4Ix_Gntg?JfqC(2QDPZlJwD2j z!f+9VQ1cXi!k3~>?Mehg^_!u_jwqw&N0Qu+U=U}fquJR?N2V2ABI#Fxv=!%ZdP3q8 zip%*V&{!{UNlv7uP@kgRXQRw)a(EkYn3NVx$RKe-1*y{XG@8~So_Yt~B8TuIDu<}Y z)}jiZ0_43B>U-MJ72uO!`MlX8$pM;tq&>XxK^D1*e^7fFzicRK9dU3tQSoN#gGfp} zp4MMQN0MgOr_2}KK{&RwBjU}JC#Vu#amw9zMdi{Wr7jS}#iAjUY9uyIAU;Z_luL0V zTtGFNPh3)pPvp9<#gCgRQc7O98;+w;%i8vRHs!Li-JdOXeKelgpaWG<3Lxh$EN zWKS+@<|x^d%bNL07Ui;J?vh2hESbloCs&nzuMO_U_64xR>vwRag(Y>v%u4Jo(F!P9 z7lBoo*acHevQ7DNy%?PN7rMb(S*m@n5xkwa^S!ys)948WQ(xsO^OqKB`_laWZSD3o zevzER3+;RXjFLzdS;cQlQ$kH1zVOZ7Wd+r=BPDbSDKNFVm`snU#g$@OOsy^h(`0IG zjh7Zvs|&cam|9((rN`9b(knft7S~m2F}1pAN{gx06;XOjEiQM`Vrq3QlNM8}3zhVk zT3m&s#nkE&BR!@T*AMA2wYWG)i>cL_e_BkfPV3WSYH`k<9#e}G^0-VR=9)=vZ{U+V zzB5yWFL?)ieD5Ni(4OEW`mN2_m`GzArV6$}{C?ZW&UI?B=u=c578#l?O_t}x{9A_c zuE|r79y4-eV49p6Ijql|Ga^R@Ces;`!}9z(BXVS5%AFB8GB69zj2zY{=9!Vh`dmFD za%5ooo)I}RFr&|m9M&iK8IdCc^Zty;k%6T^X5_HGI>?9|8CWo6Mh@$1h|I`geOZwa zIWn-)$cP*nSbStg4(scY%*bJViDE(yeydly6Mt>_g6XzydY6vLCs|ZQ8P#e^_875} z;gxpe1Ye!P$Kfn}!P1odXI$A_e2XC^%Qdp9tf{FxoaPAxIv^RNd#4!jfebpSLWPr>lsw8&_P>a7RRCYRMtqt* zy{Y?9Ya^##ssjNZKe3Wpdo|Oc)2T-%KOdHA+);hG8CEzDOF5cN4IEiP&(KV`O>ncS zXG@WrMQvJY+^p)%Qsrh%GnN`RtNO3hxLMV5rO3^qE-OWD7ByC>akHwIN{yRU?Nf@} zEb5q2<7QQplo~gy`l1xMS=0)p#?7kkCq-@+H9RSDv#7^Ojhj_%O={e%>SR*nW>NE! zA~%crm1u5CLyLnikj+nJ>9}((U+rNQC9bs&*yDWFkGPTT<%}h?u2(!>w~xNX3v)yY zOlwZ#B8QII$53$aQ(5#*yA3E>fRS;0y9*^_R^WCCD_Pm%2&_nTQNgn}dY!^crbcbk ztVt=t)aLHmBCpnkt4?rM*xFIhS}%QnmRq#WpznHXZ)WJ@lqN}R08Wl`Oe zExD{JhO#A>RTWa!KV{f#)+?)?$*GD_7%OZax2kY@8*xDfOQ;cYBjakhrqIOSY zoLDoec8D)tM%joI!q$t|Cqp7}=XA5r+n8Ewi?r5-D?Dka$tgT#Kat^LN3PZo_Glv` z&S-YTP~;cbm*g9>2P!kv=1kZ|B4()c;w2MzW_zGa&9A5cyV{l(UjUs9Dznm}wmALm z3T|I$RP9R=f7*c;>hen3%ZTSOA9ohoi%4xelMFrSDp|xGVMkYRCS#LtuSB1-(5%SD zk0p%!9D-5e*#1lk96KfILb_9m#ocT# zD8zOV^3>326Td}{KqEGgcn^x~&Zbsy1}}2M_7(3>QxL$a`p>!m7WI6V1+c2n%YXpZ zGX zfJL2_WdW=zudEATQA=fA0E_A<%K}){J6RUMs)EV702Vb#)&;PrGKwQWQ^2$(A32MDH~Pb2c+c(lo%&}17V$`o9u;@M*D}K9L99U#HnugyB9Dt- zA(D<3ON7<58T{HidyGY=woKvwN0b>NsIU-jZ!K^Sp&rjvRIiN@GD#~ zwx}_Le4=kN;CoVG!?vg>llrz}Wt1>gabn2SlG~84W(3ay+c-G@n?#|FiX5s*>mwD> z1(_h)ly8MJ#EcIBrTZa*#a?_*Fm3ZCvTXC?D@HC0H}MFK>#}B*&O@28w_7N>APRye z5WrVPwOXwAKlGw8)D(#>LF0rKAn(scg-Osl<4e=VHYz$C>g)K&$t=_n z(x{{7CLOl+VN^1k%y7iY9Bvtn^RS*qN@%9Vb<(F&>)OxN`ngdboFvb%bwp9J%c#hLCM+P8GT0OEv-gVpd)Hr3MFW{X}5|J~@ zH%Z#T%tHH&E`qe+*y=KCeB|7qjUL$p*W1J&3q6T(9~gV-&y9z8ZZ2 z@j1C9G2vPid^O7vphi97oAC1>Lk-`gwDA)hE}zom2+{;FhR1WbM_%!LN{HspxFnvw zG#0}z-(5_veCRt^*fk|S{F9*$OiDxcczLOqn07Qs*5;Xf75dy;}eLLJR_VCVYX_33dQ$Rbs ztwFopaxU;M7Ji!yscN?HvoCb>1R6GKJnx!goq<8C&^1OaYA{Iq?D2Nbc^sl2Nw;2z zmKSD~VCOnBoajry$x}qV3C&70-${gu@+|7382Wf`oqpbs2tLe%!!s+78;d2?(Gu_L zorWRGNYV(modE~FqM@$=(7_m| zTl&f@5~aVv;auT%c<3;eHddr4)945?AXICxDcV*`i!;Sk^4^9oCvas^GmPQFX-qph zvHVfyD065D#Cv>Lf@y|fQDbe&Ix#LSB3?@UOE^xmW8X4_Gu1AcB8Qg#&YExt@e$K*-AN^Vn(uLE*WGX#akhz!x(i|(qZ5hqV}0`I_Qj|9taYm zlS?_Qf@vO=x&X|XtE?oMx5S?*4maPv()bg+r2P>OSMtd zP|(&i`)nT8>q@)Nj~zbUT;zQ|MY*a8Cubv*)9|!IA9ve=FfEIWk*3E31&b5C4z>mT zt?9-wL#xO9h%h?Vy7o|$&s*!ooZgQQBdY;M)5n>l)@WWBQvLjAmAX(3Xj9c$^A_{1 zCGEK{?4_DwAgem=4Hl6;?>tQ1xVP#yPbki-Spijzijp4adKDfRH#yAk`ADxc;-@yn z)gM-Q0#L1kE9|==>qu%Sm0d(cL_ZHe(n7w0tW={ z5m@wq_I{O>0M&Q8@Y0no3yhY$nfyX?oy>AeBufR_PmOP@pUoG%C zfnN(e2d4@Tm5FgTS{09`l&?{91v}3f$*$?Rh}pfWS8d z{#Rh$6PiD-zyX0@3!MH7?fn{o&k7uaeu(})Kp#baLjvy=c)dWsz(G-;xM%#gR)qhJ zz}E!6D{!~KF9hz7{*3Ks|E)eBw7kHY$!v*dm@Go05|8EF< zTHv6-4FYcvxLTlJ;DrL`3M>(LyugD6GJ#**tMS_e?53J*2fX#DHl4`0CFqgVkR0;1Yq}ih8$r z(X28(wwu7AIuKCKbvj!E0oBFW8u4T%ZJ@HN#IIQ@&djlTiIl?LMx`=H!1g2gFm>jF z@Fx@DV^*`z5rUU0Uu#1%W?r{}`=VCb-o~*_wuIvF;1+rM!Oquz@dPi>f6)v&q_!}& zOn-v8p7vz1{zSVdi!IlG(`>NW<5XEIq^j~b1&5W9Uu_|F4}4?Q&8*e)1P6Y zj*SxiF{^;wwJm-e5W_h!0xhv@H=c_+JrfE=_F43A++5$}bFtG=QVX%`qhf8Q%xqGf zD;S$@d`1Pwe#qlYDLk9o5$?}+yhi#;dtI{+zCJ0;2Jh14C3Q8AjE zq5iYuP#xPLeB%Uf9M3C5y5BWO&r+(ME=PMXhIdFen4-YUV@Uy4uL#WmF*}&Y&dGA zFjKp~j54UjZ-?ax*K)Ggwftyo_BCkwZTL?Z3=9q#TM_a>`_9i0JIPeWn2su57jhH& zp|p~YLoeS)M5*wb$f9CgSgFi`Pk0=8q?#TT@$I#nsNQK`tLA&B;cA!gzVLjyK9YR_ z&b3|`dARMPb5y^gjok%b__%g_Ul4JLGK-FhL*0(O)cGfip>UN1{N%_iQF&1aY*)}?gup(ouZ!2BYYh*wgO*3bhI`$g$#S8qU~|Z%7@1) z=&xMLB|eWAGe+^tgI?~+vC6zqn*ydQ*fpBFm0N?d#bcG3xTSs-c0t$z%}u66P?gNr zeqn#TA>ebk$n;ymKojCUA|yZn)`~LG47+Fg{UiDdQXU8K9@|&iUUv&_?-b%vjiC2e z;{8&Ochq~XL1#acD;&+M&?<5H0Pb2`X{SLJr}DMT7yXC0fAC+(LsG&CFEJ19lVTM6 z=jbaUNBT*Ud^{thx?0HHmArNrj;TODm)S@sQz-_UJnr=3trWs?0q>7$Pg>r@Ot_im z#R{Z~iWweXFjnsVDyO2+=X2TPyk?C#6>gf$#qdBXVRrV|id7yL%yeT!a1K1274JQ6 z_&bNI>8DYDsq3TBwM&QPih)J}PY zz+A6FrQKMx&+ty7xmv-m!KDp$_8dx=O#0G~;_NXM*wQsvADT=}E&3wrZ%B&Sh{Ku` zb;t{g31KUc7gRe_&kR_$P-o5_L&>Tky0;?50zm|n7u=wWEk{}`W(WJ2dPZy|rCd#E z{w0cKH;=<(*akCQwBa~D;Bh!s8Vv?a$AXcK`o>DCVV26}m%LokD_CAGDBLHl#zA}k z>>P~ubzWZ6a^Z95bgC-KL%5?T!y zHWFu1JW*STDaIo@*UOC~CdTY|TjAP#P9H1p#kz;o7~u{eX-ICwQKPLn`n*;ORLmF3o9tZailSypxjAV;VkCUkBs?bikF^G zMCzD`AQs zT5+m6>;$GAgoiPXFs-mNh~sPuFm{}H7dB3UakamlvExTQcR_6(@U=6EDKp#JxW=Yh zH@Byq8HFCt3cxtIhTG!@iWobS(RF7ne(0pRi()>DTrIH6WoMHMvwgABJ5fkMiZA{p7o3UWQYg)&)@Dm)E?7K&QSH3yaJr38lzc5O&R__;(uYqc zta5~cFrQ)L8D)&m$=C$uhH()?uZn(@Ke~?c2HI=JHnCOCAa^ygNZyq*1`x$G6x*S2-;f(9z@LdPmv6;jaO&ceEAI6jh z71leNs!eTU>Lks#V)w|LY@Oima4=alDflUbL{gjY{?=($kxYn@| zg$X|5jo$B39aP&?XDjx2q5>TQS+HAzNggQ>kq=HylZZM`l2B2x(uiA8nZxgy+7g_K z?_x|v$fhPiSqGY4*C^GGugzkL1`AO!yKV`+Bpa4fxYr_s4n1hB6+NT>>|DY(jT!~b83U)hc7S-$1hPk>tM)D#$-3(9XDfx zQJ?UV5sXs91z*R2ih) zB^NtUs`3P>Vr!n~dKvJKIs~~I3q1B>hFu9O+AjbC=XX7H66j0Q$W5wcq>3iNB!xy{UJc8t0{n$1h!!_dEA%-+a@$9(Bv` zM>1$>8>h#@f6{Z~-?GDHF0cOVbXX#X$0uzf#9zho7yl+2>`cdqcf}qP)fQ7j^VfIMhIb^tS}bvRTe+>cywK(-EOs~? zrH-QF0=2MEEh{K?6_=M67MyLjCk$Uumh_s-gyC&!X?b~x%TeSmEiNo|7BrMPic4$- zPSu_7uobxTlkqP~e#c`%{w2xp2TT~=#Pxm&!@HB;%9S{L@@vHshR;uaQES5RMai$Y zOc>sg{ARj@;Z5B0nlQYH+Yu9nH*wQ$%}>T07(>~U(e$w6#?M$-bm=trb6`hS?Z zU?ANQyzjR9Y)*!(9U z@fR~*?jLuV;46^&R3W}Ud<>MPj0wg`Jw4bXG$*IW9rhTXufbEobl?zWIJQLTrTmHb zjm38s4)BP>;T4s(wxR~r(V#kA=-p$-3$r7hlg?)3Ws*DY5C#8>#aE993ntwrC1$x9 zHc`?#UaQMM7+a2%_7jcKC5(@Bg%DWN2E^Oqh;ztJXOc)>Q@ThIQKEQ~s^R108^Rfk zIW$JG`6#u%U;~T>!_7obUc&s#K=vh!AM%^fkcUO(j@aoFJ1D6~mry=Z1CZ`C&SCNx zxtX|rn>$_~zZ`2}PRh5>MlN0uBv!t}{0Oti&RG66Gc6}{H<626g!kC-&sJgD1)X4m zfU)>Qt%F8LqLZhG=U*KDCZ=&&VpjQ^P=jWbzX>&NR{5JyLuZx02{n3F`J31V z$SQvm+Zf6D$7=Us%LA?{8PQQ>>1SRpV^@JDRLQIpxKp@y0i{D3`+T;{a)GwP82iCF zGOGvv9pqYw?X>T~Y68^1K<2mw?kC_T9D6up@4%f4bby{fSi*kLFx-SYKp(+941}2k z+dUI;0tVTW@fIP-Fkj%g{K;)lLIgYV1xCsNGMR0cjUpyW%&w~FG7&{qM zw?yW+9qxL#2_FTu!AQ0f_w61Fr@R;W^<`XCZ&#CVU6N zPxun33~oYq1UyK|Txu|1=wQ32E5_Jnk+;Bq(sQ^8ZA;JwkUxZ%gC@e=0X%T2=06W;2T}ZWz zK%VCU7lJ6g^}v4N9t8ePxY;o~4b+=Q=yI^f;~ zJm6wcj=(d8+YX!s>VzM{CeV7g310vW!cF)w=tZ~*zXrVnH&bwu9YlF(170B9^}ucr z$vg-gSFgEqfi4jF_X7`fi08o7!rcMwueudk{D;pv74Sd?=`;B`?I-^PnPx9R^Nm)t=jcYe6JiC-8324%7j{uR#~WKjDIv zC_A|8fyb?a{OGrBz#7mv^bdrGv>_khCd>ud;3g~uorZcy7y#MfCVT_52<~0Lj&{^N z#83Dkh{|XWaQbDMJcK_3?SyQETR@+~P53B?p${1X_OI6J_8_pOLyN}`{2D}QWLKcg zfpX!WaP}JfhMUj>vcXLl0#(6H*eRY9UiTx^MeyweegL9!-vj*TTCMG{Dk!I+X4LgX2^OTp8o{>-mRzyaOVOa z0Z~6N1pElZ%K{j@O@sx$1fu*L2EGHLzGWBi-@w``Pcc5iP1pi@4Q|3KKp(+Pc*h2`3%CjI2GR53yHNMpRbTptTGmsC!Pe2p# zd=IetIn)_D$_{vsa1R2E38Ju_z>kG{5Af2LG&wtfuY<_XF5pwY)xr(~#|>jFL7aqt0CmDmc=IbL z3%I+0wpX=y2(JWjUcfV6*Phn{zj+J&e+Ba7ZN?UZ$WJ}+Y0wbzXBasB9h5cPcHpfb z^3w%;;a%hj!VUwcy~o(~l^835SAsY%;K#zf2l&eSn*6(f#UE&X?7)%_F}@;BJMdNz zg&hJ;`UGR*1&9;49`rfQ$$@{P=Z%p0k7$FSez*sLIlHy-Iv4mjh@KAtKLag-n{f4? zP>10r{1~(r?mfVxK1CV9O}GuT3vR;kpD_%C=-+^cevW$805>rA&lvBV7)OAML7%&@ z_5rR0eGNC^c2KU0IuCpWbQ;`*7ybo#3OC_O&@#9Qe*kKOn{W+i9o&P!WB!J`CBDG9 zpc?S41O63s3&{^0{|~e!@XZ6(ff$|>F8n8CfSYhR=rp(q70`6J>*?A5zz^cI0qe+( z^b*e7i*^AwVH0RN+=Qz^i{K_)3ktzacrA$h3;{3x7uvW3@dJMfqVcE;Sn)OLDxTYc zmxHJe>;T^KAM_pQi@JdS{03zQd9WhF4Kuj^aw+l|_;b*7gdGAFkCj-_g@_0EV-UsP z349U6$7|qm`$&8ou>qSw^xO}89z_0!fiLVUv0+-j180ttu$DmDf&C!zKM34sKh2#7 z{1QaKuhk#QL(D>Sc2OlW0+>0SU@J-Nkq-7WI;DaRg4wWfzKB(Ra zzQEf-|G@Ju;3FU!vxb1r9U`$w^^h6(`FPwhia7TG-#YCRl&avcms&i z+X?(kxc30(oTfeZ1Ai*qUBK5sl+U|>%~O#l7>oVDuY{YOjxq$1A3N~ZAU;n3{tZOD z*ffd#9n_5UG8>+QLdZjwkMaeP9~&?LqIf!hClw$cF2Zx*v!F7#hk>p_&Fu&7S0u5` zc%BP<64U|r5O8_1_Pic=aEbOj7x<)b4*|WU+H*f}OqmvEF7Oc$@f`v>%O$o0Za?st zGc^Bsz(+yEcL?Y`Q^W&2X1eeLd|9}MftQ>mu>&a{;IU_;jNr}#J_Mq44FR98z;oou zFz_ZjWWt)IekSArQTwW|LVd1=Jm^n`fPbBZdvM)I`)r9_S|hPMxQBtqoFlOf@KXo8 zc@F9!++D!`%tcwk51WVjFdy{?ZX0m*xe}{`yA#+4T893IF!wyvXSfMZ0$mTc4cNF4 zckN=X=m-95vBW0gIWC7|`z@2SIXSQz^x|@v<7T+uft&C{&>px6_kzZpk8~|Z`W%oM z@#Fz72J!X?yuATooiaz}LRh#7dq4-0Y;K9wf<8i9t^-~p+?~LjM$vBo?*`F4knp-D ziFLt0;k}^ka1R2fctkw}p5B76=$CB3J3urq?E>!S)!ezj$)Ha3GlW-wHo#3d1R8{! zu;xyW2#`+=Wd1KyBf5Af(~Arst$S6zoX05{=g&~CT~ffrtnHjFWv z@D)%E+=K`I81LZD1)g$))~;>9UkLXQu=YmON${-$p3(_FC|?`!&708P5q1}_^JXpI z2p9bXe(;=d3urUkggZgo$v-gf7UU=5A$%Cr12^H5peNuS0=^BReA@*az7=%|VF@SP zrj=tJaQF_YXLt^rd#9F%b-?faR7*=9a1CfW;v{?$R0lWVIUCT|z+DF%`!n=caNB?j zKvX{o*MVL`|4euTXgAzlcS-DH&{~Y4geP>NpNE^U9P|*}gx7$cg?l^jpCF3!w2i2@ z-EiaiBH;HyG60q!Ue*;2>2d|`0fI> zZ2>>XL%0D%&k27e+&h4BkM?{F@F)<;IT6?_+=OfH!?=cc)&lqKMc)heIH0W$^1)5m z0^(y9aO?nNy#)CToC~7-Tm)PPA{huT*oHK~&xOEYP#)ZSfM?z>vD4t54*U!>9q!M8 zhdiLkHXXQBxa)x%Ks1&S?iB8>2PM`GszKNx;B%llxN{$pSP7_THrgZbCE*?hekt7S zVf4cw@?!_Og}V#*3*jCD{zVX{~o?hVl!fksJ{XdAF=l&8hgUD?MdWCxz@IS&mIE1hu?*A#wi$LTa27W2r zga<#3@N7Smy%Y1$Dr^3t0M$<_DAA zEd8NQB4$uWGy}8~Jg~)hzTX(EG4QTFZw$K}ML6i~WyhA`Eqk`?-NJgt_2l+U=&9+c z>si**)Z_1I>*?%S-_zC8(=*t!y=SOr_`aR@?YeLGeS7ZPdmrl^*PGirp*OF0O0TWA ztk>RK(_7cOthc_msn_4z*4xp$r+;rh+d6J*?$!xgr);%twQsH2x@>FxR{z$vt?RaS zZtdFIvvvE{p{>JPcW&Lib9bLm+JG*vu?e5yswYQ6H%-uL)W8TIo8*LlQHrhAV zZ*1D=-`KXXW8=Dwog3G0?Aq9~ad6}IjYAuEY#iRWbK|a!yEpFHxOXG#9@m}QJ)wI_ zx2?OZ+umK%UDv&=yS}@r+uz;R-O;_SyR&_m1x2?w#Gcx_5W)>E7GT zHjUeqyJ^CvyiHR!**2AJvTv%{v~E-9ruCb;HuY>8+_Zhu(54-mhBxipv}@DuO?x)& z-NZJJ+nl?3!sfiqQ#RW+Q=Z1o!*#u#z3Y3sdV6{Yd$;!v_3r2$?%mnDt9N(rp5DE^ ztZ!UjZr_ByyuK-Yw!X4HdtXgoUEi|4`o5+&c5}1U41=$gMHikhWd8& z4fpNr+ts(bZ%^OeJ}hYabNeUs=k-tNxAm9x+xu(!>-v}V*Y`K|`}^DaJNno4clNLE z@9OXAAMD@WKh(dYf4F~V|E~Vs{l?PGI>>AiTuxDWJ z0NXZhTkf_A+w!)dp5)4G5}@whW%st-+i~yid)bz8TROM&Y#H3Lo!4@_9L{04ke{x4 z)RQS2m!VelpbjvUdL7DlxI1@K9m;KZQ|{)v&7GTvH|O3{cTeX%!}sLgTX%2gy~FqB zZmC0@!)Pm0&`LVcGKPEh_Ds32?!LDBy6)S7mN5Y>!H=@q)oVxI@98T;K6WA3Y+E~! z_B_<-^+?6u0o%5wZJpbOwy|9~xW@v2T{hHh28kGI8{5=@I?}P3-P3UoLu%NT4)E;g zVfS^Q<#nLs+xjSw`M{!D-DNZ);mg z(X<*z0M`k7oaY9exa)~N!g+eo_rvPBvt&ui(YfZ}whiM@U>7gC>|=E=%+AW#0r)?U z9n1Y<7>WB=GgXUCN0zI3rKdbbtn>Nb2rS23n*MY&hU5G?g~%gt=IuNf)6p1iAH1>` zXwuqeMC0}&v~d0-HPpkA6clcIndX0cO*HNi;_En01SelaJz6qGVV>}(_|=JUyTms{M&c7_nC3J?B#T~g$xUs6e$)H&@2+2b=&N_`(^$3lvb=X6Uv_it)A!>Q+ZPeu z2eg9@1hFX{mJTNE&0WIeFbJFMmsdoG&a6`RMfHF@Lg`%!O+E^}z?vMX!Y*xYet zd29W)jw?Fk@4#0z8;7^{j>9@JL7cIJRgbM>!7q`nL(Fx#Re0>`2Le#ZyeGnRIfl3pQFaq=ye*m1yA>6*^d#-Km4jr;Dp zE9_4lh8A3cjT8JE$HrCJ*x22duc?JZ8`RH(Bu4x_TMi6zXT0|o&h}zdJgnFXb0%mpcgOy48yhy|9fBHN5D@&yFu8lV1EVae|zBgLjM7T+Mj(1 zf|{TIeTD!3E!PX!1#~H>6|@r64!R6p}Fl0eCm43k08n+y9~=%{`V~KIneXs_YQ%-2L1;0GUyf1t04XF z4Y+rL-U9sr^e*T<(1#%X?<2TBHvFdNpWyeO#IsL@ zC;Q*sbbi73{qD3MH1nNTfBj+4nR5<*8ul{UuFm&#M2W@xtUi9B@{`1EbUu=Io zXa3V2&z|XQuCCef^Ak2)^V-vw-tqJS5AAc~CtrQM=ZLb2$270#JM}v6>!%#M^wJwo zZ#}cqw)}!QZR@w?UbL;R&GE;)>Pq{gpTG3gfoD(OcGs6DAI~oTtmT05y`c-=d1Ljw z!pj#uvvOJOIku`J`#!#L#g<2Zc3r{GD|4jF?p$;C6-Ry6c*^Tv{_>ya{z|$@?VfYC z=Z@YhW-v_e z?Pd3Rzj*40fB1e=(+A2mD_*{_<=U(4xBmC0_79H9z2o4EUMXlj)BVgo?2=!7wZ8N> zhrfNs@4k5Rpd()X&1(C$yK>%Jv)$YDz4i4uKfQnaH4__^xeHG|{Hx;efBn;6l_%LZ z$_qEYaQ6#O4!r)rEvs{P-sAc~<=Ge1zP{v-wXZ(#$ysmg3f(kq+VrokzU%1Y?KNlJ z`tJ`O{QJaxw;z4vlDD@N-Z}C1KYzW?J8wO|WJ=!SGhTe@s89cU>TlcLp0wZH$GC$> zo^o2~gxWp(j#=Kd?Y`q)Kj=_b?geMRe)#s2OHSW3?S^AFK2m=0ro7>2?w|1I%g66` z%m?dV?VWt>oO8dk`<0>ggWoyh+yh?ouYCDW3qJhBEh&?(|JOiu*9})cQFiaT`JS_? zntFe7Yx&GmTkbmPz4kkwS^xU1-8-+h?H&K!{SN&})%Py`>UPIx6E0qH;|1*vPmh(x zextBwKfHMJoaf4Vw|_eMpZRaDy1DM1fhq63`T0W|{~vqT0vA=a^$#YBPoT7@w9v3H z(-;(m$^vopjDn7Wk1`uZ7=+|8!k}oGn3_yPVNZKwPjB4{Z&$OI7c0#k8rHS!VPRzs znT3``Fa6fqd!NIa%{ja(ng&C9vn^Hzc0E z@nYxhi#^`6zwnjcxpMlAx6j!Sn*EP1{ic06I`XQkK7H=!DYj=1{rtjblV0rEC+Wdg zj&wV+e^RgTZ;zdSdC^S^zq({re(`y>2~WN`|J)Dm{5-E@YPVr~?)iAsdp9k<9#evuBpS}J(+w6a2&FlT-M}^6coO;RH2mX8P zUQ5i%_YN=q{)2Ikj{k1lxgR#<+upxubL8?VuU+)aCr|gg@ZCo){dI`@AD4Dk?Pb?F zKf8bD+~}p7PuVc#`AJtCdE)$A2M&Gb_`L(~+yCcn!(P7bW_Op*?)>z%+R$NT!(M;$ z>(E0VC*Sd}$987lUg+5O?*4$qm)`&IdoK(bJhI#QF)@Q{Cq+zm4S=&YvAup=`}Ce2 zyVUJ>PfqE&>f`$J!d`o0$~pHh2)*Xf__yx);m(`yxy$jy&8OV3`sk$#ug@xb{Edc# zm(1F*=+g(rW)G?wf5+kjNmu>WbGW_m1^d$<6IeL^_mPB~ZmCM>^I}xGwesk_PrVoY@^w!YjxBximtnsa zUT5vMyihYPyOk}k0biow(Y;^o7)2pto|nQ zz0vtQe;xJjPZobYvDDM+{ikfNj@>lqzU3KD{$BXl@Rwfr?bPS){pcxI-|cTLeB*`0 zwE8n%e>AXX!oB}J{Mf{2R=mFWrF7+r+lmepr53)hsBizdPvjJy{nC4mXO|xTU~=EP zCtcEY)d%@^S{yeIcTb)=;kS`LOvr5bZt#TdD@T5Hz}EG$r)Trtrs=-}CcwO)DXn~W z3-pZ!{HQWx-f}neqXzPUXB+E3-$c(D{fzbWztuS2Ly^YvXWnicZ=DHWQ*JE((H+Ko zriq?wDvaevFEQqK!W6>D{~wshe*)<>l0VZV-Y;%5_S>4l#(sNurm_Bs7aH?Dz&}R$ zejiMdjqD7FFy>c|HRj*xZ_H1Cc#X>E^Ct0*gR(i$pnPVZZmfS`H)B2_!mKV+KN^QnnF9_a6k@^}6qV>|DjWh`F@<;^Hv&w)RU`b~#Pc}_9Wv)80wt};n) zUz2j^^BUWE>8v6f7NSBfQkD27-9T2b4IR7FO z{g0T~zX|+ol<(Ch@&0bo4p*6sSNE8-tGAf618Ys{OMwU0rs10dzHyyd>D9_c4AkF& zN^d3VBlIlnX>?+Ll*y%>PaDB^G>7xsigPLFYtG|5RA+YmHc!Z($$3aVyT(I4qW)?) zbB0%-&_ClYv?oNVe}cw%Q)}g;>tUaznEWctABpYhw%)iH~eKT zA0^~RFXr;aQvN0gKiCKJ2yveRuMf`U@rG1zJzsHJ**lQyui3%*8o~cLjq_xc@LM5{|A>JF64K1Rg|?@4n=-x_2SJ-g`IF-7%#1y zY *&sJiu=J^7{6?VNgfXhd{!{fa}`1$?&dAya+@OUp4^3@BueEiv**Xm0Iv}5Sc zD%{+H>qVjGlI5IF%i!|a{I>F^mXEm62iLFcU)Vw|;C_ph{P{z7p=T49*ZdQa#_bGW z%k?J;J>5k9*7xT88G=ukDfEl-sp(k={Q}xwgPVSE-NAz?Ux#zP8aMsm(%OwU@IT7C zMSejwWY-R1|8~j#$H9KA7g1yV>qX2`5wFxPC0@e$aMeE_^X}v6RsP8h94+Dvhjliz zrxrKv;L`RIL$BuYB@b|UsQ&B<3*+{L2tR1$vj`eAEGLmtIe}pd*S~@LVUW;s2r4r3 z18z{lHHiOF$}Z!48ayYBmsSp!i}o}8Np4RHy9@6e0Ws8*wvj7P;<#x(2E?;$!%WM7kl(TsK3K8{7 zn4|cFKb6&-IYr!j=e8niv z9~1JQu=;p9Jm<#SB}gW_eiP-E9TWiOWoWj~k}DA&y5`B;_cZ~rsl zLci2*pBc^hG(1cJm)2gbgK~)Fp;{^rrL6rtU1^Z?Uk3)G{b^!6prtn~m-C9`hXW;? zFNx%azr|vQ=XmWrUPT%&`GyPqV&JKj|1a-G`4A<39oM6c)AqA*SBO$2#-)9Q{)Hmm z%CTG?&u`$R3OyyF9Zux5^3*CGZ}@O7uZ^ECnZfyrkGX=k`E6yeXy;4dxjnqJ`uK=R zzYwsT>!}{X6>JmnTA*P;f5zj17r5>f<$uymoUe-F@)rxfx0mzbUe5Ordb*y;>sPo` zzXqJo<>mJO4H2&!581$_joY^@T));YxJ3CZ!NW{&Y31|fYec?0%;mLwiGhI` zrmI#OH+~k+`H(BQ-*$<7`5<4!E0v#NH*nrPkn7jRA@>P8?SnbLlUW64fW&wyTpBNp z(du;<9-&9n>#a9){q-Zco*gj0!7DSH^WptDe+Q?P<6>N)Jji)1y~kKNJY6Y~+LPx+ z`xbI5_gkINV}W{v^{zy!cdDrGQTK2=XNi6@L)71jU%8&o`5$Hdt=!J4Y;I4G;1fiC zrAhr@`BE;Q79r9l=WADU`?db`Owqs8h;fLj z`7=$FA8G!zU5s;T#5hMAcRd02qui@}0FBVT)=c9&kUTd$W0K|Apgx{c;vFkrwgdWlFi0)C@X%W2C zFI;~c=c}*f`mciWgx953&R4m3x?)8687=Bd<*8gA&wAo@uPFZ&w{SwMzX_KMJ*&7L z5llG)8hmW$YeakAkE>B`75-0?`h|y}eqefQr2h0)FY1B%CFY@8KXrc|=Y3MY^FB1B zsHakDH`0Xt@i%e%OL_8?M8TIV=lm-?8Ol4S@^&~%YKJq$_^o;Z7u3pO74(~E=N`$P zPlSK$Vu42+*FADQmoFCOKV0}He7lY*Nb|=XTv%ermI3M06=hdje&xPeATVo z9?`riZ>4ZPem&5Zh=<@bs9aOWJ(mxy*)Yk#g3?Q^Bz zwRMRfAbqGON^0kC6XUd6F;1H->?|%7_PDrz%6at4jO%#*&Xm?4-d`!~xsuDDDdg1~ zIIoC#1Qb_x?HPmqJWHvaAqs-{(_NH@%IkTCXyw6b(yo3yn(MEy^9cVf;{C$K`O0&- zJz9O4e+{o+wNgLwn}|35MjkITv+P=}*|XH&9xsfeusvyz+LH&M!^L$opXg_WIZECl z;h%o~_E?19LZ)%PzX$a!`c9b^Xy`SX*x zo!>}y7J~rlul8)} z`US1syN=7R74oIRez`q=Pt;$02MJz1c`)VZa$%2?+c{708`4C2#rmO^-g~d&<)=c- z`$CvDcvqV6PrO(ch2q4n9iSHdSs{%#*0b?&h*BZ?aVQ4t8db#gM~Ze>)Bg_qz;at9 zjWY+0;ri?0-5YoTWY^6xoR1O*_a71I+9uLlaS>1NTS9)pwOqdXG|q<#`3cY;VZ8NH zypbhbUT%la9>96KSeMiK(=B2=pxnk4YyNjb{Hz>`^+VB2DyNC|GyXN6F0G$^{A?~C zjt`f>HJAMh&kD^#J42K-QO?g2e4ja7K3-KamFSoaYJFD>?7JnCsX2#|_Y7p+9S-aoUw8?awXXK$NeZ&GjVkL}1cJHzR&X8`3R4p#-;h=n(8 zUUQKs=aphT$|~Z$QtKb3b&|aaB3`K->&fahjN7I5wiL!+=(l*u&o2x8)%ZXeT+ocL zYg-`KA0_5Ng9LvqWE?C1VcZYecq11YCe$A!`T3thf10HK6XEB2$<9FST!Xfx}ro!h6N=()!s$eK~Iz;|*wD z*tKE?=PSSC^82|vlzGsepdZpCKVN#DkQeg{O;4;zdx-TI?FkY6pw{0x!Z{x=)?>AL z^p)uMOT>8o3SlSaC(AFXzvyYwPTt@W@rwQ2!9q{h1w370Qa}5_DlT7d=ME6tGs?5m zIiDsbno&@m@TwGc#!GgNKTX*A63_Q5xwvu>WE|QbW#xX>`azE<54BRg=q$!jC869N zt$w8l`-3F=lVU`?Qu{niq)SQhPuHtQJWSl;$X_Iw}A`AVrA&NOL< z*N5?WUznKpiDq8;wJYj@^%Jo#17bYh3%UIv`?%p(3;Pv2kGEEew@%BiWn6Hk(DR3A z4;5+tGDP%W;lg2>`~~BAy!IUKhlj;{YLn<6Yw;mUxE>ew)Qfen5-|=}MZA+ob3Nhk zj(@za74id9Iq!af^Q$2Jczq@ED;y`7aN)C`c)csy0eM~SiLqRdlFkLicB68^HQb*i z(!6ZcUEFVLqk)JX9+ZzSYK+!@$z$;uygqm&W8-( ze40pau84P?h*!(sZ?t+ho-5eLVuy7b(Z6h$_HpjJj@z$FdaEwr>D9*V=bXp+N~s+VbZ|Q> zrG1Cca4s*mx6gY7FZ!KWo-E~gQGS&FaC@}<>-kAsKI(eTr*pHE-$gyLi}N1XUgPzk zXs;S1KR87DET6}im&WZck>+2+MES24nEDo9OE#8+{pI_KoL_&wu0j`U)u zBNN^blasv|1h1MjF~t*;jvq0yrCVb&@*NAaGhBJrboo{s+=AE26yz_orn8$V9?K~9 z#ZnKvEX$Dv?*hwl71+kjNw-c)@x(g7G>TP&7n zu+LTO@Dw<+J%tdrHEB+i{=d~~wM03R9q@86=Ui8Tb!v(y4iz~voF3=Yi7BxTh(6Yl z%x^pL-L3*B+5uLlVl3^CAJg9fNwm-;#z6|R*?YyTkS3DC$sOfkzv%x057YQN*uxem zL)Mey;b=!EimgxV&)bp}^(lWlasmJJBmip-7 zjubcl@Utt!;r0|jBjnC;rn~%F&Jm7D;}V*0v8~n=r!6JMVNIG82fb5X5o_9`9ZC7= z^BnH{ob2>P)@ZABY_xR&uk8QpF9asju)w3Xf5!jOJ%GI6GVPg|k#r}exQyUv${M_3el0>}``-_sO#oxh6{3~-& zYp6RYiQI#tFZc^`k;dPFNz@9;UolVMIf(O>4y%h=6n|AgrSZ2S7uz;cK1rptO_6tG z;!bAfwK{RF>?m5L|72IbHbvf1Ime4WRPDc_SZa~8!Hj!RE7I7Og%(yoo1Y_(pU#$u z+P9zr@xXiN(s4waSCj*Z>HwW=VQX%YvmnFaOrKwrT>z_Fge!n?xU-P0cy++Cjy5{O z9^nLw4kyK-5YI_>XcL`z3vpbZ;)#aL_hiA!pJR>_oLf-fT;#}eEp+6#^5%N7aB9aq zN@a3#^3(Z^xS~RrBNHZ<9vfL`gy~SK1J}Gpf}j&uEd3WEEyH2@#8x6_!}$!|@|L&p^-s3h%7~g{47qq^)t~`(3kIu`_a6w|FEm7>216KvDybHq3IVX0W z&}BJX#py1$*ww=D!wZV?JlVN$1N*GRXdVWtKispBxJ1RL6mLFYZ>)(ZEBdT=7sI7Y z%s44(ee?(WZt2dvJlM)hx8nYr1Nv5nt1#W^hA`>5POa5yt>@TsX@QHaLOYyA#n#;1 zT(Jk%T2*oGqQWdFUFq{+6B9FZyvvgc9xk-QmoGB&3vxA5d~irA5zvIPO-88o+#H*q;&Hi|Tp~6?IXZ;+c#A@n_%p(5{-p4o zKgm5PI5tEXxd4)qFYT|JclZ%eV!mY5c5?Up6lF_!NqU@K%}OMK9ri#-OLy)B)B)w&?nkrKzc9+>c) zgbag~|77U!SVsZ#BkTih6Q)jtE(8V=3+P@P_%aWgJZFw08`=x}@{i398+Iw99I!3t zo}7wC!2~Z#pA)eu9i0?mk5u3i9R$vZ^z4alSuw*QB7EH}du|@?fsJsmdI0r(tZPou zTpX%G4JlBjhjs<sbv8Iz#Q#}yXMfvr~PZEY!Iz$!SV6l2g3u_`Z0O@`Ah zHm-FS#yS3$_BJiZe?voIu3%6d|E7lO9@VRQ3~le`6lIz(JBd`nyqpx+mIuda1RK;h z25~uaO=3jhwnZc61Z@n=H~(qi7WN0w#loT-&VdSw(mhsfm>-*+U&!W!Ft5Q%hYpD2 z9)~+dzGiMpr50%Npc*3mNo-H4 z!*;rGQC_;$p3c@fC#7VwA&#pUddzf>EyoJSD6^-sJ|7I$9o|?c^`fr%q-Bn5EJ%7Y z>DMh`hGT!0o&)0mezKHw_I{!`rKFt_oIPv47sErM&Wr^(=5AJy&z4h$J0RsvC*rZ- zH;=Oz&XKaitZYK=w>C|duA{RvSjlNzyapx)%mAJhT)NJUr324UiTsUW3*ivlk+CSx z3E#to8C@aF2XWP4q&5b_MRue?8J{N#3}TpV+R0k#Hz+qSsAxy(v{7k_XN23CU0};e zanPe*r0;A=FKvN-l{S{m)IsQn**s({_Q+5#Q^waJsDOn7r{o5v!ESnaC{9Pp$6+b zjs?z~B9}G04bEaENF)%~vO0OC1Q)N6vY?D=}>Lget zwhK;%uQXLB$*Pm&E3q?R;H#EYX6Ux`3JB}B(9K(Oa^|$NptD&T>|jA}msym@mg?Ap zC1S+Zz9I%&e>lp3v0(;pzhIo%xJM1GtTqZ7m7b3uOU6|M7&$fFuWnq+(Rxi1xWye{ z%18(6F4&5UX}{NQ%_pJE@r^&pd9+t*bu|?g!aqAeW8-KTR*b~DS=%#R6!0doKtn&) zHNS}OM8Sy1i6i93dTxAM_ahkrXa%N~qokJpN(ZDoeDn5*r>@1vTg|*6&4y?g!OX6hdv%<8m zG3r8WApERt*>Dl6WUd>|$%~yYC@O}zBsxv81LA^JAGg+N#$;fYYq`x@FMYp=E&0Ma zDij0j0&R7a7H~L%j{~U`*L*rSZsKa*z@+2jc-r_uMB651ta*TkEc`~3U?Fjgv_+9%3hcL|*|*^`f3z z7j%?zJT%fF%0GKIot3(IetHJ1Ra`!li>g6Q-Be)GX=Q1Bg#F^ z1GYJEax$R>F?4JiPC_=EAPsUp7Q-O|)3+GO-^~7fNXmZnOh#dDQ*gDB8#8YDq zHyqw_7A&&aVL5;uVQFFB7@Ymk)(igHRdzN@YI2Gs2YRt_Y`{MWmY?CY$aqJJjr3J; zS_Gd<)*lDZ7HDA0A}2?TybZh67PV;&r06ej@l3t%S>&SVtA2|)if{J&=+Sbp)h z%&^0qxDY-mMRnkEJs#?CtoH=vz9of0= zod5L$l5pPA@Ps6ce&NLMRJ2Yj6L$WxA8mbQ4h(v2j;SpCaJF0si770~ffv02pBe?@ zNqoQtwEGILu@!)X9MvT zEVo%4Y+3F-ivRsev<@iOI?4r1Z#FJ5DAvcQk&WbvNvyB0CCs8 z1qE;vKL267-rKZz_*p6~NQh@|u?xZFU-srOSfQBf(qGD{*Xomz(O5PAjv0-1X&J32 zxYeE%Yjz#(No{TL@8(I(t|p#ranWJpc&+ilgM7SkZ2R^u?%%*uO1W*Y`2GxXPr zU5<8Ya^^ro4-ZVj6N2r~grihAbpg+5LD$iCJ>n4aNc!dsXf2wKm5k0a;{XEA)c`aTZwJrpKhYI=#-fxTJ~`(rt7dK4xm;Pi<$z$qO{XZ+X0BYnvnB zwaXcJbVVG`Z}mDqn>L82q{(w+jW2(&jNorO;kLq&co=`gYELxdNCWa8mcaSyZGMr* zk)P>c-G?JH*8_$v1`+nMNmx`X^1$Q+5_Y1;C@~0oth{wWrcZ*@!1?xK=bY>X7M$*| zgGp>?59_^|`D}EVV)U>+y92W!<9A*#`&;`gsFsMfg!4}cK1*4ceZs_8ESc~o>m2^1 zWs?LM+=U9`|NOtms=uHEzgC?xfb*#LE#bq_F3++(iX0$AatvPZ7L8gLj5;()&}IX)POju~GI#eANKAKw-_J z`L`D|ZufVD^2TG%{G}(*+A`b3LL`hT95(jhf&zTn4c=~&(8ZjfO0neYJA@`+p8FPQXM36l|4Cpay8=^1A0B7R6GaPScpz; zWQ`A#oMgQscN;5>-$<#^dnN+H3DQVP^MXkrEiq=d(I)$%@wOyhL#%m)&>&D|YU8UX zEiq8ZbDRtG_XCZ<7sXgJ*gJ(_h-Y*dNY9{v;Bd zk1+dy3eG4`rXA+*hyy#ZR5nSlCbpkaG*(MaoG?X+fXYQ5v)z;7w+O##AfFahR`WP2QEq_t&^ z8`EK>ty!B|@3`n_{nc82Z{vcs9qoN7?eSo<>RaYYcs9O+DsIb+YrRh`&?1O8oN2Es zWA21^Ikdk_Hlw^{{_MDhuqCFQ(3|b+uo6dD$-uR7xvXWbfvp|0qLyki=-Zl!&7hwb zThi^RPL>#G=jiJPtr@V4!rye^%&}!yad6(EF+*#Udg7R2mNPAzP5r)^oQ(9FofuvM^F4C3jaB(WFIHm0e=GRY{Oj!Dwk z5gk=)gyxPh+Y)E+_F1;^*W#Mf%!tdloSh&_lY|=UG9QbYch`oRPBdCJv8r{G>xoU) z58~KMckvx?d}fC)I%rX!pV0hZuB9<`P_ishkS_6FV4kq-mL=;1jaJiS8OPL-$?{vk z=+I;}iK&BW#}1`wWijnFj(VjO>k{x*u2Sw{OE{HjuU>ll+2d3Z62eQckP~4aq~SA2fP6p z&XmAWpm`Eil*dT6Lt%-9mxE@&yIbIp?E)vPiNFWVEm3*-@aB+3@C1y049iGPTdLRh zIYwNYpUtCm!HL8Yp0D%{aPY4%}wu&oRMEJkG;~w zzQ|!K)L*<0-w8lMdk;VE2w34gaE=sM{ZD0I5)cbJ2I(EK5I%~IA3e8Nit@7Y9YyFL z+-hSkv}(FwZUnFN*3?0CXS-yCb#f`ZZbk8e4gV5J5O9LwKY&Xe8|r5B&9 zkUpVfi4q^zg-;#RV?$cr`n~n(#Ij;bWM}q0k+=!W|mgkZ(lRY+hKCkd0+c2ZK|_f21+yflVP8vvOVTCX=Kp8 zCdITRON=KWVXaw0ZA0-kT~%wp>CvXT+mShR$*KJ&Qf)0as_CRf8Va}CuoG%*xp7U) zslkY})dsdmYkQ1rZVlliSlgWXmYdx)o7imCe_GdSYg?qX-A4M&Nm{>4Z(^js*7g`_ zuyAq`JZYe{g+{`GdG^sKJNvpJW^sWVK1Ksyrs7kDb{tb_W*@FaW7!iuOwn}otv$=w z{JB1nGJ{D$Sd8!ZWz=HKQEx zAv<^f=fTz{GF3XM;H$+?vd6N8tpIrvPR1^X57d z3h@u1X?fY{`1!$vLTeuWfXz@`D17I|ESzzf0wvz19O-vkKXsX#p}PB=4?lMUZ> zg92!fk~r~(68ECUPw2ruKFrix{Z6N*YXuOe|0hMnXBzSgTll>Tn1L1)I3}mVYsm}P z^c3MZ7ykfVs?n!3TC7_8gaHPCuWo2>>@oN7YsXjU<$$wA zE>3m1F$>YoL@XoB?Zlal-{gQiH2Fdbf8Wn=Fid;$N;?0?9{-+1w6^r)1-#y9AImWL zfJh80+*kpl9k%>J{2yN;%fsrWqIFrxrfpCw@Z7Cql51h3!xG^wO#UbtKHim&YZ1JB zk1NQ}g(3~^O32F)HHhC4KzQC(m|DQ99xL`d4y^`=nl>&cAHM35P&hWf2-avY2KY{( z10!H{jWyBw^BRBCw+i8jWKn0~!76-xx$&!24CWrDJ;|`MDS?#>j>Z!4!jQo-hct0L4l9t2~V`ZV3Bn#kc5_qd{`P^$j$Qvorj;w zYkcvaY5wwpfR2F|l8Avs0hDX_Fk-YLhOs;sX+mRpG~f(qbiakgpgIl=^YO2C-g@Wz+ zwk8p++oeX&H=UVDi-XMpIlk6~Z~1XH z!zy5Dvaa8}6Pl!;jYo=%jcLz`5z^jzUBhMCm%OztCYQyh{dVNN__LpP1+vJ}gQZpJW@8=!hORJX-%b zLjPrH{24tghSM`=LbwsbqD4>vP+fN&x(K3j5m`esyC11;W2w;J;2vpwgB78vwsLvtON*Af=nq zoyi8l?;r>r2&lW#1ODY20)fTfh`O`?d%(ZiZ`2jQBzi&|DAf!8*EkG=ztO5d_6tMy zR)XO-{^~6LhJZwOV0-9Np>Vf53&E|xI51w63sO#HzYw9G##n;jE=nNNLl56sIgRP% zsX~i1tN33n42I&m&^Dgt|Nr;@J@Ee?XpIN9KcFZf&>Drq|Mv42^-!Sp{Q?|b0Z`*V z6!-0b|F!z=E{YPW92Z=l?Cf3`y8DA%$DQncFQt#bMQ3GqQ=tBz3m7kKZC(^%5xX1i z>7+pGFht0EmktITtXv}SqFv8{J-roG!%V)XGEHD7!#$J?fj@X|UZ4W)NTI+P?0z@p z27%)l?y8gvynx{@$^!z=XE;!KT3{E$ot2jaE@1c+3dgKrPQc6<+DyyC}nW`BWX zSG+rb{Z(Z?pYwK!SA_gbiH{QUSrT8H$m4TMyn7MXUo7$Ai#cB+@pch!rNrNO`zg%+ zDv7`0PR>_L{O6ByzDDA=2>Duxe^c=F62C+6ib$X4&kqD2BJmA^5106l1s^5xUkE;4 z;`a;QF7XEipC<7R;UBle9~SZ@690?feG*@Fukhy#|MF8UaIM70iFkbyKU(nh5}zRW z28p){UYY41uTAhl5}z#i5Q(26_%Ml|CirlPpC$N6iC-Z2D2aCoK33v$1s^Z*iv_Pr z{0hO_CH_vq&y@Ik1fM4H_X$2r;vW*cTjC!Pe6hqoA@~xBe@gJB6925=DQ&k=l>#77D~T;hidK2qYx3O-8Wrwcw- z;vIsIm-uYKs}i3tc)P?e5d2JuAEC8-62C;qXG#2xf_F>&O@c3$_?rb^BJmZ1FO~Sa z1YaTX_X&QD#6KkXN{N3=@arW0DZy7s{PTj}B=OG)zFOj66#RCH-!AwXiT|(Q_elIV zg0GeMp9Jrd_)a3f>Lq@YR{kaaO|AT2=!up1Dj^>)@&6I>s>};{yTrdD_?Z%am*CSRzNgTiCGnGmyj$XX3;AM+ z4-tHc#7`7@N+teOAzvZ!eFVQo;!hWRrNoaH`qxQ(n2@iM__GARN#gqnzFOj=1ixM4 zFA;o=#7`Fd9*Mt7@U;@}61-31-w}Mh#21V7Hb{JdkXL5;mw#>C8zk|ggq{$I_Xs^< z65r(s-j9S!{9Yj+De(&gA0_d{f{&H>U4oC7_%9yg@v0KPRmj^V{%s*YQ{qcRJCi2y zn}vLq#4i^5-4g$ykS~^aZN5+<@ruw>D)E8B{}mE{y^voc@!CA4QsQR{`E?R+75P;q z@g+ijlf>UB_-cv2OYqwz{!zi#Nc;KGuX+P57B6e3l7cY{HkC@M}!?bte2K6Mnl1zsH34neYuJe9(9k z|C{iUCj1&wuYcuLo&CFx@PT|Ohw)W}&jv!aqy+8p8jF@OudV81Y*@;eA9tjMzzeh1Bl_yNRAXgda`f^%4FVqQ8OgD&d1jK5ioVqX_>n;o}K^Cefo3eg@&~gg=$= zGYNl;=uabj8j;T;{3k@dnDFa~d@13>3130@mx-QA!e2w=s|f!bk>5o4LB#*t34egd z*ASk!bo^#5;Rlj<>k02Ayh7T~a|s_p`2BNu`{0V+laiK@aqVlM);#d zkDKtFiGNB6|0mH?LHMPFuO$3Ogs&p}RKiyieh=Ym2!A=TrI3136_uZf;LgbyO}wS<3*$omLCi}3Y?pGNow!Y?7bqVoJg>O8^+5&mhC zFCm0~jqqWFuO)iI2_HlFNW%X>_$b1^K=@d~2M|7<@Pi4j68;9l+X+90@M(naMffbj zuOhsg@DmANO!#huFD3j!!dDQ!n(%7~e-q&=3E!RY>j?iC;j0M$2;tXFXsoBDgs&p} zL6X0l2tRiKScO?!v9YA2Ez9x z`jw={`W{B)g9!f&@k0pVrxW=w!aqd#aKeX?cq0k_Hj$4a{BRO)Ea5*Rd_3Vt5?&?z zlSIFr@I8nags&s~cEXP&d=24WC3fy1{3k@dmhc}Cc^~1A z5&3$;G-!5tWY{2Ic~CVVB~XAnEr5&m@IhbqEfMdUXTemK!zP55BKZzufgL{AOj zXAyo6;m;!W)Dk{`@IJ!NBk|S~{#~NKf$)0>uaJ4!*Mtuu{2zo5A^cpzhY|i4!iN(+ zgxDEL_&}1bD8i=``B=j5BYZsJI}tr9;hjX@PWXEWKa=q96F!abRU}8&LE4Mct&;kyy}D#G_C@opmgk3>&3;h!S%+X?>< zB40!J&P09>;XfkswS;#Qc^~25Ci3-!|Cz`)5PmD+mC23md{5$sAi|$Y_z=QxB6`9I zzn<6=PWX$7o=C#qP4q+&{!_xo68=4+Kc4Vk5?&?zErhodzMSwg2|t>|n@0Fo2%kmx zIKsOLe<#sjO!%(|Uqbjl313S12Etbm{wxyj8p8jJ@Rfw0MEG@t|BUcegx^j0O@xQz z(E3$P_)48slgdpKFrR`KhAwW zB=&?8p3q7p;r~r;L=m18b0wDW^ND;s;rYo2Zj?&+?nIHD@Ni&Uzh)A?PG=P*jqp#9 zc(VwPYnR&PCcJib8SfPn{t{wO3E@W({iTHOt|^DV6@-Ta^7^%g@CwmWN%(qQ8uoDr zFOG(@yH$kmN#r*X9uEHNS2f{#>nxO8!Uq$+hVa^6CEngc_)sEWOZZa>?<4$ags&(3 zF5-sE0BB>d-uk0SiJq@2VO{v4t| zp73W8Ju2bPCcK^S{Rlsk@E;RCjqrm=yjg_rPvqT%KZo$egg=+?C4`?$^p_HT0FkdC z{B|P0hVTQ4d?n$}Bm6qTM-aY>@Xr!{6XBmHd^O<*5q>-2&nJ8h;U6aU?;-pJM81~r zk%adV{wkuUp70kE`3AxdA-poRvHTAqQt7ZLdo!hc8jFv1Tdd^q8U5k8XeD~bJ4 zgij#$#1ei0;o}Ma4$-d?{$j%02_HrHnS}p>*po(h3z5$vd?=B36F!>A7ZZL1kuM?q zzlnS);YSd@g7DRZUqkp9!dDW06yetqz8~>t72)HE{3gO*LilRJUrPAxgujgNHH05c z_&tQboba`T??L?LBm5OazMk;$gl`~x0^yaE#_~Ug@Ii!sk?!2s|i1q$Zse7 zS|VRV_*5dlhwvK+UrYFDg!d7CI^pXHA4k&LK=_$NUP)~%|KUVFi11eu`4GZKkaUF+ z{tTigobXo@J&}Z;P53CnUqkpj=M?@KuDr zj_{iZznbW;Cj1T}zn$>c6TXJ5n~1!d@T&-4O!!j5mk{1d_)@}0l6Wf!{|n*Q5dL+-R}%ha zqJJIXcM$n1!j}>GO@zOa$X65oRwBQh@ZS@@hVZu$eh=Y4Ci-g$A42lWNB9b&r=IYu z3Ex2Y+X=6T=i+hQ@(#iW5&n8&PYB`9Abc3%?;?7_34aQ)Ka%iw6Zt5@-$VFV!fz&g zJmKq!Ju2bX5P3V{?i8DfPNWI_}PJf{uGIY|(K~h6m`l z7sH`C?#*z3j)NIKd`wF(ZcIS>bsWm@E*+oB@D?582E>uN^*Zju@LC=BWq7rYPiJ_U zj&UOa(ywFOC^%B*)bW`NPth?h^dG59&@nFXAE~qG7#H@B)D6&ae}+SKd=A3_IzE@- z!+&b&4`+Cvjt4NjOUDBl-lF647+$aA2!_|{co4&@b$mX<%XEAJ!}E0<$*@z$gBhNp z;|m#1(D4w4Ejqr4;Q=}x%5bQThcO(Wv$x?%XEwjvylHfj%C=X<2Z(==olAZA^&xZ3$Kv>I>rT8$bTJ=W;j&G zmopro<0}|G{JWO^c!u}sIDz3^Iv&ID79A%tyk5s+8D6VnE5oaGJdWXIIv&sPd>yL{ zJ9Uf;k4NgJ=y(Fd2|Aw0utmqXzzFHraWcc9I-bOEfQ~0KeE2sl{dR`;>39mmyL3F2 z;Vn8&VR*fcQyE^X<7o`9*70ij`J7}&~ZM)ha0r?yBXf6TXc*I z4v>BwFJU-T$2Tw>pyQXE;>Hw=x`{6y3b$mC&%XEAX!}E2#hGD0U?`3$3j_+eQLC5zq zY|-%p3=h!pgA9l2_#uV^bo?;GhmUIMuVi?ij@L50OUI8eyhX>4GQ3{Lk1@Pf$B#3- zTE|Z?yiCVWGCW_$>lk+G_#X^U(eYCZC+PTThAle&C&L4D{0zgPI)0Yn03AQa@Zq1d z^j9&wPsi&S-lgMzF}y{`&ojJU$Ny${t&TS^yjsT_8D6I27Z{$e<4p`Zb^Id3Q*`_i z!wEWmnPH2LUtxHFj$dUsRL7ec4$$#`7(V=?mi}sn_vv^G!@G3+8pB(3{5r$yb^Hdy zYjwPp;nh0c#_%#7zsc}?9dBpYspGd8o}%No8BWmgI}BTN{4T=-bi9M%P#y1NI6%km zF?{%lmi`)s_vv^S!@G3+KEqpd`~k!3b^Kq3*XnpT!>e`tA;Zgb{1L)6MzQ^$1-PtozW3@7OLAj1|Nf5-3u9Uo#iRL9>l z9H8SL7(QIDrN5rxeL6nO@Gc!6VR(y612``awk%V(4?2_ zSHim`yhFlUCH$&{H%Ryy2|q632PJ%$gv%woQo>6lTqNOK3A-dbTf);MoGf9hghxv_ zM#4iSJV?U*CEQoSy(Anc;Xivy`7hx^65cQ2y%OFn;T;m*D&bcpyg|axNceFHKPch5 zBwQ}xl@eYe;UWp=O4udg*%F>6;baM0B|KWfF%ljs;XxAaFX6rt?j_+s3I8dLmySwU z9#8F;?(dcGZVB&@@Ky=GD&Y+hen!HNOZY(v-zDL439pp!5(yVcI9I|h3D1`BGzlk5 z*ec=C5{{AZPzeu`aDNH+m2fW!2TJ(Q?o$1i@F5BBm+)Q*@0Rcm32&9~s}kNI;b$cL zxP%{+@Lduvm+(pnFOhJOgmWeAlJIN^Pm^%6gsl=DE#Vjm50&sB3HO(9UkUe;aG-?$ z>?Y;Egbzu0zl8Tnc(;UiNO-G+UzP9%2|pv@$0hurgzu7YxrA3rc!`9IB%CW@mxO0a zc$$QhC2W=OXbHzic&LO2Nw~j+`%1W%gaak~XONWt8dg^x4_<-m&#-~*hHq0Pq)t=4 zf2zx?LsF8;=Ekbtu4!EySJ!O=;&pZTZvmbj z@NXy29G?d^4}JfE6_lj%jEHcxY+jh^ebcuYwiJ@Q2UPDi-)VytCCU5c=FVul_Xpp3 zu-c@ST@SM5V0W z^aIuG=eIFJwdS{}&8WT`Rpb5~_uC_&`UHN@=p-(M`$s84~c+J(13n^y9Ms}J~~8G=i37= z7k(QW;qSN6;0H6mEyEZuf*85qk|A-vC4QRy%G>+ran+jNz64P-)#sz?U67sJZ%3eY zJVEk*f?Dr;7($@mo&=*>>bI9MV5SI|M+5r#Efwzg9{$eHZ+|W|L zEysYvM8J_Wpr7CR!5!b+gMNO?g4`8;dv$=n-*$kcS>i%5##f-Ma=$$Y?Y^&acGG#K`@&7218@?mDX3&ux7pwaGrz5cI_yh=7`fjLLA&pJ&`-0U-;SKaRcn6h)QswJsQNHuC->X!p!x*K z?}H)!10m3FZ-P-R_1mY=T=;Gh0dJ)N{ru*EJHG!M@bg4#dd)Ru1C~->YAl`t4MqTJzh$W>jaQ>QGe8{1y$WS#og3XFSgu zfg!$t0)WXs03r3{pCI7x&|LVQ5CNa10sZ`TC*1LU@g+^2uNGV`{1$Vjzu&Cj2Q$BU zF~$)PBlp_|7-#ra`f2tnZ=Zy5)tcYF1yM8A!%+3dke%FbSApsiB>xYn^}Z(|1p4hc zFsh|~dmRJjiGT}eK;pMKzN_Jm@5z1OC|DN>(Ps8fKyC@o{eFhO=X$W@T@SJ_wP))v zY--OJVkTjvzd{SaUH3H%HGC5!<$lh4P-xej_Y4T*x`?6ndZ@|1`%yb9bvJ=_?md*} z#ZETpRSc2^LC}3}@P_0*^N`Ciax#?s+s2=hf&fA%H0YKaWRBI$BhCv zNRBhD=o2yE&rp6a;4x632PA%*GQq+xxaEh=M56tHRr`Nr@a`ppNHC+_x8Y8iF=Qgo~=d|0oxZ}J(L4nN^F3j z+zVS9*W;kX8iwqFAvcPUn`uZt|2+nGd_U~7N_|7l#i=D(vLY^psLwSNU!%l#MN??1MlDc9RT3{nL_uoP_q!}L-_ zJZ~8C9cV#(3q;5pXh`Behc5%}_@4XBuijRH>xK6MMf$Yya9@@_DD(#FnO9+X>QsN%y#%6WO~Ns#)4qQ|yTCm+6jbx{n^wPfF~mX$fsR`WMoEq{ z4R{9zyj%nvM*}kB;(p-k-+T!-leIM_i{m!932yWSU?xCY9)(Ve*H_Tv*$R4u&|puB zJHR!k@%4Y?q0YR4mf�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 z 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 literal 0 HcmV?d00001 diff --git a/examples/HarmonicOscillator6D.fmu b/examples/HarmonicOscillator6D.fmu new file mode 100644 index 0000000000000000000000000000000000000000..cb527deea5a656e77752d0038342e5e0075cb9de GIT binary patch literal 796106 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∾VqRUq3 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 z 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™H;~ZWl7?G)BV2EI$l#?4%9fC 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

jH(M(k|y^Gz- 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

;3CILRbkW33n1W_IUmo`*oZ1N+Qfm}Z9Po27?a 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%0DD;~!8k)uMStyR( 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

e`*YV!2o{2r|=tyjZK}bT14Fx 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 F{|}@f-=_co literal 0 HcmV?d00001 diff --git a/examples/oscillator.py b/examples/oscillator.py index a7dd68e..6fbf3b1 100644 --- a/examples/oscillator.py +++ b/examples/oscillator.py @@ -88,7 +88,7 @@ def do_step(self, current_time: float, step_size: int | float) -> bool: @property def period(self): - """Calculate the natural period of the oscillator (without damping an).""" + """Calculate the natural period of the oscillator (without damping).""" w2 = [] for i in range(self.dim): w2i = self.k[i] / self.m[i] - (self.c[i] / 2 / self.m[i]) ** 2 diff --git a/examples/oscillator_6d.py b/examples/oscillator_6d.py new file mode 100644 index 0000000..6fbf3b1 --- /dev/null +++ b/examples/oscillator_6d.py @@ -0,0 +1,114 @@ +from typing import Callable + +import numpy as np +from scipy import integrate + + +class Oscillator: + """Construct a simple model of a general harmonic oscillator, potentially driven by a force. + + The system obeys the equation F(t) - k*x - c*dx/dt = m*d^2x/dt^2 + See also `Wikipedia `_ + + where x shall be a 3D vector with an initial position. F(t)=0 as long as there is not external driving force. + NOTE: This is the basic oscillator model. + + * Unrelated with Model and Variable + * FMU cannot be built from that! See oscillator_fmu where this is extended to an FMU package. + * The HarmonicOscillator can be used for model testing (highly recomended, see test_oscillator.py) + * It can of cause be used stand-alone + + We use the scipy.integrate.solve_ivp algorithm for the integration (see do_step) + + Args: + k (tuple)=(1.0, 1.0, 1.0): spring constant in N/m. May vary in 3D + c (tuple)=(0.0, 0.0, 0.0): Viscous damping coefficient in N.s/m. May vary in 3D + m (float | tuple)=1.0: Mass of the spring load (spring mass negligible) in kg. Same in all dim. if float + tolerance (float)=1e-5: Optional tolerance in m, i.e. maximum uncertainty in displacement x. + """ + + def __init__( + self, + k: tuple[float, ...] | tuple[str, ...] = (1.0, 1.0, 1.0), # type str for FMU option + c: tuple[float, ...] | tuple[str, ...] = (0.0, 0.0, 0.0), + m: float | tuple[float, ...] = 1.0, + tolerance: float = 1e-5, + f_func: Callable | None = None, + ): + self.dim = len(k) + self.k = np.array(k, float) + self.c = np.array(c, float) + if isinstance(m, float): + self.m = np.array((m,) * self.dim, float) + else: + self.m = np.array(m, float) + self.tolerance = tolerance + self.x = np.array((0,) * self.dim, float) + self.v = np.array((0,) * self.dim, float) + self.f = np.array((0,) * self.dim, float) + # standard ODE matrix (homogeneous system): + self.ode = [ + np.array(((-self.c[i] / self.m[i], -self.k[i] / self.m[i]), (1, 0)), float) for i in range(self.dim) + ] + self.f_func = f_func + + def ode_func( + self, + t: float, # scalar time + y: np.ndarray, # combined array of position and speed for component i + i: int, # dimension + f: float, + ) -> np.ndarray: # force for component i + res = self.ode[i].dot(y) + if self.f_func is None: + if f != 0: + res += np.array((f, 0), float) + else: + res += np.array((self.f_func(t)[i], 0), float) + return res + + def do_step(self, current_time: float, step_size: int | float) -> bool: + """Do one simulation step of size dt. + + We implement a very simplistic algoritm based on difference calculus. + """ + for i in range(self.dim): # this is a xD oscillator + if self.x[i] != 0 or self.v[i] != 0 or self.f[i] != 0 or self.f_func is not None: + y0 = np.array([self.v[i], self.x[i]], float) + sol = integrate.solve_ivp( + fun=self.ode_func, + t_span=[current_time, current_time + step_size], + y0=y0, + args=(i, self.f[i]), # axis and force as extra arguments to fun + atol=self.tolerance, + ) + self.x[i] = sol.y[1][-1] + self.v[i] = sol.y[0][-1] + return True # to keep the signature when moving to FMU + + @property + def period(self): + """Calculate the natural period of the oscillator (without damping).""" + w2 = [] + for i in range(self.dim): + w2i = self.k[i] / self.m[i] - (self.c[i] / 2 / self.m[i]) ** 2 + if w2i > 0: + w2.append(2 * np.pi / np.sqrt(w2i)) + else: + w2.append(float("nan")) # critically or over-damped. There is no period + return w2 + + +class Force: + """A driving force in 3 dimensions which produces an ouput per time and can be connected to the oscillator. + + Args: + func (callable)=lambda t:np.array( (0,0,0), float): A function of t, producing a 3D vector + """ + + def __init__(self, func: Callable): + self.func = func + self.out = np.array((0, 0, 0), float) + + def do_step(self, current_time: float, step_size: float): + self.out = self.func(current_time) diff --git a/examples/oscillator_xd.py b/examples/oscillator_xd.py new file mode 100644 index 0000000..5a1d2f6 --- /dev/null +++ b/examples/oscillator_xd.py @@ -0,0 +1,145 @@ +from typing import Any, Callable + +import numpy as np +from scipy import integrate + + +class OscillatorXD: + """Construct a simple model of a general harmonic oscillator, potentially driven by a force. + + The system obeys the equation F(t) - k*x - c*dx/dt = m*d^2x/dt^2 + See also `Wikipedia `_ + + where x shall be an xD vector with an initial position and velocity. Common dimentsions are + + * 1: a one dimensional oscillator (e.g. a pendulum) + * 3: a three-dimensional oscillator in 3D space + * 6: a six-dimensional oscillator, represented e.g. by a rigid body mounted on springs. + The generalized position is in this case the 3D cartesian position + 3D angular position + + F(t)=0 as long as there is not external driving force. + NOTE: This is a basic oscillator model. + + * Unrelated with Model and Variable + * FMU cannot be built from that! See oscillator_fmu where this is extended to an FMU package. + * The HarmonicOscillator can be used for model testing (highly recomended, see test_oscillator_xv.py) + * It can of cause be used stand-alone + + We use the scipy.integrate.solve_ivp algorithm for the integration (see do_step) + + Args: + dim (int)=6: dimension of the oscillator + k (float|tuple)=1.0: spring constant in N/m (if x position). May vary in the dimensions if tuple + c (float|tuple)=0.0: Viscous damping coefficient in N.s/m (if x position). May vary in the dimensions if tuple + m (float | tuple)=1.0: Mass of the spring load (spring mass negligible) in kg if x position. + Same in all dim if float. + tolerance (float)=1e-5: Optional tolerance in m, i.e. maximum uncertainty in displacement x. + force (obj): Force object + """ + + def __init__( + self, + dim: int = 6, + k: float | tuple[float, ...] | tuple[str, ...] = 1.0, + c: float | tuple[float, ...] | tuple[str, ...] = 0.0, + m: float | tuple[float, ...] = 1.0, + tolerance: float = 1e-5, + force: Any | None = None, + ): + self.dim = dim + self._m = np.array((m,) * self.dim, float) if isinstance(m, float) else np.array(m, float) + assert len(self._m) == self.dim, f"Expect dimension {self.dim} for mass. Found: {self._m}" + + if isinstance(k, float): + self.w2 = np.array((k,) * self.dim, float) / self._m + elif isinstance(k, tuple): + assert len(k) == self.dim, f"Expect dimension {self.dim} for k. Found: {k}" + self.w2 = np.array(k, float) / self._m + else: + raise ValueError(f"Unhandled type for 'k': {k}") + + if isinstance(c, float): + self.gam = np.array((c / 2.0,) * self.dim, float) / self._m + elif isinstance(c, tuple): + assert len(c) == self.dim, f"Expect dimension {self.dim} for k. Found: {k}" + self.gam = np.array(c, float) / self._m / 2.0 + else: + raise ValueError(f"Unhandled type of 'c': {c}") from None + + self.tolerance = tolerance + self.x = np.array((0,) * 2 * self.dim, float) # generalized position + generalized first derivatives + self.v = self.x[self.dim :] # link to generalized derivatives (for convenience) + self.force = force # the function object + + def ode_func( + self, + t: float, # scalar time + y: np.ndarray, # combined array of positions and speeds + ) -> np.ndarray: # derivative of positions and speeds + if self.force is None: + return np.append(y[self.dim :], -2.0 * self.gam * y[self.dim :] - self.w2 * y[: self.dim]) + else: # explicit force function is defined + f = self.force(t=t, x=y[: self.dim], v=y[self.dim :]) + d_dt = -2.0 * self.gam * y[self.dim :] - self.w2 * y[: self.dim] + f + # print(f"ODE({self.dim})@{t}. f:{f[2]}, z:{y[2]}, v:{y[8]} => d_dt:{d_dt[2]}.") + return np.append(y[self.dim :], d_dt) + + def do_step(self, current_time: float, step_size: int | float) -> bool: + """Do one simulation step of size dt. + + We implement a very simplistic algoritm based on difference calculus. + """ + sol = integrate.solve_ivp( + fun=self.ode_func, + t_span=[current_time, current_time + step_size], + y0=self.x, # use the combined position-velocity array + atol=self.tolerance, + ) + self.x = sol.y[:, -1] + self.v = self.x[self.dim :] # re-link + return True # to keep the signature when moving to FMU + + @property + def period(self): + """Calculate the natural period of the oscillator (without damping).""" + w2 = [] + for i in range(self.dim): + w2i = self.w2[i] - self.gam[i] ** 2 + if w2i > 0: + w2.append(2 * np.pi / np.sqrt(w2i)) + else: + w2.append(float("nan")) # critically or over-damped. There is no period + return w2 + + +class Force: + """A driving force in x dimensions which produces an ouput as function of time and/or position. + Can be connected to the oscillator. + + Args: + dim (int): the dimension of the force (6: 3*linear force + 3*torque + func (callable): Function of agreed arguments (see args) + _args (str): Sequence of 't' (time), 'x' (position), 'v' (velocity), + denoting the force dependencies + """ + + def __init__(self, dim: int, func: Callable): + self.dim = dim + self.func = func + self.current_time = 0.0 + self.dt = 0 + self.out = np.array((0,) * self.dim) + + def __call__(self, **kwargs): + """Calculate the force in dependence on keyword arguments 't', 'x' or 'v'.""" + if "t" in kwargs: + t = kwargs["t"] + if abs(t - self.current_time) < 1e-15: + return self.out + self.dt = t - self.current_time + self.current_time = t + kwargs.update({"dt": self.dt}) # make the step size known to the function + else: + kwargs.update({"t": None, "dt": None}) + self.out = self.func(**kwargs) + return self.out diff --git a/pyproject.toml b/pyproject.toml index f3b969f..7704d5c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ name = "component-model" version = "0.1.0" description = "Constructs a Functional Mockup Interface component model from a python script (fulfilling some requirements)." readme = "README.rst" -requires-python = ">= 3.10" +requires-python = ">= 3.11" license = { file = "LICENSE" } authors = [ { name = "Siegfried Eisinger", email = "Siegfried.Eisinger@dnv.com" }, @@ -58,22 +58,20 @@ classifiers = [ ] dependencies = [ "numpy>=1.26,<2.0", - "matplotlib>=3.9.1", "pint>=0.24", - "sympy>=1.13.3", "jsonpath-ng>=1.7.0", "pythonfmu>=0.6.7", "flexparser<0.4", - "scipy>=1.15.1", - "plotly>=6.0.1", - "libcosimpy@git+https://github.com/open-simulation-platform/libcosimpy@cosimc-loading-fix", ] [project.optional-dependencies] - modelTest = [ + tests = [ "fmpy==0.3.21", # version 0.3.22 does so far (25.3.25) not workwhen installing through pip + "scipy>=1.15.1", "matplotlib>=3.9.1", "plotly>=6.0.1", + "libcosimpy>=0.0.5", +# "libcosimpy @ file:///C:/Users/eis/Downloads/libcosimpy-0.0.4-cp312-cp312-win_amd64.whl", ] rest = ["docutils>=0.21"] editor = [] @@ -101,7 +99,12 @@ dev-dependencies = [ "sphinxcontrib-mermaid>=1.0.0", "myst-parser>=4.0", "furo>=2024.8", -] + ] +required-environments = [ + "sys_platform == 'win32'", + "sys_platform == 'linux'", +# "sys_platform == 'darwin'", + ] native-tls = true diff --git a/src/component_model/model.py b/src/component_model/model.py index ffe0b31..fd171c3 100644 --- a/src/component_model/model.py +++ b/src/component_model/model.py @@ -105,7 +105,7 @@ def __init__( copyright: str | None = None, default_experiment: dict[str, float] | None = None, flags: dict | None = None, - guid : str = None, + guid: str | None = None, **kwargs, ): kwargs.update( @@ -160,7 +160,7 @@ def enter_initialization_mode(self): def exit_initialization_mode(self): """Initialize the model after initial variables are set.""" - #super().exit_initialization_mode() + # super().exit_initialization_mode() self.dirty_do() # run on_set on all dirty variables @abstractmethod # mark the class as 'still abstract' @@ -200,10 +200,12 @@ def owner_hierarchy(self, parent: str | None) -> list: elif len(parsed.indices) == 1: idx = parsed.indices[0] else: + logger.critical("Object indices other than 0 and 1D not implement. Found {parsed.indices}") raise NotImplementedError( "Object indices other than 0 and 1D not implement. Found {parsed.indices}" ) from None if parsed.der > 0: + logger.critical("Derivatives are so far not implemented") raise NotImplementedError("Derivatives are so far not implemented") from None ownernames.append((parsed.var, idx)) parent = parsed.parent @@ -237,6 +239,7 @@ def register_variable( # type: ignore [reportIncompatibleMethodOverride] # not assert isinstance(var, Variable), f"Variable object expected here. Found {var}" for idx, v in self.vars.items(): if v is not None and v.name == var.name: + logger.critical(f"Variable {var.name} already used as index {idx} in model {self.name}") raise KeyError(f"Variable {var.name} already used as index {idx} in model {self.name}") from None # ensure that the model has the value as attribute: vref = len(self.vars) @@ -717,6 +720,7 @@ def vars_iter(self, key=None): yield v else: + logger.critical(f"Unknown iteration key {key} in 'vars_iter'") raise KeyError(f"Unknown iteration key {key} in 'vars_iter'") def ref_to_var(self, vr: int) -> tuple[Variable, int]: @@ -744,6 +748,7 @@ def _vrs_slices(self, vrs: Sequence[int]) -> Generator[tuple[Variable, slice, sl try: test = self.vars[vr] except KeyError as err: + logger.critical(f"valueReference={vr} does not exist in model {self.name}") raise AssertionError(f"valueReference={vr} does not exist in model {self.name}") from err if vr != _vr + 1 or test is not None: # new slice if var is not None: # only if initialized @@ -789,7 +794,6 @@ def _set(self, vrs: Sequence[int], values: Sequence[int | float | bool | str], t Variable range check, unit check and type check are performed by setter() function. on_set (if defined) is only run if the whole variable (all elements) are set. """ - print(f"Set {vrs} to {values}. Type {typ.__qualname__}") for var, sv, svr in self._vrs_slices(vrs): assert isinstance(var, Variable) assert isinstance(var.typ, type) @@ -803,6 +807,7 @@ def _set(self, vrs: Sequence[int], values: Sequence[int | float | bool | str], t var.setter((values[_svr],), idx=_sv) else: # simple Variable var.setter(values[svr], idx=0) + # print(f"{self.name}. Set {vrs}:{values}") def set_integer(self, vrs: Sequence[int], values: Sequence[int]): self._set(vrs, values, int) diff --git a/src/component_model/utils/analysis.py b/src/component_model/utils/analysis.py new file mode 100644 index 0000000..ef0c71f --- /dev/null +++ b/src/component_model/utils/analysis.py @@ -0,0 +1,56 @@ +import logging + +import numpy as np + +logger = logging.getLogger(__name__) + + +def extremum(x: tuple | list | np.ndarray, y: tuple | list | np.ndarray, aerr: float = 0.0): + """Check whether the provided (3) points contain an extremum. + Return 0 (no extremum), -1 (low point), 1 (top point) and the point, or (0,0). + """ + assert len(x) == 3 and len(y) == 3, f"Exactly three points expected. Found {x}, {y}" + a = np.array(((1, x[0], x[0] ** 2), (1, x[1], x[1] ** 2), (1, x[2], x[2] ** 2)), float) + z = np.linalg.solve(a, y) + if (abs(z[1]) < 1e-15 and abs(z[2]) < 1e-15) or abs(z[2]) < abs(z[1] * 1e-15): # very nearly linear. + return (0, (0, 0)) + else: + x0 = -z[1] / 2.0 / z[2] + if x[0] - aerr <= x0 <= x[2] + aerr: # extremum in the range + z0 = z[0] + (z[1] + z[2] * x0) * x0 + if z[2] < 0: + return (1, (x0, z0)) + else: + return (-1, (x0, z0)) + else: + return (0, (0, 0)) + + +def extremum_series(t: tuple | list | np.ndarray, y: tuple | list | np.ndarray, which: str = "max"): + """Estimate the extrema from the time series defined by y(t). + which can be 'max', 'min' or 'all'. + """ + + def w1(x: float): + return x + + def w_1(x: float): + return -x + + def w0(x: float): + return abs(x) + + assert len(t) == len(y) > 2, "Something wrong with lengths of t ({len(t)}) and y ([len(y)})" + if which == "max": + w = w1 + elif which == "min": + w = w_1 + else: + w = w0 + + res = [] + for i in range(1, len(t) - 1): + e, p = extremum(t[i - 1 : i + 2], y[i - 1 : i + 2]) + if e != 0 and w(e) == 1 and p[0] < t[i]: + res.append(p) + return res diff --git a/src/component_model/utils/transform.py b/src/component_model/utils/transform.py new file mode 100644 index 0000000..ddb292e --- /dev/null +++ b/src/component_model/utils/transform.py @@ -0,0 +1,217 @@ +import logging +from math import isinf + +import numpy as np +from scipy.spatial.transform import Rotation as Rot + +logger = logging.getLogger(__name__) + + +# Utility functions for handling special variable types +def spherical_to_cartesian(vec: np.ndarray | tuple, deg: bool = False) -> np.ndarray: + """Turn spherical vector 'vec' (defined according to ISO 80000-2 (r,polar,azimuth)) into cartesian coordinates.""" + if deg: + theta = np.radians(vec[1]) + phi = np.radians(vec[2]) + else: + theta = vec[1] + phi = vec[2] + sinTheta = np.sin(theta) + cosTheta = np.cos(theta) + sinPhi = np.sin(phi) + cosPhi = np.cos(phi) + r = vec[0] + return np.array((r * sinTheta * cosPhi, r * sinTheta * sinPhi, r * cosTheta)) + + +def cartesian_to_spherical(vec: np.ndarray | tuple, deg: bool = False) -> np.ndarray: + """Turn the vector 'vec' given in cartesian coordinates into spherical coordinates. + (defined according to ISO 80000-2, (r, polar, azimuth)). + """ + r = np.linalg.norm(vec) + if vec[0] == vec[1] == 0: + if vec[2] == 0: + return np.array((0, 0, 0), float) + else: + return np.array((r, 0, 0), float) + elif deg: + return np.array((r, np.degrees(np.arccos(vec[2] / r)), np.degrees(np.arctan2(vec[1], vec[0]))), float) + else: + return np.array((r, np.arccos(vec[2] / r), np.arctan2(vec[1], vec[0])), float) + + +def cartesian_to_cylindrical(vec: np.ndarray | tuple, deg: bool = False) -> np.ndarray: + """Turn the vector 'vec' given in cartesian coordinates into cylindrical coordinates. + (defined according to ISO, (r, phi, z), with phi right-handed wrt. x-axis). + """ + phi = np.arctan2(vec[1], vec[0]) + if deg: + phi = np.degrees(phi) + return np.array((np.sqrt(vec[0] * vec[0] + vec[1] * vec[1]), phi, vec[2]), float) + + +def cylindrical_to_cartesian(vec: np.ndarray | tuple, deg: bool = False) -> np.ndarray: + """Turn cylinder coordinate vector 'vec' (defined according to ISO (r,phi,z)) into cartesian coordinates. + The angle phi is measured with respect to x-axis, right hand. + """ + phi = np.radians(vec[1]) if deg else vec[1] + return np.array((vec[0] * np.cos(phi), vec[0] * np.sin(phi), vec[2]), float) + + +def euler_rot_spherical( + rpy: tuple | list | Rot, + vec: tuple | list | None = None, + seq: str = "XYZ", # sequence of axis of rotation as defined in scipy Rotation object + degrees: bool = False, +) -> tuple | list | np.ndarray: + """Rotate the spherical vector vec using the Euler angles (yaw,pitch,roll). + + Args: + rpy (Sequence|Rotation): The sequence of (yaw,pitch,roll) Euler angles or the pre-calculated Rotation object + vec: (Sequence): the spherical vector to be rotated + None: Use unit vector in z-direction, i.e. (1,0,0) + 2-sequence: only polar and azimuth provided and returned => (polar,azimuth) + 3-sequence: (r,polar,azimuth) + seq (str) = 'XYZ': Sequence of rotations as defined in scipy.spatial.transform.Rotation.from_euler() + degrees (bool): angles optionally provided in degrees. Default: radians + Returns: + The rotated vector in spherical coordinates (radius only if 3-vector is provided) + """ + if isinstance(rpy, Rot): + r = rpy + elif isinstance(rpy, (tuple, list, np.ndarray)): + r = Rot.from_euler(seq, (rpy[0], rpy[1], rpy[2]), degrees) # 0: roll, 1: pitch, 2: yaw + else: + logger.critical(f"Unknown object {rpy} to rotate") + raise NotImplementedError(f"Unknown object {rpy} to rotate") from None + radius = 0.0 # avoid spurious unbound error + if vec is None: + tp = [0.0, 0.0] + else: # explicit vector provided + if len(vec) == 3: + radius = vec[0] + tp = list(vec[1:]) + else: + tp = list(vec) + if degrees: + tp = [np.radians(x) for x in tp] + st = np.sin(tp[0]) + # rotate the cartesian vector (r is definitely not a list, even if pyright might think so) + x = r.apply((st * np.cos(tp[1]), st * np.sin(tp[1]), np.cos(tp[0]))) # type: ignore[reportAttributeAccessIssue] + x2 = x[2] + if abs(x2) < 1.0: + pass + elif abs(x2 - 1.0) < 1e-10: + x2 = 1.0 + elif abs(x2 + 1.0) < 1e-10: + x2 = -1.0 + else: + logger.critical(f"Invalid argument {x2} for arccos calculation") + raise ValueError(f"Invalid argument {x2} for arccos calculation") from None + if abs(x[0]) < 1e-10 and abs(x[1]) < 1e-10: # define the normally undefined arctan + phi = 0.0 + else: + phi = np.arctan2(x[1], x[0]) + if vec is not None and len(vec) == 3: + return (radius, np.arccos(x2), phi) # return the spherical vector + else: + return (np.arccos(x2), phi) # return only the direction + + +def rot_from_spherical(vec: tuple | list | np.ndarray, degrees: bool = False): + """Return a scipy Rotation object from the spherical coordinates vec, + i.e. the rotation which turns a vector along the z-axis into vec. + + Args: + vec (Sequence | np.ndarray): a spherical vector as 3D or 2D (radius omitted) + degrees (bool): optional possibility to provide angles in degrees + """ + angle = vec[1:] if len(vec) == 3 else vec + return Rot.from_rotvec((0.0, 0.0, angle[1]), degrees) * Rot.from_rotvec((0.0, angle[0], 0.0), degrees) + + +def rot_from_vectors(vec1: np.ndarray, vec2: np.ndarray): + """Find the rotation object which rotates vec1 into vec2. Lengths of vec1 and vec2 shall be equal.""" + n = np.linalg.norm(vec1) + assert abs(n - np.linalg.norm(vec2)) < 1e-10, f"Vectors len({vec1}={n} != len{vec2}. Cannot rotate into each other" + if abs(n - 1.0) > 1e-10: + vec1 /= n + vec2 /= n + _c = vec1.dot(vec2) # type: ignore + if abs(_c + 1.0) < 1e-10: # vectors are exactly opposite to each other + imax, vmax, _sum = (-1, float("-inf"), 0.0) + for k, v in enumerate(vec1): + if isinf(vmax) or abs(v) > abs(vmax): + imax, vmax = (k, v) + _sum += v + vec = np.zeros(3) + vec[imax] = -(_sum - vmax) / vmax + vec[imax + 1 if imax < 2 else 0] = 0.0 + i_remain = imax + 2 if imax < 1 else 1 + vec[i_remain] = np.sqrt(1.0 / (1 + (vec1[i_remain] / vmax) ** 2)) + return Rot.from_rotvec(np.pi * vec) + else: + x = np.cross(vec1, vec2) + vx = np.array([[0, -x[2], x[1]], [x[2], 0, -x[0]], [-x[1], x[0], 0]], float) + # print(vec1, vec2, _c, x,'\n',vx,'\n',np.matmul(vx,vx)) + return Rot.from_matrix(np.identity(3) + vx + np.matmul(vx, vx) / (1 + _c)) + + +def spherical_unique(vec: np.ndarray, eps: float = 1e-10) -> np.ndarray: + if len(vec) == 3: + if abs(vec[0]) < eps: + return np.array((0, 0, 0), float) + elif vec[0] < 0: + return np.append(-vec[0], spherical_unique(np.array((vec[1] + np.pi, vec[2]), float))) + else: + return np.append(vec[0], spherical_unique(vec[1:], eps)) + else: + if abs(vec[0]) < eps: + return np.array((0, 0), float) + elif 0 <= vec[0] <= np.pi and 0 <= vec[1] <= 2 * np.pi: + return vec + # angles not in unique range + theta = vec[0] + phi = vec[1] + _2pi = np.pi + np.pi + while theta > np.pi: + theta -= _2pi + while theta < -np.pi: + theta += _2pi + if theta < 0: + theta = -theta + phi += np.pi + while phi < 0: + phi += _2pi + while phi >= _2pi: + phi -= _2pi + return np.array((theta, phi), float) + + +def quantity_direction(quantity_direction: tuple, spherical: bool = False, deg: bool = False) -> np.ndarray: + """Turn a 4-tuple, consisting of quantity (float) and a direction 3-vector to a direction 3-vector, + where the norm denotes the direction and the length denotes the quantity. + The return vector is always a cartesian vector. + + Args: + quantity_direction (tuple): a 4-tuple consisting of the desired length of the resulting vector (in standard units (m or m/s)) + and the direction 3-vector (in standard units) + spherical (bool)=False: Optional possibility to provide the input direction vector in spherical coordinates + deg (bool)=False: Optional possibility to provide the input angle (of spherical coordinates) in degrees. Only relevant if spherical=True + """ + if quantity_direction[0] < 1e-15: + return np.array((0, 0, 0), float) + if spherical: + direction = spherical_to_cartesian(quantity_direction[1:], deg) # turn to cartesian coordinates, if required + else: + direction = np.array(quantity_direction[1:], float) + n = np.linalg.norm(direction) # normalize + return quantity_direction[0] / n * direction + + +def normalized(vec: np.ndarray): + """Return the normalized vector. Helper function.""" + assert len(vec) == 3, f"{vec} should be a 3-dim vector" + norm = np.linalg.norm(vec) + assert norm > 0, f"Zero norm detected for vector {vec}" + return vec / norm diff --git a/src/component_model/variable.py b/src/component_model/variable.py index 8fe3ebc..ef32cf1 100644 --- a/src/component_model/variable.py +++ b/src/component_model/variable.py @@ -4,11 +4,9 @@ import xml.etree.ElementTree as ET # noqa: N817 from enum import Enum, IntFlag from functools import partial -from math import acos, atan2, cos, degrees, radians, sin, sqrt, isinf from typing import Any, Callable, Sequence, TypeAlias import numpy as np -from scipy.spatial.transform import Rotation as Rot from pint import Quantity, UnitRegistry # management of units from pythonfmu.enums import Fmi2Causality as Causality # type: ignore from pythonfmu.enums import Fmi2Initial as Initial # type: ignore @@ -95,6 +93,7 @@ def parse_quantity(self, quantity: PyType, ureg: UnitRegistry, typ: type | None # transform to base units ('SI' units). All internal calculations will be performed with these val = self.val_unit_display(q, ureg) else: + logger.critical(f"Unknown quantity {quantity} to disect") raise VariableInitError(f"Unknown quantity {quantity} to disect") from None # no recognized units. Assume a free string. ??Maybe we should be more selective about the exact error type: except Exception as warn: @@ -110,6 +109,7 @@ def parse_quantity(self, quantity: PyType, ureg: UnitRegistry, typ: type | None try: # try to convert the magnitude to the correct type. val = typ(val) except Exception as err: + logger.critical(f"Value {val} is not of the correct type {typ}") raise VariableInitError(f"Value {val} is not of the correct type {typ}") from err return val @@ -244,7 +244,7 @@ class Variable(ScalarVariable): #. The Variable value is per default owned by the related model (see `self.model`). Through the `owner` parameter this can be changes. In structured models like the crane_fmu this might be adequate. - #. The current value of the variable directly accessible through the owner. + #. The current value of the variable is directly accessible through the owner. Direct value access assumes always internal units (per default: SI units) and range checking is not performed. #. Other access to the value is achieved through the `self.getter()` and the `self.setter( v)` functions, which are also used by the FMU getxxx and setxxx functions (external access). @@ -278,12 +278,15 @@ class Variable(ScalarVariable): If None, _typ is set to Enum/str if derived from these after disection or float if a number. 'int' is not automatically detected. start (PyType): The initial value of the variable. - Optionally, the unit can be included, providing the initial value as string, evaluating to quantity of type typ a display unit and base unit. - Note that the quantities are always converted to standard units of the same type, while the display unit may be different, i.e. the preferred user communication. - rng (tuple) = (): Optional range of the variable in terms of a tuple of the same type as initial value. Should be specified with units (as string). + Optionally, the unit can be included, providing the initial value as string, + evaluating to quantity of type typ a display unit and base unit. + Note that the quantities are always converted to standard units of the same type, while the display unit may be different, + i.e. the preferred user communication. + rng (tuple) = (): Optional range of the variable in terms of a tuple of the same type as initial value. + Should be specified with units (as string). * If an empty tuple is specified, the range is automatically determined. - That is only possible float or enum type variables, where the former evaluates to (-inf, inf). + That is only possible for float or enum type variables, where the former evaluates to (-inf, inf). Maximum or minimum int values do not exist in Python, such that these always must be provided explicitly. It is not possible to set only one of the elements of the tuple automatically. * If None is specified, the initial value is chosen, i.e. no range. @@ -404,8 +407,10 @@ def __init__( self.range = self._init_range(rng) if not self.check_range(self._start, disp=False): # range checks of initial value + logger.critical(f"The provided value {self._start} is not in the valid range {self._range}") raise VariableInitError(f"The provided value {self._start} is not in the valid range {self._range}") self.model.register_variable(self) + assert len(self._start) > 0, "Empty tuples are not handled here:" try: setattr(self.owner, self.local_name, np.array(self._start, self.typ) if self._len > 1 else self._start[0]) except AttributeError as _: # can happen if a @property is defined for local_name, but no @local_name.setter @@ -417,9 +422,11 @@ def der1(self, current_time: float, step_size: float): if (isinstance(der, float) and der != 0.0) or ( isinstance(der, (Sequence, np.ndarray)) and any(x != 0.0 for x in der) ): # there is a slope - varname = self.local_name[5:] # local name of the base variable + # varname = self.local_name[5:] # local name of the base variable basevar = self.model.derivatives[self.name] # base variable object - val = getattr(self.owner, basevar.local_name) #getattr(self.owner, varname) # previous value of base variable # + val = getattr( + self.owner, basevar.local_name + ) # getattr(self.owner, varname) # previous value of base variable # if not isinstance(der, (Sequence, np.ndarray)): der = [der] assert not isinstance(val, (Sequence, np.ndarray)), "Should be the same as der" @@ -433,6 +440,7 @@ def der1(self, current_time: float, step_size: float): # disable super() functions and properties which are not in use here def to_xml(self) -> ET.Element: + logger.critical("The function to_xml() shall not be used from component-model") raise NotImplementedError("The function to_xml() shall not be used from component-model") from None # External access to read-only variables: @@ -505,11 +513,11 @@ def setter(self, values: Sequence[int | float | bool | str | Enum] | np.ndarray, for i in range(self._len): values[i] = self._typ(values[i]) # type: ignore - if True:#??self._check & Check.ranges: # do that before unit conversion, since range is stored in display units! + if self._check & Check.ranges: # do that before unit conversion, since range is stored in display units! if not self.check_range(values, idx): - raise VariableRangeError(f"set(): values {values} outside range.") from None + logger.error(f"set(): values {values} outside range.") - if True: #?? self._check & Check.units: #'values' expected as displayUnit. Convert to unit + if self._check & Check.units: #'values' expected as displayUnit. Convert to unit if idx >= 0: # explicit index of single values if self._unit[idx].du is None: dvals = list(values) @@ -560,24 +568,25 @@ def setter_internal( setattr(self.owner, self.local_name, values if self.on_set is None else self.on_set(values)) if self.on_set is None: logger.debug(f"SETTER {self.name}, {values}[{idx}] => {getattr(self.owner, self.local_name)}") - logger.info(f"Set variable {self.name}->{values}") def getter(self) -> list[PyType]: """Get the value (output a value from the model), including range checking and unit conversion. - The whole variable value is returned. - The return a list of values. Can later be indexed/sliced to get elements of compound variables. + For compound variables, the whole variable is returned as list (even for scalar variables). + Returned value lists can later be indexed/sliced to get elements of (compound) variables. """ assert self._typ is not None, "Need a proper type at this stage" if self._len == 1: - value = getattr(self.owner, self.local_name) + value = getattr(self.owner, self.local_name) # work with the single value if issubclass(self._typ, Enum): # native Enums do not exist in FMI2. Convert to int - value = value.value - elif not isinstance(value, self._typ): # other type conversion - value = self._typ(value) # type: ignore[call-arg] - if self._check & Check.units: # Convert 'value' display.u -> base unit - if self._unit[0].du is not None: - value = self._unit[0].from_base(value) - values = [value] + values = [value.value] + else: + if not isinstance(value, self._typ): # other type conversion + value = self._typ(value) # type: ignore[call-arg] + if self._check & Check.units: # Convert 'value' base unit -> display.u + if self._unit[0].du is not None: + assert isinstance(value, float) + value = self._unit[0].from_base(value) + values = [value] else: # compound variable values = list(getattr(self.owner, self.local_name)) # make value available as copy @@ -588,13 +597,13 @@ def getter(self) -> list[PyType]: for i in range(self._len): # check whether conversion to _typ is necessary if not isinstance(values[i], self._typ): values[i] = self._typ(values[i]) # type: ignore[call-arg] - if self._check & Check.units: # Convert 'value' display.u -> base unit + if self._check & Check.units: # Convert 'value' base unit -> display.u for i in range(self._len): if self._unit[i].du is not None: values[i] = self._unit[i].from_base(values[i]) - if self._check & Check.ranges and not self.check_range(values, -1): - raise VariableRangeError(f"getter(): Value {values} outside range.") from None + if self._check & Check.ranges and not self.check_range(values, -1): # check the range if so instructed + logger.error(f"getter(): Value of {self.name}: {values} outside range {self.range}!") return values def _init_range(self, rng: tuple | None) -> tuple: @@ -606,17 +615,6 @@ def _init_range(self, rng: tuple | None) -> tuple: Always for the whole variable with scalar variables packed in a singleton """ - def ensure_display_limits(val: float, idx: int, right: bool): - """Ensure that value is provided as display unit and that limits are included in range.""" - if self._unit[idx].du is not None: # Range in display units! - val = self._unit[idx].from_base(val) - if isinstance(val, float) and abs(val) != float("inf") and int(val) != val: - if right: - val += 1e-15 - else: - val -= 1e-15 - return val - assert hasattr(self, "_start") and hasattr(self, "_unit"), "Missing self._start / self._unit" assert isinstance(self._typ, type), "init_range(): Need a defined _typ at this stage" # Configure input. Could be None, () or (min,max) of scalar @@ -627,13 +625,10 @@ def ensure_display_limits(val: float, idx: int, right: bool): for idx in range(self._len): # go through all elements _rng = rng[idx] if _rng is None: # => no range. Used for compound variables if not all elements have a range - assert isinstance(self._start[idx], float) - _range.append( - ( - ensure_display_limits(self._start[idx], idx, right=False), # type: ignore ## it is a float! - ensure_display_limits(self._start[idx], idx, right=True), # type: ignore ## it is a float! - ) - ) # no range + s0 = self._start[idx] + assert isinstance(s0, float) + v = self._unit[idx].from_base(s0) if self._unit[idx].du is not None else s0 + _range.append((v, v)) elif isinstance(_rng, tuple) and not len(_rng): # empty tuple => try automatic range _range.append(self._auto_extreme(self._start[idx])) elif isinstance(_rng, tuple) and len(_rng) == 2: # normal range as 2-tuple @@ -648,20 +643,23 @@ def ensure_display_limits(val: float, idx: int, right: bool): if check: logger.warn(f"{self.name}[{idx}] range {r}: Use display units {self._unit[idx].du}!") else: - raise VariableInitError( - f"{self.name}[{idx}]: range {r} does not conform to the unit type {self._unit[idx]}" - ) + msg = f"{self.name}[{idx}]: range {r} not conformant to the unit type {self._unit[idx]}" + logger.critical(msg) + raise VariableInitError(msg) assert isinstance(q, float) or (self._typ is int and isinstance(q, int)) - q = ensure_display_limits(q, idx, len(i_range) > 0) + if self._unit[idx].du is not None: + q = self._unit[idx].from_base(q) i_range.append(q) try: # check variable type i_range = [self._typ(x) for x in i_range] except Exception as err: + logger.critical(f"Incompatible types range {rng} - {self._start}") raise VariableRangeError(f"Incompatible types range {rng} - {self._start}") from err assert all(isinstance(x, self._typ) for x in i_range) _range.append(tuple(i_range)) # type: ignore else: + logger.critical(f"init_range(): Unhandled range argument {rng}") raise AssertionError(f"init_range(): Unhandled range argument {rng}") return tuple(_range) @@ -688,9 +686,10 @@ def check_range_single(self, value: PyType | None, idx: int = 0, disp: bool = Tr elif isinstance(value, (int, float)) and all(isinstance(x, (int, float)) for x in self._range[idx]): if not disp and self._unit[idx].du is not None: # check an internal unit values value = self._unit[idx].from_base(value) - return self._range[idx] is None or self._range[idx][0] <= value <= self._range[idx][1] # type: ignore + return self._range[idx] is None or self._range[idx][0] <= value <= self._range[idx][1] else: - raise VariableUseError(f"check_range(): value={value}, type={self.typ}, range={self.range}") from None + logger.error(f"check_range(): value={value}, type={self.typ}, range={self.range}") + return False def check_range(self, values: Sequence[PyType | None] | np.ndarray, idx: int = 0, disp: bool = True) -> bool: """Check the provided 'values' with respect to the range. @@ -754,8 +753,10 @@ def auto_type(cls, val: PyType | Compound, allow_int: bool = False): elif typ is int and t is float: # we allow that, even if no subclass typ = float else: + logger.critical(f"Incompatible variable types {typ}, {t} in {val}") raise VariableInitError(f"Incompatible variable types {typ}, {t} in {val}") from None else: + logger.critical(f"auto_type(). Unhandled {t}, {typ}") raise ValueError(f"auto_type(). Unhandled {t}, {typ}") return typ else: # single value @@ -773,8 +774,10 @@ def _auto_extreme(cls, var: PyType) -> tuple: """Return the extreme values of the variable. Args: - var: the variable for which to determine the extremes. Represented by an instantiated object - Returns: + var: the variable for which to determine the extremes, represented by an instantiated object (example) + + Returns + ------- A tuple containing the minimum and maximum value the given variable can have """ if isinstance(var, bool): @@ -782,7 +785,8 @@ def _auto_extreme(cls, var: PyType) -> tuple: elif isinstance(var, float): return (float("-inf"), float("inf")) elif isinstance(var, int): - raise VariableInitError(f"Range must be specified for int variable {cls} or use float.") + logger.critical(f"Range must be specified for int variable {cls} or use float.") + return (var, var) # restrict to start value elif isinstance(var, Enum): return (min(x.value for x in type(var)), max(x.value for x in type(var))) else: @@ -865,205 +869,3 @@ def primitive(self) -> Variable | None: else: name = parsed.as_string(("parent", "var", "der"), simplified=True, primitive=True) return self.model.variable_by_name(name) - - -# Utility functions for handling special variable types -def spherical_to_cartesian(vec: np.ndarray | tuple, deg: bool = False) -> np.ndarray: - """Turn spherical vector 'vec' (defined according to ISO 80000-2 (r,polar,azimuth)) into cartesian coordinates.""" - if deg: - theta = radians(vec[1]) - phi = radians(vec[2]) - else: - theta = vec[1] - phi = vec[2] - sinTheta = sin(theta) - cosTheta = cos(theta) - sinPhi = sin(phi) - cosPhi = cos(phi) - r = vec[0] - return np.array((r * sinTheta * cosPhi, r * sinTheta * sinPhi, r * cosTheta)) - - -def cartesian_to_spherical(vec: np.ndarray | tuple, deg: bool = False) -> np.ndarray: - """Turn the vector 'vec' given in cartesian coordinates into spherical coordinates. - (defined according to ISO 80000-2, (r, polar, azimuth)). - """ - r = np.linalg.norm(vec) - if vec[0] == vec[1] == 0: - if vec[2] == 0: - return np.array((0, 0, 0), dtype="float") - else: - return np.array((r, 0, 0), dtype="float") - elif deg: - return np.array((r, degrees(acos(vec[2] / r)), degrees(atan2(vec[1], vec[0]))), dtype="float64") - else: - return np.array((r, acos(vec[2] / r), atan2(vec[1], vec[0])), dtype="float") - - -def cartesian_to_cylindrical(vec: np.ndarray | tuple, deg: bool = False) -> np.ndarray: - """Turn the vector 'vec' given in cartesian coordinates into cylindrical coordinates. - (defined according to ISO, (r, phi, z), with phi right-handed wrt. x-axis). - """ - phi = atan2(vec[1], vec[0]) - if deg: - phi = degrees(phi) - return np.array((sqrt(vec[0] * vec[0] + vec[1] * vec[1]), phi, vec[2]), dtype="float") - - -def cylindrical_to_cartesian(vec: np.ndarray | tuple, deg: bool = False) -> np.ndarray: - """Turn cylinder coordinate vector 'vec' (defined according to ISO (r,phi,z)) into cartesian coordinates. - The angle phi is measured with respect to x-axis, right hand. - """ - phi = radians(vec[1]) if deg else vec[1] - return np.array((vec[0] * cos(phi), vec[0] * sin(phi), vec[2]), dtype="float") - -def euler_rot_spherical( - rpy: Sequence|Rot, - vec: Sequence | None = None, - seq: str = 'XYZ', # sequence of axis of rotation as defined in scipy Rotation object - degrees:bool=False) -> Sequence: - """ Rotate the spherical vector vec using the Euler angles (yaw,pitch,roll). - - Args: - rpy (Sequence|Rotation): The sequence of (yaw,pitch,roll) Euler angles or the pre-calculated Rotation object - vec: (Sequence): the spherical vector to be rotated - None: Use unit vector in z-direction, i.e. (1,0,0) - 2-sequence: only polar and azimuth provided and returned => (polar,azimuth) - 3-sequence: (r,polar,azimuth) - seq (str) = 'XYZ': Sequence of rotations as defined in scipy.spatial.transform.Rotation.from_euler() - degrees (bool): angles optionally provided in degrees. Default: radians - Returns: - The rotated vector in spherical coordinates (radius only if 3-vector is provided) - """ - if isinstance(rpy, Rot): - r = rpy - elif isinstance( rpy, (Sequence,np.ndarray)): - r = Rot.from_euler(seq, (rpy[0], rpy[1], rpy[2]), degrees) #0: roll, 1: pitch, 2: yaw - else: - raise NotImplementedError(f"Unknown object {rpy} to rotate") from None - if vec is None: - tp = (0,0) - else: # explicit vector provided - if len(vec)==3: - radius = vec[0] - tp = vec[1:] - else: - tp = vec - if degrees: - tp = np.radians( tp) - st = np.sin(tp[0]) - x = r.apply( (st*np.cos(tp[1]), st*np.sin(tp[1]), np.cos(tp[0]))) # rotate the cartesian vector - x2 = x[2] - if abs(x2) < 1.0: - pass - elif abs(x2-1.0) < 1e-10: - x2 = 1.0 - elif abs(x2+1.0) < 1e-10: - x2 = -1.0 - else: - raise ValueError(f"Invalid argument {x2} for arccos calculation") from None - if abs(x[0])<1e-10 and abs(x[1])<1e-10: # define the normally undefined arctan - phi = 0.0 - else: - phi = np.arctan2( x[1], x[0]) - if vec is not None and len(vec) == 3: - return (radius, np.arccos( x2), phi) # return the spherical vector - else: - return (np.arccos( x2), phi) # return only the direction - -def rot_from_spherical( vec : Sequence | np.ndarray, degrees:bool = False): - """Return a scipy Rotation object from the spherical coordinates vec, - i.e. the rotation which turns a vector along the z-axis into vec. - - Args: - vec (Sequence | np.ndarray): a spherical vector as 3D or 2D (radius omitted) - degrees (bool): optional possibility to provide angles in degrees - """ - angle = vec[1:] if len(vec)==3 else vec - return Rot.from_rotvec( (0.0,0.0,angle[1]), degrees) * Rot.from_rotvec( (0.0,angle[0],0.0), degrees) - -def rot_from_vectors( vec1 : Sequence | np.ndarray, vec2 : Sequence | np.ndarray): - """Find the rotation object which rotates vec1 into vec2. Lengths of vec1 and vec2 shall be equal.""" - n = np.linalg.norm( vec1) - assert abs( n - np.linalg.norm(vec2)) < 1e-10, f"Vectors len({vec1}={n} != len{vec2}. Cannot rotate into each other" - if abs(n-1.0) >1e-10: - vec1 /= n - vec2 /= n - _c = vec1.dot(vec2) - if abs(_c+1.0) < 1e-10: # vectors are exactly opposite to each other - imax,vmax,_sum = (-1, float('-inf'), 0.0) - for k, v in enumerate( vec1): - if isinf(vmax) or abs(v) > abs(vmax): - imax,vmax = (k,v) - _sum += v - vec = np.zeros(3) - vec[imax] = -(_sum-vmax)/vmax - vec[imax+1 if imax<2 else 0] = 0.0 - i_remain = imax+2 if imax<1 else 1 - vec[i_remain] = np.sqrt( 1.0/ (1 + (vec1[i_remain]/vmax)**2)) - return Rot.from_rotvec( np.pi*vec) - else: - x = np.cross( vec1, vec2) - vx = np.array( [[0, -x[2], x[1]], - [x[2], 0, -x[0]], - [-x[1], x[0], 0]]) - # print(vec1, vec2, _c, x,'\n',vx,'\n',np.matmul(vx,vx)) - return Rot.from_matrix( np.identity(3) + vx + np.matmul(vx,vx)/ (1+_c)) - -def spherical_unique( vec : np.ndarray, eps : float = 1e-10) -> np.ndarray: - if len(vec) == 3: - if abs(vec[0]) < eps: - return np.array( (0,0,0), float) - elif vec[0] < 0: - return np.append( -vec[0], spherical_unique( np.array( (vec[1]+np.pi, vec[2]), float))) - else: - return np.append( vec[0], spherical_unique( vec[1:], eps)) - else: - if abs( vec[0]) < eps: - return np.array( (0,0), float) - elif 0 <= vec[0] <= np.pi and 0 <= vec[1] <= 2*np.pi: - return vec - # angles not in unique range - theta = vec[0] - phi = vec[1] - _2pi = np.pi + np.pi - while theta > np.pi: - theta -= _2pi - while theta < -np.pi: - theta += _2pi - if theta < 0: - theta = -theta - phi += np.pi - while phi < 0: - phi += _2pi - while phi >= _2pi: - phi -= _2pi - return np.array( (theta,phi), float) - - -def quantity_direction(quantity_direction: tuple, spherical: bool = False, deg: bool = False) -> np.ndarray: - """Turn a 4-tuple, consisting of quantity (float) and a direction 3-vector to a direction 3-vector, - where the norm denotes the direction and the length denotes the quantity. - The return vector is always a cartesian vector. - - Args: - quantity_direction (tuple): a 4-tuple consisting of the desired length of the resulting vector (in standard units (m or m/s)) - and the direction 3-vector (in standard units) - spherical (bool)=False: Optional possibility to provide the input direction vector in spherical coordinates - deg (bool)=False: Optional possibility to provide the input angle (of spherical coordinates) in degrees. Only relevant if spherical=True - """ - if quantity_direction[0] < 1e-15: - return np.array((0, 0, 0), dtype="float") - if spherical: - direction = spherical_to_cartesian(quantity_direction[1:], deg) # turn to cartesian coordinates, if required - else: - direction = np.array(quantity_direction[1:], dtype="float") - n = np.linalg.norm(direction) # normalize - return quantity_direction[0] / n * direction - -def normalized(vec: np.ndarray): - """Return the normalized vector. Helper function.""" - assert len(vec) == 3, f"{vec} should be a 3-dim vector" - norm = np.linalg.norm(vec) - assert norm > 0, f"Zero norm detected for vector {vec}" - return vec / norm \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index a45ad4c..a4aaad1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -77,4 +77,4 @@ def pytest_addoption(parser): @pytest.fixture(scope="session") def show(request): - return request.config.getoption("--show") == "True" + return request.config.getoption("--show") == "False" diff --git a/tests/test_oscillator.py b/tests/test_oscillator.py index d17373d..a003636 100644 --- a/tests/test_oscillator.py +++ b/tests/test_oscillator.py @@ -4,8 +4,8 @@ import matplotlib.pyplot as plt import numpy as np -from scipy.integrate import solve_ivp import pytest +from scipy.integrate import solve_ivp def arrays_equal(res: tuple[float, ...] | list[float], expected: tuple[float, ...] | list[float], eps=1e-7): @@ -24,7 +24,8 @@ def do_show( compare1: list | None = None, compare2: list | None = None, z_label: str = "z-position", - v_label: str = "z-speed"): + v_label: str = "z-speed", +): fig, ax = plt.subplots() ax.plot(time, z, label=z_label) ax.plot(time, v, label=v_label) @@ -164,7 +165,7 @@ def sweep_oscillation_z( return (osc, times, z, v, f) -def test_oscillator_class(show): +def test_oscillator_class(show: bool = False): """Test the Oscillator class in isolation. Such tests are strongly recommended before compiling the model into an FMU. @@ -205,7 +206,7 @@ def test_oscillator_class(show): print(f". Max absolute error: {emax}") -def test_2d(show): +def test_2d(show: bool = False): from examples.oscillator import Oscillator def run_2d( @@ -273,7 +274,7 @@ def area(x: list[float], y: list[float]): show_2d(x, y) -def test_sweep_oscillator(show: bool = True): +def test_sweep_oscillator(show: bool = False): """A forced oscillator where the force frequency is changed linearly as d_omega*time. The test demonstrates that a monolithic simulation provides accurate results in all ranges of the force frequency. Co-simulating the oscillator and the force, this does not work. @@ -332,40 +333,38 @@ def test_sweep_oscillator(show: bool = True): ax.legend() plt.show() -def test_ivp(show: bool = True): + +def test_ivp(show: bool = False): """Perform a few tests to get more acquainted with the IVP solver. Taken from scipy documentation""" - def upward_cannon(t, y): # return speed and accelleration as function of (position, speed) + + def upward_cannon(t, y): # return speed and accelleration as function of (position, speed) return [y[1], -9.81] + def hit_ground(t, y): return y[0] + sol = solve_ivp( - upward_cannon, # initial value function - [0, 100], # time range - [0, 200], # start values (position, speed) - t_eval=[t for t in range(100)] # evaluate at these points (not only last time value. For plotting) - ) - assert sol.status==0, "No events involved. Successful status should be 0" - assert len(sol.y)==2, "y is a double vector of (position, speed), which is also reflected in results" + upward_cannon, # initial value function + [0, 100], # time range + [0, 200], # start values (position, speed) + t_eval=[t for t in range(100)], # evaluate at these points (not only last time value. For plotting) + ) + assert sol.status == 0, "No events involved. Successful status should be 0" + assert len(sol.y) == 2, "y is a double vector of (position, speed), which is also reflected in results" if show: - do_show( sol.t, sol.y[0], sol.y[1], z_label='pos', v_label='speed') - # include hit_ground event - hit_ground.terminal = True # attributes can be added to any function! Used here to qualify event. - hit_ground.direction = -1 - sol = solve_ivp( - upward_cannon, - [0, 100], - [0, 200], - t_eval=[t for t in range(100)], - events=hit_ground) - print( sol) - assert np.allclose(sol.t_events, [2*200/9.81]), "Time when hitting the ground" - assert np.allclose( sol.y_events, [[ 0.0, -200.0]]), "Position and speed when hitting the ground" - #print( sol) + do_show(sol.t, sol.y[0], sol.y[1], z_label="pos", v_label="speed") + # include hit_ground event. Monkey patching function (which mypy, pyright do not like) + hit_ground.terminal = True # type: ignore[attr-defined] + hit_ground.direction = -1 # type: ignore[attr-defined] + sol = solve_ivp(upward_cannon, [0, 100], [0, 200], t_eval=[t for t in range(100)], events=hit_ground) + assert np.allclose(sol.t_events, [2 * 200 / 9.81]), "Time when hitting the ground" + assert np.allclose(sol.y_events, [[0.0, -200.0]]), "Position and speed when hitting the ground" if show: - do_show( sol.t, sol.y[0], sol.y[1], z_label='pos', v_label='speed') + do_show(sol.t, sol.y[0], sol.y[1], z_label="pos", v_label="speed") + if __name__ == "__main__": - retcode = 0#pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__]) + retcode = pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__]) assert retcode == 0, f"Non-zero return code {retcode}" import os diff --git a/tests/test_oscillator_6dof_fmu.py b/tests/test_oscillator_6dof_fmu.py index 221582c..c700aeb 100644 --- a/tests/test_oscillator_6dof_fmu.py +++ b/tests/test_oscillator_6dof_fmu.py @@ -102,11 +102,16 @@ def arrays_equal( assert abs(x - y) < eps, f"Element {i} not nearly equal in {x}, {y}" -def do_show(traces: dict[str, tuple[list[float], list[float]]]): +def do_show( + traces: dict[str, tuple[list[float], list[float]]], + xlabel: str = "frequency in Hz", + ylabel: str = "position/angle", + title: str = "External force frequency sweep with time between co-sim calls = 1.0", +): fig, ax = plt.subplots() - ax.set_title("External force frequency sweep with time between co-sim calls = 1.0") - ax.set_xlabel("frequency in Hz") - ax.set_ylabel("position/angle") + ax.set_title(title) + ax.set_xlabel(xlabel) + ax.set_ylabel(ylabel) for label, trace in traces.items(): _ = ax.plot(trace[0], trace[1], label=label) _ = ax.legend() @@ -198,7 +203,7 @@ def test_make_fmus( # -def test_use_fmu(oscillator_fmu: Path, driver_fmu: Path, show: bool = True): +def test_use_fmu(oscillator_fmu: Path, driver_fmu: Path, show: bool = False): """Test single FMUs.""" # sourcery skip: move-assign result = simulate_fmu( @@ -242,7 +247,7 @@ def test_run_osp(oscillator_fmu: Path, driver_fmu: Path): @pytest.mark.skipif(sys.platform.startswith("linux"), reason="HarmonicOsciallatorFMU.fmu throws an error on Linux") -def test_run_osp_system_structure(system_structure: Path, show: bool = True): +def test_run_osp_system_structure(system_structure: Path, show: bool = False): "Run an OSP simulation in the same way as the SimulatorInterface of sim-explorer is implemented" log_output_level(CosimLogLevel.TRACE) print("STRUCTURE", system_structure) @@ -271,17 +276,30 @@ def test_run_osp_system_structure(system_structure: Path, show: bool = True): times = [] pos = [] speed = [] + for i in range(6): + sim.real_initial_value(*_var_ref(sim, "osc", f"m[{i}]"), value=10000.0) + sim.real_initial_value(*_var_ref(sim, "osc", f"k[{i}]"), value=10000.0) + sim.real_initial_value(*_var_ref(sim, "osc", "v[0]"), value=1.0) + slave, x0_ref = _var_ref(sim, "osc", "x[0]") + slave, v0_ref = _var_ref(sim, "osc", "v[0]") slave, x2_ref = _var_ref(sim, "osc", "x[2]") slave, v2_ref = _var_ref(sim, "osc", "v[2]") - for step in range(1, 1000): + for step in range(1, 100): time = step * 0.01 - _ = sim.simulate_until(step * 1e8) - values = observer.last_real_values(slave_index=0, variable_references=[x2_ref, v2_ref]) + _ = sim.simulate_until(step * 1e7) + values = observer.last_real_values(slave_index=0, variable_references=[x0_ref, v0_ref, x2_ref, v2_ref]) times.append(time) pos.append(values[0]) speed.append(values[1]) if show: - do_show(traces={"z-pos": (times, pos), "z-speed": (times, speed)}) + do_show( + traces={"z-pos": (times, pos), "z-speed": (times, speed)}, + xlabel="time", + ylabel="pos/speed", + title="Oscillator excited through initial speed", + ) + for t, p, v in zip(times, pos, speed, strict=True): + print(f"@{t}, {p}, {v}, {np.sin(t)}") def test_system_structure_change(system_structure): @@ -289,11 +307,11 @@ def test_system_structure_change(system_structure): @pytest.mark.parametrize("alg", ["fixedStep", "ecco"]) -def test_run_osp_sweep(system_structure: Path, alg: str, show: bool = True): +def test_run_osp_sweep(system_structure: Path, alg: str, show: bool = False): _test_run_osp_sweep(system_structure, show, alg) -def _test_run_osp_sweep(system_structure: Path, show: bool = True, alg: str = "fixedStep"): +def _test_run_osp_sweep(system_structure: Path, show: bool = False, alg: str = "fixedStep"): "Run an OSP simulation of the oscillator and the force sweep as co-simulation." dt = 1.0 @@ -351,13 +369,13 @@ def _test_run_osp_sweep(system_structure: Path, show: bool = True, alg: str = "f if __name__ == "__main__": - retcode = pytest.main(args=["-rA", "-v", __file__, "--show", "True"]) + retcode = 0 # pytest.main(args=["-rA", "-v", __file__, "--show", "True"]) assert retcode == 0, f"Non-zero return code {retcode}" import os os.chdir(Path(__file__).parent.absolute() / "test_working_directory") # test_system_structure_change(_system_structure()) - # test_make_fmus(_oscillator_fmu(), _driver_fmu()) + test_make_fmus(_oscillator_fmu(), _driver_fmu()) # test_use_fmu(_oscillator_fmu(), _driver_fmu(), show=True) # test_run_osp(_oscillator_fmu(), _driver_fmu()) # test_run_osp_system_structure(_system_structure(), show=True) diff --git a/tests/test_oscillator_fmu.py b/tests/test_oscillator_fmu.py index c7bd15a..a3d2c3d 100644 --- a/tests/test_oscillator_fmu.py +++ b/tests/test_oscillator_fmu.py @@ -1,3 +1,4 @@ +# ruff: noqa: I001 import shutil import sys import xml.etree.ElementTree as ET # noqa: N817 @@ -5,6 +6,7 @@ from pathlib import Path from typing import Any +from libcosimpy.CosimExecution import CosimExecution import matplotlib.pyplot as plt import numpy as np import pytest @@ -17,7 +19,6 @@ CosimVariableType, CosimVariableVariability, ) -from libcosimpy.CosimExecution import CosimExecution from libcosimpy.CosimLogging import CosimLogLevel, log_output_level from libcosimpy.CosimManipulator import CosimManipulator from libcosimpy.CosimObserver import CosimObserver @@ -148,8 +149,7 @@ def oscillator_fmu(): def _oscillator_fmu(): """Make FMU and return .fmu file with path.""" - build_path = Path.cwd() - build_path.mkdir(exist_ok=True) + build_path = Path(__file__).parent.parent / "examples" src = Path(__file__).parent.parent / "examples" / "oscillator_fmu.py" fmu_path = Model.build( script=src, @@ -165,8 +165,7 @@ def driver_fmu(): def _driver_fmu(): """Make FMU and return .fmu file with path.""" - build_path = Path.cwd() - build_path.mkdir(exist_ok=True) + build_path = Path(__file__).parent.parent / "examples" src = Path(__file__).parent.parent / "examples" / "driving_force_fmu.py" fmu_path = Model.build( script=src, @@ -190,14 +189,19 @@ def _system_structure(): def test_make_fmus( oscillator_fmu: Path, driver_fmu: Path, + show: bool = False, ): info = fmu_info(filename=str(oscillator_fmu)) # this is a formatted string. Not easy to check - print(f"Info Oscillator: {info}") + if show: + print(f"Info Oscillator @{oscillator_fmu}") + print(info) val = validate_fmu(filename=str(oscillator_fmu)) assert not len(val), f"Validation of of {oscillator_fmu.name} was not successful. Errors: {val}" info = fmu_info(filename=str(driver_fmu)) # this is a formatted string. Not easy to check - print(f"Info Driver: {info}") + if show: + print(f"Info Driver: @{driver_fmu}") + print(info) val = validate_fmu(filename=str(driver_fmu)) assert not len(val), f"Validation of of {driver_fmu.name} was not successful. Errors: {val}" @@ -216,7 +220,7 @@ def test_make_fmus( # -def test_run_fmpy(oscillator_fmu: Path, driver_fmu: Path, show: bool = True): +def test_run_fmpy(oscillator_fmu: Path, driver_fmu: Path, show: bool = False): """Test single FMUs.""" # sourcery skip: move-assign result = simulate_fmu( @@ -235,6 +239,34 @@ def test_run_fmpy(oscillator_fmu: Path, driver_fmu: Path, show: bool = True): plot_result(result) +def test_run_fmpy2(oscillator_fmu: Path, driver_fmu: Path, show: bool = False): + """Test oscillator in setting similar to 'crane_on_spring'""" + # sourcery skip: move-assign + result = simulate_fmu( + oscillator_fmu, + stop_time=10, + step_size=0.01, + validate=True, + solver="Euler", + debug_logging=True, + logger=print, # fmi_call_logger=print, + start_values={ + "m": 10000.0, + "k[0]": 10000.0, + "k[1]": 10000.0, + "k[2]": 10000.0, + "x[0]": 0.0, + "x[1]": 0.0, + "x[2]": 0.0, + "v[0]": 1.0, + }, + step_finished=None, # pyright: ignore[reportArgumentType] # (typing incorrect in fmpy) + fmu_instance=None, # pyright: ignore[reportArgumentType] # (typing incorrect in fmpy) + ) + if show: + plot_result(result) + + def test_run_osp(oscillator_fmu: Path, driver_fmu: Path): # sourcery skip: extract-duplicate-method sim = CosimExecution.from_step_size(step_size=1e8) # empty execution object with fixed time step in nanos @@ -267,7 +299,7 @@ def test_run_osp(oscillator_fmu: Path, driver_fmu: Path): @pytest.mark.skipif(sys.platform.startswith("linux"), reason="HarmonicOsciallatorFMU.fmu throws an error on Linux") -def test_run_osp_system_structure(system_structure: Path, show: bool = True): +def test_run_osp_system_structure(system_structure: Path, show: bool = False): "Run an OSP simulation in the same way as the SimulatorInterface of sim-explorer is implemented" log_output_level(CosimLogLevel.TRACE) print("STRUCTURE", system_structure) @@ -336,11 +368,11 @@ def test_system_structure_change(system_structure): @pytest.mark.parametrize("alg", ["fixedStep", "ecco"]) -def test_run_osp_sweep(system_structure: Path, alg: str, show: bool = True): +def test_run_osp_sweep(system_structure: Path, alg: str, show: bool = False): _test_run_osp_sweep(system_structure, show, alg) -def _test_run_osp_sweep(system_structure: Path, show: bool = True, alg: str = "fixedStep"): +def _test_run_osp_sweep(system_structure: Path, show: bool = False, alg: str = "fixedStep"): "Run an OSP simulation of the oscillator and the force sweep as co-simulation." dt = 1.0 @@ -398,14 +430,15 @@ def _test_run_osp_sweep(system_structure: Path, show: bool = True, alg: str = "f if __name__ == "__main__": - retcode = pytest.main(args=["-rA", "-v", __file__, "--show", "True"]) + retcode = 0 # pytest.main(args=["-rA", "-v", __file__, "--show", "True"]) assert retcode == 0, f"Non-zero return code {retcode}" import os os.chdir(Path(__file__).parent.absolute() / "test_working_directory") # test_system_structure_change(_system_structure()) - # test_make_fmus(_oscillator_fmu(), _driver_fmu()) + test_make_fmus(_oscillator_fmu(), _driver_fmu(), show=True) # test_run_fmpy(_oscillator_fmu(), _driver_fmu(), show=True) + # test_run_fmpy2(_oscillator_fmu(), _driver_fmu(), show=True) # test_run_osp(_oscillator_fmu(), _driver_fmu()) # test_run_osp_system_structure(_system_structure(), show=True) # _test_run_osp_sweep(_system_structure(), show=True, alg="fixedStep") diff --git a/tests/test_oscillator_xd.py b/tests/test_oscillator_xd.py new file mode 100644 index 0000000..efd43c0 --- /dev/null +++ b/tests/test_oscillator_xd.py @@ -0,0 +1,409 @@ +from functools import partial +from math import atan2, cos, exp, pi, sin, sqrt +from pathlib import Path + +import matplotlib.pyplot as plt +import numpy as np +import pytest + + +def do_show( + time: list, + z: list, + v: list, + compare1: list | None = None, + compare2: list | None = None, + z_label: str = "z-position", + v_label: str = "z-speed", +): + fig, ax = plt.subplots() + ax.plot(time, z, label=z_label) + ax.plot(time, v, label=v_label) + if compare1 is not None: + ax.plot(time, compare1, label="compare1") + if compare2 is not None: + ax.plot(time, compare2, label="compare2") + ax.legend() + plt.show() + + +def force_xv( + dim: int = 3, + t: float | None = None, + x: np.ndarray | None = None, + v: np.ndarray | None = None, + dt: float | None = None, + const: float | None = None, + d_omega: float = 0.0, + ampl: float | None = None, + omega: float = 0.1, + ampl_x: float | None = None, + ampl_v: float | None = None, +): + """Use a force which is dependent on position and/or velocity. Time is ignored""" + force = np.array((0,) * dim, float) + if isinstance(const, float): + force += np.array((const,) * dim, float) + if t is not None and ampl is not None: + if d_omega == 0.0: + force += np.array((0, 0, ampl * sin(omega * t)), float) # fixed frequency + else: + force += np.array((0, 0, ampl * sin((omega + d_omega * t) * t)), float) # frequency sweep + if isinstance(ampl_x, float): + force += ampl_x * x # type: ignore[operator] ##it is definitely float* ndarray + if isinstance(ampl_v, float): + force += ampl_v * v # type: ignore[operator] ##it is definitely float* ndarray + return force + + +def forced_oscillator( + t: float, k: float, c: float, m: float, a: float = 0.0, wf: float = 0.1, x0: float = 1.0, v0: float = 0.0 +): + """Calculates the expected (analytic) position and speed of a harmonic oscillator (in one dimension) + with the given parameter setting. + + Args: + t (float): time + k,c,m (float): harmonic oscillator parameters + a,wf (float): sinusoidal force parameters (amplitude and angular frequency) + x0, v0 (float): start values for harmonic oscillator (force has fixed start values) + """ + from math import atan2, sin, sqrt + + w0 = sqrt(k / m) # omega0 + b = c / (2 * m) # beta + if a != 0: + assert x0 == 0 and v0 == 0, "Checking of forced oscillations is only implemented for x0=0 and v0=0" + A = a / sqrt(((w0**2 - wf**2) ** 2 + (2 * b * wf) ** 2)) + d = atan2(2 * b * wf, w0**2 - wf**2) # phase angle in equilibrium + x0 = A * sin(d) + v0 = -A * wf * cos(d) + x_e = A * sin(wf * t - d) + v_e = A * wf * cos(wf * t - d) + # return x_e, v_e + else: + x_e, v_e = (0.0, 0.0) + + if w0 - b > 1e-10: # damped oscillation + w1 = sqrt(w0**2 - b**2) # angular frequency of oscillation + x = exp(-b * t) * (x0 * cos(w1 * t) + (x0 * b + v0) / w1 * sin(w1 * t)) + v = exp(-b * t) * (v0 * cos(w1 * t) - (w1 * x0 + b**2 / w1 * x0 + b / w1 * v0) * sin(w1 * t)) + elif abs(w0 - b) < 1e-10: # critically damped oscillation + x = ((v0 + b * x0) * t + x0) * exp(-b * t) + v = -b * (v0 + b * x0) * t * exp(-b * t) + else: # over-damped oscillation + w1_ = sqrt(b**2 - w0**2) + _b1 = 2 * b - w1_ + _b2 = 2 * b + w1_ + x = ((b + w1_) * x0 + v0) / 2 / w1_ * exp(-(b - w1_) * t) - ((b - w1_) * x0 + v0) / 2 / w1_ * exp( + -(b + w1_) * t + ) + v = -((b + w1_) * x0 + v0) / 2 / w1_ * (b - w1_) * exp(-(b - w1_) * t) + ((b - w1_) * x0 + v0) / 2 / w1_ * ( + b + w1_ + ) * exp(-(b + w1_) * t) + if a != 0: + return (x + x_e, v + v_e) + else: + return (x, v) + + +def run_oscillation_z( + k: float, + c: float, + m: float, + ampl: float, + omega: float, + x0: float = 1.0, + v0: float = 0.0, + dt: float = 0.01, + end: float = 30.0, + tol: float = 1e-3, +): + """Run the oscillator with the given settings for the given time (only z-direction activated) + and return the oscillator object and the time series for z-position and z-velocity.""" + + from examples.oscillator_xd import Force, OscillatorXD + + _f = partial(force_xv, dim=3, ampl=ampl, omega=omega) + _force = Force(3, _f) + osc = OscillatorXD(dim=3, k=(1.0, 1.0, k), c=(0.0, 0.0, c), m=m, tolerance=tol, force=_force) + osc.x[2] = x0 # set initial z value + osc.v[2] = v0 # set initial z-speed + times, z, v = [], [], [] + time = 0.0 + while time < end: + times.append(time) + z.append(osc.x[2]) + v.append(osc.v[2]) + osc.do_step(time, dt) + time += dt + + return (osc, times, z, v) + + +def sweep_oscillation_z( + k: float, + c: float, + m: float, + ampl: float, + d_omega: float, + x0: float = 1.0, + v0: float = 0.0, + dt: float = 0.01, + end: float = 30.0, + tol: float = 1e-3, +): + """Run the oscillator with the given settings + with linearly increasing force frequency + for the given time (only z-direction activated) + and return the oscillator object and the time series for z-position and z-velocity.""" + + from examples.oscillator_xd import Force, OscillatorXD + + _f = partial(force_xv, dim=3, ampl=ampl, omega=0.0, d_omega=d_omega) + _force = Force(3, _f) + osc = OscillatorXD(dim=3, k=(1.0, 1.0, k), c=(0.0, 0.0, c), m=m, tolerance=tol, force=_force) + osc.x[2] = x0 # set initial z value + osc.v[2] = v0 # set initial z-speed + times, z, v, f = [], [], [], [] + time = 0.0 + while time < end: + times.append(time) + z.append(osc.x[2]) + v.append(osc.v[2]) + osc.do_step(time, dt) + f.append(_f(t=time)[2]) + time += dt + + return (osc, times, z, v, f) + + +def test_oscillator_class(show): + """Test the Oscillator class in isolation. + Such tests are strongly recommended before compiling the model into an FMU. + + With respect to `wiki `_ our parameters are: + b = c, k=k => beta = c/(2m), w0 = sqrt(k/m) => w1 = sqrt(beta^2 - w0^2) = sqrt( c^2/4/m^2 - k/m) + """ + test_cases: list[tuple[float, float, float, float, float, float, str]] = [ + # k c m a w x0 description + (1.0, 0.0, 1.0, 0.0, 0.1, 1.0, "Oscillator without damping and force"), + (1.0, 0.2, 1.0, 0.0, 0.1, 1.0, "Oscillator include damping"), + (1.0, 2.0, 1.0, 0.0, 0.1, 1.0, "Oscillator critically damped"), + (1.0, 5.0, 1.0, 0.0, 0.1, 1.0, "Oscillator over-damped"), + (1.0, 0.2, 1.0, 1.0, 0.5, 0.0, "Forced oscillation. Less than resonance freq"), + (1.0, 0.2, 1.0, 1.0, 1.0, 0.0, "Forced oscillation. Damped. Resonant"), + (1.0, 0.2, 1.0, 1.0, 2.0, 0.0, "Forced oscillation. Damped. Above resonance freq"), + (1.0, 2.0, 1.0, 1.0, 1.0, 0.0, "Forced oscillation. Crit. damped. Resonant"), + (1.0, 5.0, 1.0, 1.0, 1.0, 0.0, "Forced oscillation. Over-damped. Resonant"), + ] + tol = 1e-3 # tolerance for simulation + for k, c, m, a, w, x0, msg in test_cases: + print(f"{msg}: k={k}, c={c}, m={m}, a={a}, wf={w}", end="") + osc, t, z, v = run_oscillation_z(k=k, c=c, m=m, ampl=a, omega=w, x0=x0, tol=tol) + if c < 2.0: # only if damping is small enough + cp = 2.0 * pi / sqrt(k / m - (c / 2.0 / m) ** 2) + assert abs(osc.period[2] - cp) < 1e-12, f"Period[{2}]: {osc.period[2]} != {cp}. {osc.w2}, {osc.gam}" + x_expect, v_expect = [], [] + for ti in t: + _x, _v = forced_oscillator(ti, k, c, m, a, w, x0=x0) + x_expect.append(_x) + v_expect.append(_v) + if show: + do_show(t, z, v, x_expect, v_expect) + emax = 0.0 + for i, ti in enumerate(t): + assert abs(z[i] - x_expect[i]) < 50 * tol, f"@{ti}: z={z[i]} != {x_expect[i]}" + assert abs(v[i] - v_expect[i]) < 50 * tol, f"@{ti}: v={v[i]} != {v_expect[i]}" + emax = max(emax, abs(z[i] - x_expect[i]), abs(v[i] - v_expect[i])) + print(f". Max absolute error: {emax}") + + +def test_2d(show): + from examples.oscillator_xd import OscillatorXD + + def run_2d( + x0: tuple[float, float, float], + v0: tuple[float, float, float], + k: tuple[float, float, float] = (1.0, 1.0, 1.0), + c: tuple[float, float, float] = (0.0, 0.0, 0.0), + end: float = 100.0, + dt: float = 0.01, + tolerance: float = 1e-5, + ): + osc = OscillatorXD(dim=3, k=k, c=c, tolerance=tolerance) + osc.x[:3] = np.array(x0, float) # set initial 3D position + osc.x[3:] = np.array(v0, float) # set initial 3D speed + x, y = [], [] + t0 = 0.0 + for time in np.linspace(dt, end, int(end / dt), endpoint=True): + x.append(osc.x[0]) + y.append(osc.x[1]) + osc.do_step(time, time - t0) + t0 = time + x.append(osc.x[0]) + y.append(osc.x[1]) + + return (osc, x, y) + + def show_2d(x: list[float], y: list[float]): + fig, ax = plt.subplots() + ax.plot(x, y, label="x-y") + ax.legend() + plt.show() + + def area(x: list[float], y: list[float]): + """Calculate the area within the curve.""" + angle0 = 0.0 + area = 0.0 + anglesum = 0.0 + for _x, _y in zip(x, y, strict=False): + angle = atan2(_y, _x) + dangle = min((2 * np.pi) - abs(angle0 - angle), abs(angle0 - angle)) + area += (_x**2 + _y**2) * dangle / 2 + anglesum += dangle + angle0 = angle + return area + + osc, x, y = run_2d(x0=(1.0, 0.0, 0.0), v0=(0.0, 1.0, 0.0), end=2 * np.pi, tolerance=1e-5) + assert np.allclose(osc.period, (2 * np.pi, 2 * np.pi, 2 * np.pi)), f"Found {osc.period}" + assert abs(area(x, y) - np.pi) < 1e-10, f"Found area {area(x, y)}" + if show: + show_2d(x, y) + for _x, _y in zip(x, y, strict=False): + assert abs(_x**2 + _y**2 - 1.0) < 1e-10, f"Found {_x}**2 + {_y}**2 = {_x**2 + _y**2} != 1.0" + + osc, x, y = run_2d(x0=(1.0, 0.0, 0.0), v0=(0.0, 1.0, 0.0), c=(0.5, 0.5, 0), end=10 * np.pi) + assert (area(x, y) - 0.9977641389836932) < 1e-15 + + if show: + show_2d(x, y) + + osc, x, y = run_2d(x0=(1.0, 0.0, 0.0), v0=(0.0, 1.0, 0.0), k=(1.0, 1.0 / 16, 0), end=10 * np.pi) + assert abs(x[-1] - 1.0) < 1e-12, f"Found {x[-1]}" + assert abs(y[-1] - 4.0) < 1e-12, f"Found {y[-1]}" + osc, x, y = run_2d(x0=(1.0, 0.0, 0.0), v0=(0.0, 1.0, 0.0), k=(1.0, 1.0 / 15.8, 0), end=20 * np.pi) + if show: + show_2d(x, y) + + +def test_sweep_oscillator(show: bool = False): + """A forced oscillator where the force frequency is changed linearly as d_omega*time. + The test demonstrates that a monolithic simulation provides accurate results in all ranges of the force frequency. + Co-simulating the oscillator and the force, this does not work. + """ + osc, times0, z0, v0, f0 = sweep_oscillation_z( + k=1.0, + c=0.1, + m=1.0, + ampl=1.0, + d_omega=0.1, + x0=0.0, + v0=0.0, + dt=0.1, # 'ground truth', small dt + end=100.0, + tol=1e-3, + ) + with open(Path.cwd() / "oscillator_sweep0.dat", "w") as fp: + for i in range(len(times0)): + fp.write(f"{times0[i]}\t{z0[i]}\t{v0[i]}\t{f0[i]}\n") + + if show: + freq = [0.1 * t / 2 / np.pi for t in times0] + fig, ax = plt.subplots() + ax.plot(freq, z0, label="z0(t)") + ax.plot(freq, v0, label="v0(t)") + # ax.plot(freq, f0, label="F0(t)") + ax.legend() + plt.show() + + osc, times, z, v, f = sweep_oscillation_z( + k=1.0, + c=0.1, + m=1.0, + ampl=1.0, + d_omega=0.1, + x0=0.0, + v0=0.0, + dt=1, # dt similar to resonance frequency + end=100.0, + tol=1e-3, + ) + i0 = 0 + for i in range(len(times)): # demonstrate that the results are accurate, even if dt is large + t = times[i] + while abs(times0[i0] - t) > 1e-10: + i0 += 1 + assert times0[i0] - t < 0.1, f"Time entry for time {t} not found in times0" + + assert abs(z0[i0] - z[i]) < 4e-2, f"Time {t}. Found {z0[i0]} != {z[i]}" + assert abs(v0[i0] - v[i]) < 4e-2, f"Time {t}. Found {v0[i0]} != {v[i]}" + + if show: + fig, ax = plt.subplots() + ax.plot(times0, z0, label="z0(t)") + ax.plot(times, z, label="z(t)") + ax.legend() + plt.show() + + +def test_forced_xv(show: bool = False): + from examples.oscillator_xd import Force, OscillatorXD + + def do_scenario( + k: float = 1.0, + c: float = 0.0, + ax: float = 0.0, + av: float = 0.0, + const: float | None = None, + v0: float = 1.0, + show=show, + title: str = "Scenario", + ): + _f = partial(force_xv, dim=6, ampl_x=ax, ampl_v=av, const=const) + _force = Force(6, _f) + osc = OscillatorXD(dim=6, k=(k,) * 6, c=(c,) * 6, m=1.0, tolerance=1e-3, force=_force) + osc.v[2] = v0 # set initial z-speed + times, z, v = [], [], [] + time = 0.0 + dt = 0.1 + while time < 50.0: + times.append(time) + z.append(osc.x[2]) + v.append(osc.v[2]) + osc.do_step(time, dt) + time += dt + + if show: + fig, axis = plt.subplots() + axis.plot(times, z, label="z0(t)") + axis.plot(times, v, label="v0(t)") + plt.legend() + axis.set_title(title) + plt.show() + return (z, v) + + z, v = do_scenario(const=-10.0, v0=0.0, show=False, title="Constant force -10.0") + assert abs(sum(v) / len(v)) < 0.01, "Velocity still around +-0" + assert abs(sum(z) / len(z) + 10.0) < 0.06, "New equilibrium with constant force" + return + z0, v0 = do_scenario(show=False, title="Basic oscillator with start velocity 1.0") + z, v = do_scenario(k=0, ax=-1.0, show=False, title="Spring constant 0.0, replaced with force(position)") + assert np.allclose(z0, z), "Same effect as change in frequency when force dependent on position." + assert np.allclose(v0, v), "Same effect as change in frequency when force dependent on position." + z0, v0 = do_scenario(c=0.1, show=False, title="Basic oscillator with damping=0.1 and start veocity=1.0") + z, v = do_scenario(c=0.0, av=-0.1, show=False, title="Daming replaced by force(velocity)") + assert np.allclose(z0, z), "Same effect as change in damping when force dependent on velocity." + assert np.allclose(v0, v), "Same effect as change in damping when force dependent on velocity." + + +if __name__ == "__main__": + retcode = pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__]) + assert retcode == 0, f"Non-zero return code {retcode}" + import os + + os.chdir(Path(__file__).parent.absolute() / "test_working_directory") + # test_oscillator_class(show=True) + # test_2d(show=True) + # test_sweep_oscillator(show=True) + # test_forced_xv(show=True) diff --git a/tests/test_transform.py b/tests/test_transform.py new file mode 100644 index 0000000..7161a56 --- /dev/null +++ b/tests/test_transform.py @@ -0,0 +1,179 @@ +import logging + +import numpy as np +import pytest +from scipy.spatial.transform import Rotation as Rot + +from component_model.utils.transform import ( + cartesian_to_spherical, + euler_rot_spherical, + normalized, + rot_from_spherical, + rot_from_vectors, + spherical_to_cartesian, + spherical_unique, +) + +logger = logging.getLogger(__name__) +logging.basicConfig(level=logging.INFO) + + +def arrays_equal(arr1: np.ndarray | tuple | list, arr2: np.ndarray | tuple | list, dtype="float", eps=1e-7): + assert len(arr1) == len(arr2), "Length not equal!" + + for i in range(len(arr1)): + # assert type(arr1[i]) == type(arr2[i]), f"Array element {i} type {type(arr1[i])} != {type(arr2[i])}" + assert abs(arr1[i] - arr2[i]) < eps, f"Component {i}: {arr1[i]} != {arr2[i]}" + + +def tuples_nearly_equal(tuple1: tuple, tuple2: tuple, eps=1e-10): + """Check whether the values in tuples (of any tuple-structure) are nearly equal""" + assert isinstance(tuple1, tuple), f"{tuple1} is not a tuple" + assert isinstance(tuple2, tuple), f"{tuple2} is not a tuple" + assert len(tuple1) == len(tuple2), f"Lenghts of tuples {tuple1}, {tuple2} not equal" + for t1, t2 in zip(tuple1, tuple2, strict=False): + if isinstance(t1, tuple): + assert isinstance(t2, tuple), f"Tuple expected. Found {t2}" + assert tuples_nearly_equal(t1, t2) + elif isinstance(t1, float) or isinstance(t2, float): + assert t1 == t2 or abs(t1 - t2) < eps, f"abs({t1} - {t2}) >= {eps}" + else: + assert t1 == t2, f"{t1} != {t2}" + return True + + +def test_spherical_cartesian(): + for vec in [ + (0, 0, 0), + (0, 0, 1), + (0, 1, 0), + (0, 1, 1), + (1, 0, 0), + (1, 0, 1), + (1, 1, 0), + (1, 1, 1), + ]: + sVec = cartesian_to_spherical(vec) + _vec = spherical_to_cartesian(sVec) + arrays_equal(np.array(vec, dtype="float"), _vec) + + +def test_spherical_unique(): + def do_test(x0: tuple, x1: tuple): + if len(x0) == 3: + _unique = spherical_unique(np.append(x0[0], np.radians(x0[1:]))) + unique = np.append(_unique[0], np.degrees(_unique[1:])) + else: + unique = np.degrees(spherical_unique(np.radians(x0))) + assert np.allclose(unique, x1), f"{x0} -> {list(unique)} != {list(x1)}" + + do_test((0, 99), (0.0, 0.0)) + do_test((-1, 10, 20), (1, 180 - 10.0, 180 + 20)) + do_test((10, 300), (10, 300)) + do_test((190, 300), (170.0, 300.0 + 180 - 360)) + do_test((170, 300), (170.0, 300.0)) + do_test((170, 720), (170.0, 0.0)) + + +def test_rot_from_spherical(): + assert np.allclose(rot_from_spherical((0, 0)).as_matrix(), Rot.identity().as_matrix()) + assert np.allclose(rot_from_spherical((90, 0), True).apply((0, 0, 1)), (1.0, 0, 0)) + assert np.allclose(rot_from_spherical((0, 90), True).apply((0, 0, 1)), (0.0, 0, 1.0)) + assert np.allclose(rot_from_spherical((0, 90), True).apply((1.0, 0, 0)), (0.0, 1.0, 0.0)) + assert np.allclose(rot_from_spherical((1.0, 45, 45), True).apply((0.0, 0, 1.0)), (0.5, 0.5, np.sqrt(2) / 2)) + down = rot_from_spherical((180, 0), True) + print("down+2", down, down * rot_from_spherical((2, 0), True)) + + +def test_rot_from_vectors(): + def do_check(vec1: tuple | list | np.ndarray, vec2: tuple | list | np.ndarray): + v1 = np.array(vec1, float) + v2 = np.array(vec2, float) + r = rot_from_vectors(v1, v2) + v = r.apply(v1) + assert np.allclose(v2, v), f"{r.as_matrix} does not turn {v1} into {v2}" + + do_check((1, 0, 0), (-1, 0, 0)) + return + do_check((1, 0, 0), (0, 1, 0)) + rng = np.random.default_rng(12345) + for _i in range(100): + v1 = normalized(rng.random(3)) + do_check(v1, -v1) # opposite vectors + do_check(v1, v1) # identical vectors + for _i in range(1000): + do_check(normalized(rng.random(3)), normalized(rng.random(3))) + + +def test_euler_rot_spherical(): + """Test euler rotations. + Note: We use XYZ + (roll, pitch, yaw) convention Tait-Brian.""" + _re = Rot.from_euler("zyx", (20, 40, 60), degrees=True) # extrinsic rotation + _ri = Rot.from_euler("XYZ", (60, 40, 20), degrees=True) # intrinsic rotation + assert np.allclose(_re.as_matrix(), _ri.as_matrix()), "Rotation matrices for extrinsic == intrisic+reversed" + _re_inv = Rot.from_euler("xyz", (-60, -40, -20), degrees=True) + assert np.allclose(_re.as_matrix(), _re_inv.as_matrix().transpose()), "_re_inv is inverse to _re" + assert np.allclose(_re.as_matrix() @ _re_inv.as_matrix(), Rot.identity(3).as_matrix()), "_re_inv is the inverse." + + assert np.allclose(Rot.from_euler("XYZ", (90, 0, 0), degrees=True).apply((1, 0, 0)), (1, 0, 0)), "Roll invariant x" + assert np.allclose(Rot.from_euler("XYZ", (90, 0, 0), degrees=True).apply((0, 1, 0)), (0, 0, 1)), ( + "Roll y(SB) -> z(down)" + ) + assert np.allclose(Rot.from_euler("XYZ", (90, 0, 0), degrees=True).apply((0, 0, -1)), (0, 1, 0)), ( + "Roll -z(up) -> y(SB)" + ) + assert np.allclose(Rot.from_euler("XYZ", (0, 90, 0), degrees=True).apply((1, 0, 0)), (0, 0, -1)), ( + "Pitch x(FW) -> -z(up)" + ) + assert np.allclose(Rot.from_euler("XYZ", (0, 90, 0), degrees=True).apply((0, 1, 0)), (0, 1, 0)), "Pitch invariant y" + assert np.allclose(Rot.from_euler("XYZ", (0, 90, 0), degrees=True).apply((0, 0, 1)), (1, 0, 0)), ( + "Pitch z(down) -> x(FW)" + ) + assert np.allclose(Rot.from_euler("XYZ", (0, 0, 90), degrees=True).apply((1, 0, 0)), (0, 1, 0)), ( + "Yaw x(FW) -> y(SB)" + ) + assert np.allclose(Rot.from_euler("XYZ", (0, 0, 90), degrees=True).apply((0, 1, 0)), (-1, 0, 0)), ( + "Yaw y(SB) -> -x(BW)" + ) + assert np.allclose(Rot.from_euler("XYZ", (0, 0, 90), degrees=True).apply((0, 0, 1)), (0, 0, 1)), "Yaw invariant z" + assert np.allclose(np.degrees(euler_rot_spherical((90, 0, 0), (90, 90), degrees=True)), (0, 0)), "Roll y -> z" + assert np.allclose(np.degrees(euler_rot_spherical((0, 90, 0), (0, 0), degrees=True)), (90, 0)), "Pitch z -> x" + assert np.allclose(np.degrees(euler_rot_spherical((0, 0, 90), (90, 0), degrees=True)), (90, 90)), "Yaw x -> y" + + +def test_euler_rot(): + """Test general issues about 3D rotations.""" + _rot = Rot.from_euler("XYZ", (90, 0, 0), degrees=True) # roll 90 deg + assert np.allclose(_rot.apply((0, 0, 1)), (0, -1, 0)), "z -> -y" + _rot2 = Rot.from_euler("XYZ", (90, 0, 0), degrees=True) * _rot # another 90 deg in same direction + assert np.allclose(_rot2.apply((0, 0, 1)), (0, 0, -1)), "z -> -z" + assert np.allclose(_rot2.apply((0, 0, 1)), Rot.from_euler("XYZ", (180, 0, 0), degrees=True).apply((0, 0, 1))), ( + "Angles added" + ) + _rot2 = _rot.from_euler("XYZ", (0, 90, 0), degrees=True) * _rot # + pitch 90 deg + print(_rot2.as_euler(seq="XYZ", degrees=True)) + print(Rot.from_euler("XYZ", (90, 90, 0), degrees=True).as_euler(seq="XYZ", degrees=True)) + _rot3 = Rot.from_euler("XYZ", (0, 0, 90), degrees=True) * _rot2 # +yaw 90 deg + assert np.allclose(_rot3.apply((1, 0, 0)), (0, 0, -1)) + assert np.allclose(_rot3.apply((0, 1, 0)), (0, 1, 0)) + assert np.allclose(_rot3.apply((0, 0, 1)), (1, 0, 0)) + assert np.allclose(np.cross(_rot3.apply((1, 0, 0)), _rot3.apply((0, 1, 0))), _rot3.apply((0, 0, 1))), ( + "Still right-hand" + ) + + +def test_normalized(): + assert np.allclose(normalized(np.array((1, 0, 0), float)), (1, 0, 0)) + assert np.allclose(normalized(np.array((1, 1, 1), float)), np.array((1, 1, 1), float) / np.sqrt(3)) + + +if __name__ == "__main__": + retcode = pytest.main(["-rP -s -v", __file__]) + assert retcode == 0, f"Return code {retcode}" + # test_spherical_cartesian() + # test_spherical_unique() + # test_rot_from_spherical() + # test_rot_from_vectors() + # test_euler_rot_spherical() + # test_euler_rot() + # test_normalized() diff --git a/tests/test_variable.py b/tests/test_variable.py index 2c3d632..edc4b50 100644 --- a/tests/test_variable.py +++ b/tests/test_variable.py @@ -1,31 +1,29 @@ # pyright: ignore[reportAttributeAccessIssue] # PythonFMU generates variable value objects using setattr() import logging -from typing import Sequence import math import xml.etree.ElementTree as ET # noqa: N817 from enum import Enum +from typing import Sequence import numpy as np -from scipy.spatial.transform import Rotation as rot import pytest from pythonfmu.enums import Fmi2Causality as Causality # type: ignore from pythonfmu.enums import Fmi2Initial as Initial # type: ignore from pythonfmu.enums import Fmi2Variability as Variability # type: ignore +from scipy.spatial.transform import Rotation as Rot from component_model.model import Model -from component_model.variable import ( - Check, - Variable, - VariableInitError, - VariableRangeError, +from component_model.utils.analysis import extremum, extremum_series +from component_model.utils.transform import ( cartesian_to_spherical, - spherical_to_cartesian, - spherical_unique, + euler_rot_spherical, + normalized, rot_from_spherical, rot_from_vectors, - euler_rot_spherical, - normalized + spherical_to_cartesian, + spherical_unique, ) +from component_model.variable import Check, Variable logger = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO) @@ -71,6 +69,7 @@ def tuples_nearly_equal(tuple1: tuple, tuple2: tuple, eps=1e-10): assert t1 == t2, f"{t1} != {t2}" return True + def test_var_check(): ck = Check.u_none | Check.r_none assert Check.r_none in ck, "'Check.u_none | Check.r_none' sets both unit and range checking to None" @@ -128,90 +127,111 @@ def test_spherical_cartesian(): _vec = spherical_to_cartesian(sVec) arrays_equal(np.array(vec, dtype="float"), _vec) + def test_spherical_unique(): - def do_test( x0: tuple, x1: tuple): + def do_test(x0: tuple, x1: tuple): if len(x0) == 3: - _unique = spherical_unique( np.append(x0[0], np.radians(x0[1:]))) - unique = np.append( _unique[0], np.degrees(_unique[1:])) + _unique = spherical_unique(np.append(x0[0], np.radians(x0[1:]))) + unique = np.append(_unique[0], np.degrees(_unique[1:])) else: - unique = np.degrees(spherical_unique( np.radians(x0))) - assert np.allclose(unique, x1), f"{x0} -> {list(unique)} != {list(x1)}" - do_test( (0,99), (0.,0.)) - do_test( (-1, 10, 20), (1, 180-10.,180+20)) - do_test( (10,300), (10,300)) - do_test( (190,300), (170.,300.+180-360)) - do_test( (170,300), (170.,300.)) - do_test( (170,720), (170.,0.)) - + unique = np.degrees(spherical_unique(np.radians(x0))) + assert np.allclose(unique, x1), f"{x0} -> {list(unique)} != {list(x1)}" + + do_test((0, 99), (0.0, 0.0)) + do_test((-1, 10, 20), (1, 180 - 10.0, 180 + 20)) + do_test((10, 300), (10, 300)) + do_test((190, 300), (170.0, 300.0 + 180 - 360)) + do_test((170, 300), (170.0, 300.0)) + do_test((170, 720), (170.0, 0.0)) + + def test_rot_from_spherical(): - assert np.allclose( rot_from_spherical( (0,0)).as_matrix(), rot.identity().as_matrix()) - assert np.allclose( rot_from_spherical( (90,0), True).apply( (0,0,1)), (1.0,0,0)) - assert np.allclose( rot_from_spherical( (0,90), True).apply( (0,0,1)), (0.0,0,1.0)) - assert np.allclose( rot_from_spherical( (0,90), True).apply( (1.0,0,0)), (0.0,1.0,0.0)) - assert np.allclose( rot_from_spherical( (1.0,45,45), True).apply( (0.0,0,1.0)), (0.5,0.5,np.sqrt(2)/2)) - down = rot_from_spherical( (180,0), True) - print( "down+2", down, down*rot_from_spherical( (2,0), True)) + assert np.allclose(rot_from_spherical((0, 0)).as_matrix(), Rot.identity().as_matrix()) + assert np.allclose(rot_from_spherical((90, 0), True).apply((0, 0, 1)), (1.0, 0, 0)) + assert np.allclose(rot_from_spherical((0, 90), True).apply((0, 0, 1)), (0.0, 0, 1.0)) + assert np.allclose(rot_from_spherical((0, 90), True).apply((1.0, 0, 0)), (0.0, 1.0, 0.0)) + assert np.allclose(rot_from_spherical((1.0, 45, 45), True).apply((0.0, 0, 1.0)), (0.5, 0.5, np.sqrt(2) / 2)) + down = rot_from_spherical((180, 0), True) + print("down+2", down, down * rot_from_spherical((2, 0), True)) + def test_rot_from_vectors(): - def do_check( vec1:Sequence, vec2:Sequence): + def do_check(vec1: Sequence, vec2: Sequence): v1 = np.array(vec1, float) v2 = np.array(vec2, float) - r = rot_from_vectors( v1, v2) + r = rot_from_vectors(v1, v2) v = r.apply(v1) - assert np.allclose( v2, v), f"{r.as_matrix} does not turn {v1} into {v2}" - - do_check( (1,0,0), (-1,0,0)) + assert np.allclose(v2, v), f"{r.as_matrix} does not turn {v1} into {v2}" + + do_check((1, 0, 0), (-1, 0, 0)) return - do_check( (1,0,0), (0,1,0)) + do_check((1, 0, 0), (0, 1, 0)) rng = np.random.default_rng(12345) - for i in range(100): - v1 = normalized( rng.random(3)) - do_check( v1, -v1) # opposite vectors - do_check( v1, v1) # identical vectors - for i in range(1000): - do_check( normalized( rng.random(3)), normalized( rng.random(3))) + for _i in range(100): + v1 = normalized(rng.random(3)) + do_check(v1, -v1) # opposite vectors + do_check(v1, v1) # identical vectors + for _i in range(1000): + do_check(normalized(rng.random(3)), normalized(rng.random(3))) + def test_euler_rot_spherical(): """Test euler rotations. Note: We use XYZ + (roll, pitch, yaw) convention Tait-Brian.""" - _re = rot.from_euler( 'zyx', (20,40,60), degrees=True) # extrinsic rotation - _ri = rot.from_euler( 'XYZ', (60,40,20), degrees=True) # intrinsic rotation + _re = Rot.from_euler("zyx", (20, 40, 60), degrees=True) # extrinsic rotation + _ri = Rot.from_euler("XYZ", (60, 40, 20), degrees=True) # intrinsic rotation assert np.allclose(_re.as_matrix(), _ri.as_matrix()), "Rotation matrices for extrinsic == intrisic+reversed" - _re_inv = rot.from_euler( 'xyz', (-60,-40,-20), degrees=True) + _re_inv = Rot.from_euler("xyz", (-60, -40, -20), degrees=True) assert np.allclose(_re.as_matrix(), _re_inv.as_matrix().transpose()), "_re_inv is inverse to _re" - assert np.allclose(_re.as_matrix() @ _re_inv.as_matrix(), rot.identity(3).as_matrix()), "_re_inv is the inverse." - - assert np.allclose( rot.from_euler('XYZ', (90,0,0), degrees=True).apply( (1,0,0)), (1,0,0)), "Roll invariant x" - assert np.allclose( rot.from_euler('XYZ', (90,0,0), degrees=True).apply( (0,1,0)), (0,0,1)), "Roll y(SB) -> z(down)" - assert np.allclose( rot.from_euler('XYZ', (90,0,0), degrees=True).apply( (0,0,-1)), (0,1,0)), "Roll -z(up) -> y(SB)" - assert np.allclose( rot.from_euler('XYZ', (0,90,0), degrees=True).apply( (1,0,0)), (0,0,-1)), "Pitch x(FW) -> -z(up)" - assert np.allclose( rot.from_euler('XYZ', (0,90,0), degrees=True).apply( (0,1,0)), (0,1,0)), "Pitch invariant y" - assert np.allclose( rot.from_euler('XYZ', (0,90,0), degrees=True).apply( (0,0,1)), (1,0,0)), "Pitch z(down) -> x(FW)" - assert np.allclose( rot.from_euler('XYZ', (0,0,90), degrees=True).apply( (1,0,0)), (0,1,0)), "Yaw x(FW) -> y(SB)" - assert np.allclose( rot.from_euler('XYZ', (0,0,90), degrees=True).apply( (0,1,0)), (-1,0,0)), "Yaw y(SB) -> -x(BW)" - assert np.allclose( rot.from_euler('XYZ', (0,0,90), degrees=True).apply( (0,0,1)), (0,0,1)), "Yaw invariant z" - assert np.allclose( np.degrees(euler_rot_spherical( (90,0,0), (90,90), degrees=True)), (0,0)), "Roll y -> z" - assert np.allclose( np.degrees(euler_rot_spherical( (0,90,0), (0,0), degrees=True)), (90,0)), "Pitch z -> x" - assert np.allclose( np.degrees(euler_rot_spherical( (0,0,90), (90,0), degrees=True)), (90,90)), "Yaw x -> y" + assert np.allclose(_re.as_matrix() @ _re_inv.as_matrix(), Rot.identity(3).as_matrix()), "_re_inv is the inverse." + + assert np.allclose(Rot.from_euler("XYZ", (90, 0, 0), degrees=True).apply((1, 0, 0)), (1, 0, 0)), "Roll invariant x" + assert np.allclose(Rot.from_euler("XYZ", (90, 0, 0), degrees=True).apply((0, 1, 0)), (0, 0, 1)), ( + "Roll y(SB) -> z(down)" + ) + assert np.allclose(Rot.from_euler("XYZ", (90, 0, 0), degrees=True).apply((0, 0, -1)), (0, 1, 0)), ( + "Roll -z(up) -> y(SB)" + ) + assert np.allclose(Rot.from_euler("XYZ", (0, 90, 0), degrees=True).apply((1, 0, 0)), (0, 0, -1)), ( + "Pitch x(FW) -> -z(up)" + ) + assert np.allclose(Rot.from_euler("XYZ", (0, 90, 0), degrees=True).apply((0, 1, 0)), (0, 1, 0)), "Pitch invariant y" + assert np.allclose(Rot.from_euler("XYZ", (0, 90, 0), degrees=True).apply((0, 0, 1)), (1, 0, 0)), ( + "Pitch z(down) -> x(FW)" + ) + assert np.allclose(Rot.from_euler("XYZ", (0, 0, 90), degrees=True).apply((1, 0, 0)), (0, 1, 0)), ( + "Yaw x(FW) -> y(SB)" + ) + assert np.allclose(Rot.from_euler("XYZ", (0, 0, 90), degrees=True).apply((0, 1, 0)), (-1, 0, 0)), ( + "Yaw y(SB) -> -x(BW)" + ) + assert np.allclose(Rot.from_euler("XYZ", (0, 0, 90), degrees=True).apply((0, 0, 1)), (0, 0, 1)), "Yaw invariant z" + assert np.allclose(np.degrees(euler_rot_spherical((90, 0, 0), (90, 90), degrees=True)), (0, 0)), "Roll y -> z" + assert np.allclose(np.degrees(euler_rot_spherical((0, 90, 0), (0, 0), degrees=True)), (90, 0)), "Pitch z -> x" + assert np.allclose(np.degrees(euler_rot_spherical((0, 0, 90), (90, 0), degrees=True)), (90, 90)), "Yaw x -> y" + def test_euler_rot(): """Test general issues about 3D rotations.""" - _rot = rot.from_euler('XYZ', (90,0,0), degrees=True) # roll 90 deg - assert np.allclose( _rot.apply( (0,0,1)), (0,-1,0)), "z -> -y" - _rot2 = rot.from_euler('XYZ', (90,0,0), degrees=True) * _rot # another 90 deg in same direction - assert np.allclose( _rot2.apply( (0,0,1)), (0,0,-1)), "z -> -z" - assert np.allclose( _rot2.apply( (0,0,1)), rot.from_euler('XYZ', (180,0,0), degrees=True).apply( (0,0,1))), ( - "Angles added") - _rot2 = _rot.from_euler('XYZ', (0,90,0), degrees=True) * _rot # + pitch 90 deg - print(_rot2.as_euler( seq='XYZ', degrees=True)) - print(rot.from_euler('XYZ', (90,90,0), degrees=True).as_euler( seq='XYZ', degrees=True)) - _rot3 = rot.from_euler('XYZ', (0,0,90), degrees=True) * _rot2 # +yaw 90 deg - assert np.allclose( _rot3.apply((1,0,0)), (0,0,-1)) - assert np.allclose( _rot3.apply((0,1,0)), (0,1,0)) - assert np.allclose( _rot3.apply((0,0,1)), (1,0,0)) - assert np.allclose( np.cross(_rot3.apply((1,0,0)), _rot3.apply((0,1,0))), _rot3.apply((0,0,1))), "Still right-hand" - - + _rot = Rot.from_euler("XYZ", (90, 0, 0), degrees=True) # roll 90 deg + assert np.allclose(_rot.apply((0, 0, 1)), (0, -1, 0)), "z -> -y" + _rot2 = Rot.from_euler("XYZ", (90, 0, 0), degrees=True) * _rot # another 90 deg in same direction + assert np.allclose(_rot2.apply((0, 0, 1)), (0, 0, -1)), "z -> -z" + assert np.allclose(_rot2.apply((0, 0, 1)), Rot.from_euler("XYZ", (180, 0, 0), degrees=True).apply((0, 0, 1))), ( + "Angles added" + ) + _rot2 = _rot.from_euler("XYZ", (0, 90, 0), degrees=True) * _rot # + pitch 90 deg + print(_rot2.as_euler(seq="XYZ", degrees=True)) + print(Rot.from_euler("XYZ", (90, 90, 0), degrees=True).as_euler(seq="XYZ", degrees=True)) + _rot3 = Rot.from_euler("XYZ", (0, 0, 90), degrees=True) * _rot2 # +yaw 90 deg + assert np.allclose(_rot3.apply((1, 0, 0)), (0, 0, -1)) + assert np.allclose(_rot3.apply((0, 1, 0)), (0, 1, 0)) + assert np.allclose(_rot3.apply((0, 0, 1)), (1, 0, 0)) + assert np.allclose(np.cross(_rot3.apply((1, 0, 0)), _rot3.apply((0, 1, 0))), _rot3.apply((0, 0, 1))), ( + "Still right-hand" + ) + + def init_model_variables(): """Define model and a few variables for various tests""" mod = DummyModel("MyModel") @@ -335,9 +355,7 @@ def test_init(): assert mod.int1 == 99, "Value directly accessible as model variable" mod.int1 = 110 assert mod.int1 == 110, "Internal changes not range-checked!" - with pytest.raises(VariableRangeError) as err: # ... but getter() detects the range error - _ = int1.getter() - assert str(err.value) == "getter(): Value [110] outside range." + assert not int1.check_range(int1.getter(), -1), "Error detected during getter()" assert mod.int1 == 110, f"Value {mod.int1} should still be unchanged" int1.setter([50]) assert mod.int1 == 50, f"Value {mod.int1} changed back." @@ -362,9 +380,7 @@ def test_init(): assert mod.float1 == 0.99, "Value directly accessible as model variable" mod.float1 = 1.0 assert mod.float1 == 1.0, "Internal changes not range-checked!" - with pytest.raises(VariableRangeError) as err: # ... but getter() detects the range error - _ = float1.getter() - assert str(err.value) == "getter(): Value [100.0] outside range." + assert not float1.check_range(float1.getter(), -1), "Range check during getter detects the range errror" assert mod.float1 == 1.0, f"Value {mod.float1} should still be unchanged" float1.setter([50]) assert mod.float1 == 0.5, f"Value {mod.float1} changed back." @@ -458,9 +474,7 @@ def test_init(): assert mod.np1[1] == math.radians(2), "Value directly accessible as model variable" mod.np1[1] = -1.0 assert mod.np1[1] == -1.0, "Internal changes not range-checked!" - with pytest.raises(VariableRangeError) as err: # ... but getter() detects the range error - _ = np1.getter() - assert str(err.value) == "getter(): Value [1.0, -57.29577951308233, 3.0] outside range." + assert not np1.check_range(np1.getter(), 1), "Error detected during getter()" assert mod.np1[1] == -1.0, f"Value {mod.np1} should still be unchanged" mod.np1 = np.array((1.5, 2.5, 3.5), float) assert np.linalg.norm(mod.np1) == math.sqrt(1.5**2 + 2.5**2 + 3.5**2), "np calculations are done on value" @@ -488,7 +502,7 @@ def test_init(): ) assert err2.value.args[0] == "Variable int1 already used as index 0 in model MyModel" - with pytest.raises(VariableInitError) as err3: + with pytest.raises(KeyError) as err3: int1 = Variable( mod, "bool1", @@ -500,7 +514,7 @@ def test_init(): annotations=None, typ=int, ) - assert err3.value.args[0].startswith("Range must be specified for int variable") + assert err3.value.args[0].startswith("Variable bool1 already used") assert float1.range[0][1] == 99.0 assert enum1.range[0] == (0, 4) assert enum1.check_range([Causality.parameter]) @@ -511,24 +525,18 @@ def test_init(): def test_range(): """Test the various ways of providing a range for a variable""" mod = DummyModel("MyModel2", instance_name="MyModel2") - with pytest.raises(VariableInitError) as err: - int1 = Variable(mod, "int1", start=1) - assert ( - str(err.value) - == "Range must be specified for int variable or use float." - ) - int1 = Variable(mod, "int1", start=1, rng=(0, 5)) # that works - mod.int1 = 6 - with pytest.raises(VariableRangeError) as err2: # causes an error - _ = int1.getter() - assert err2.value.args[0] == "getter(): Value [6.0] outside range." + int1 = Variable(mod, "int1", start=1) + assert int1.range == ((1, 1),), "Missing range. restricted to fixed start value." + int2 = Variable(mod, "int2", start=1, rng=(0, 5)) + assert int2.range == ((0, 5),), "That works" + mod.int2 = 6 + assert not int2.check_range(int2.getter(), -1), "Error detected in getter() function" float1 = Variable(mod, "float1", start=1, typ=float) # explicit type assert float1.range == ((float("-inf"), float("inf")),), "Auto_extreme. Same as rng=()" float2 = Variable(mod, "float2", start=1.0, rng=None) # implicit type through start value and no range assert float2.range == ((1.0, 1.0),), "No range." - with pytest.raises(VariableRangeError) as err3: - float2.setter([2.0]) - assert err3.value.args[0] == "set(): values [2.0] outside range." + mod.float2 = 99.9 # type: ignore[reportAttributeAccessIssue] ## values are accessible through model + assert not float2.check_range(float2.getter(), -1), "Error detected in getter() function" np1 = Variable(mod, "np1", start=("1.0m", 2, 3), rng=((0, "3m"), None, tuple())) assert np1.range == ((0.0, 3.0), (2.0, 2.0), (float("-inf"), float("inf"))) @@ -685,9 +693,9 @@ def test_xml(): assert len(lst) == 3 expected = '' assert ET.tostring(lst[0], encoding="unicode") == expected, ET.tostring(lst[0], encoding="unicode") - expected = '' + expected = '' assert ET.tostring(lst[1], encoding="unicode") == expected, ET.tostring(lst[1], encoding="unicode") - expected = '' + expected = '' assert ET.tostring(lst[2], encoding="unicode") == expected, ET.tostring(lst[2], encoding="unicode") int1 = Variable( @@ -728,9 +736,29 @@ def test_on_set(): mod.dirty_do() arrays_equal(mod.np2, (0.9 * 0.9 * 4, 0.9 * 7, 0.9 * 8)) + def test_normalized(): - assert np.allclose( normalized( (1,0,0)), (1,0,0)) - assert np.allclose( normalized( (1,1,1)), np.array( (1,1,1), float)/np.sqrt(3)) + assert np.allclose(normalized(np.array((1, 0, 0), float)), (1, 0, 0)) + assert np.allclose(normalized(np.array((1, 1, 1), float)), np.array((1, 1, 1), float) / np.sqrt(3)) + + +def test_extremum(): + t = [np.radians(10 * x) for x in range(100)] + x = [np.cos(x) for x in t] + e, p = extremum(t[0:3], x[0:3], 2e-3) # allow a small error + assert e == 1 + assert p[0] > -2e-3 and p[1] < 1 + 1e-6, ( + "Top of parabola somewhat to the left due to cos not exactly equal to 2.order" + ) + # for i in range(100): + # print(i, t[i], x[i]) + e, p = extremum(t[17:20], x[17:20]) + assert e == -1 and abs(p[0] - np.pi) < 1e-10 and p[1] == -1 + ex = extremum_series(t, x, "all") + assert len(ex) == 2 + assert np.allclose(ex[0], (12.566370614359142, 1.0)) + assert np.allclose(ex[1], (15.707963267948958, -1.0)) + if __name__ == "__main__": retcode = pytest.main(["-rP -s -v", __file__]) @@ -753,3 +781,4 @@ def test_normalized(): # test_euler_rot_spherical() # test_euler_rot() # test_normalized() + # test_extremum() From dcad3f00e8fa0271c9a37f33c0b3ce65c45530f9 Mon Sep 17 00:00:00 2001 From: Claas Date: Tue, 2 Dec 2025 14:29:33 +0100 Subject: [PATCH 15/29] GitHub workflows: replaced --extra 'modelTest' by --extra 'tests' --- .github/workflows/_code_quality.yml | 2 +- .github/workflows/_test.yml | 2 +- .github/workflows/_test_future.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/_code_quality.yml b/.github/workflows/_code_quality.yml index a1df23e..8f21bdb 100644 --- a/.github/workflows/_code_quality.yml +++ b/.github/workflows/_code_quality.yml @@ -78,6 +78,6 @@ jobs: python-version: "3.12.8" python-version-file: "pyproject.toml" - name: Install the project - run: uv sync --upgrade --extra modelTest + run: uv sync --upgrade --extra tests - name: Run mypy run: uv run mypy diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml index b4e4612..de8b347 100644 --- a/.github/workflows/_test.yml +++ b/.github/workflows/_test.yml @@ -28,7 +28,7 @@ jobs: with: python-version: ${{ matrix.python.version }} - name: Install the project - run: uv sync --upgrade -p ${{ matrix.python.version }} --no-dev --extra modelTest + run: uv sync --upgrade -p ${{ matrix.python.version }} --no-dev --extra tests - name: Install pytest run: | uv pip install pytest diff --git a/.github/workflows/_test_future.yml b/.github/workflows/_test_future.yml index cbc6c0c..430ccee 100644 --- a/.github/workflows/_test_future.yml +++ b/.github/workflows/_test_future.yml @@ -28,7 +28,7 @@ jobs: with: python-version: ${{ matrix.python.version }} - name: Install the project - run: uv sync --upgrade -p ${{ matrix.python.uvpy }} --no-dev --extra modelTest + run: uv sync --upgrade -p ${{ matrix.python.uvpy }} --no-dev --extra tests - name: Install pytest run: | uv pip install pytest From 339c3c6e2bcfb23cecfb35d9645d8a829d6e6243 Mon Sep 17 00:00:00 2001 From: Claas Date: Tue, 2 Dec 2025 14:32:39 +0100 Subject: [PATCH 16/29] GitHub workflows: Change Python versions to test against to 3.11 - 3.13 --- .github/workflows/_test.yml | 2 +- .github/workflows/_test_future.yml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml index de8b347..9e2efc8 100644 --- a/.github/workflows/_test.yml +++ b/.github/workflows/_test.yml @@ -13,9 +13,9 @@ jobs: - runner: windows-latest # - runner: macos-latest python: - - version: '3.10' - version: '3.11' - version: '3.12' + - version: '3.13' steps: - uses: actions/checkout@v4 - name: Install uv diff --git a/.github/workflows/_test_future.yml b/.github/workflows/_test_future.yml index 430ccee..134d28d 100644 --- a/.github/workflows/_test_future.yml +++ b/.github/workflows/_test_future.yml @@ -1,10 +1,10 @@ -name: Unit Tests (py313) -# Test also with Python 3.13 (experimental; workflow will not fail on error.) +name: Unit Tests (py314) +# Test also with Python 3.14 (experimental; workflow will not fail on error.) on: workflow_call jobs: - test313: + test314: name: Test on ${{matrix.python.version}}-${{matrix.platform.runner}} (experimental) continue-on-error: true runs-on: ${{ matrix.platform.runner }} @@ -14,8 +14,8 @@ jobs: - runner: ubuntu-latest - runner: windows-latest python: - - version: '3.13.0-alpha - 3.13.0' - uvpy: '3.13' + - version: '3.14.0-alpha - 3.14.0' + uvpy: '3.14' steps: - uses: actions/checkout@v4 - name: Install uv From 10dc6e95495ad606addf9210210fcaaff7f94ba3 Mon Sep 17 00:00:00 2001 From: Eisinger Date: Tue, 2 Dec 2025 23:15:17 +0100 Subject: [PATCH 17/29] Updated version trying to fix problems with libcosimpy. test_oscillator_6dof_fmu.py still crashes on pytest (not when run manually) --- examples/DrivingForce.fmu | Bin 792775 -> 792775 bytes examples/HarmonicOscillator.fmu | Bin 792357 -> 792357 bytes tests/conftest.py | 27 ++++++++++++++++++++++++ tests/test_analysis.py | 34 ++++++++++++++++++++++++++++++ tests/test_oscillator_6dof_fmu.py | 24 ++++++++------------- tests/test_oscillator_fmu.py | 20 ++++++------------ 6 files changed, 76 insertions(+), 29 deletions(-) create mode 100644 tests/test_analysis.py diff --git a/examples/DrivingForce.fmu b/examples/DrivingForce.fmu index acd506733e385c2dd82c919924fb05325644fef3..2f741e2ccf627e9fe7fea4475a92a156a67d7356 100644 GIT binary patch delta 240 zcmX@U(ct(-1D*hHW)=|!5YXJYkw-|FNpokjf^fTnFynRwVWur>nY4DcUpv4A#LPg< z0>rF9%m&2lK+FNeoIuRA{n`QUL}Q@l>6Tx(?Llm1 znn6m6S*o$IiGiu1g=MONg{irbsgZH2v7t%g^vbW?u7ZZfR)z*vrUu#u237_JN(yDu z1!ppfPtR3j;@$rFEB7-Akm0xac)Wo0c6EN9JM16}gAIX#Oqx5Jg9Y1z1sS&o3o>n43*@(R?q>pGW*}w(Vpbq#17da{<^W<& zAm-Z6xt}}B7^rx9=_hV`5WVdaw>O98&Zg+l((XCaIX`nxQZPs}NHH)oGc++yNi;Dt zG_o``Ff%kT0kX}^jSSLErtkjD?J8hoWMyD!WoW5wU|?lnprkN4uts9~0u?6S?WSM2 log{%~Z?EL#xx)^!BwUo|4TyFX<8cAe-C{g`2umLVMFBrQN00yj delta 217 zcmZ3w&S2>}gAIX#Oc906!Gi6rF9%m&2lK+FNe zoIuRAopV2TmN8KC^wLk<_8@xOCvI71XrCn+QwB^#I-q*$1xn3$MY zni?e~nWk7KC0bZoq@`LUnWawO{h8ZUz|h#r(7?*nK-<8;%D_NLVRB%N#PkI!OuXAo pzi>NA0?poD$;)$x9q6*@;i5cmK$f|R@wkBKZZRG|gryIGq5$7JMcx1a diff --git a/tests/conftest.py b/tests/conftest.py index a4aaad1..c92f3ec 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,6 +5,33 @@ import pytest +from component_model.model import Model + + +@pytest.fixture(scope="session") +def oscillator_fmu(): + """Make FMU and return .fmu file with path.""" + build_path = Path(__file__).parent.parent / "examples" + src = Path(__file__).parent.parent / "examples" / "oscillator_fmu.py" + fmu_path = Model.build( + script=src, + dest=build_path, + ) + return fmu_path + + +@pytest.fixture(scope="session") +def driver_fmu(): + """Make FMU and return .fmu file with path.""" + build_path = Path(__file__).parent.parent / "examples" + src = Path(__file__).parent.parent / "examples" / "driving_force_fmu.py" + fmu_path = Model.build( + script=src, + dest=build_path, + newargs={"ampl": ("3N", "2N", "1N"), "freq": ("3Hz", "2Hz", "1Hz")}, + ) + return fmu_path + @pytest.fixture(scope="package", autouse=True) def chdir() -> None: diff --git a/tests/test_analysis.py b/tests/test_analysis.py new file mode 100644 index 0000000..34dffb1 --- /dev/null +++ b/tests/test_analysis.py @@ -0,0 +1,34 @@ +# pyright: ignore[reportAttributeAccessIssue] # PythonFMU generates variable value objects using setattr() +import logging + +import numpy as np +import pytest + +from component_model.utils.analysis import extremum, extremum_series + +logger = logging.getLogger(__name__) +logging.basicConfig(level=logging.INFO) + + +def test_extremum(): + t = [np.radians(10 * x) for x in range(100)] + x = [np.cos(x) for x in t] + e, p = extremum(t[0:3], x[0:3], 2e-3) # allow a small error + assert e == 1 + assert p[0] > -2e-3 and p[1] < 1 + 1e-6, ( + "Top of parabola somewhat to the left due to cos not exactly equal to 2.order" + ) + # for i in range(100): + # print(i, t[i], x[i]) + e, p = extremum(t[17:20], x[17:20]) + assert e == -1 and abs(p[0] - np.pi) < 1e-10 and p[1] == -1 + ex = extremum_series(t, x, "all") + assert len(ex) == 2 + assert np.allclose(ex[0], (12.566370614359142, 1.0)) + assert np.allclose(ex[1], (15.707963267948958, -1.0)) + + +if __name__ == "__main__": + retcode = pytest.main(["-rP -s -v", __file__]) + assert retcode == 0, f"Return code {retcode}" + # test_extremum() diff --git a/tests/test_oscillator_6dof_fmu.py b/tests/test_oscillator_6dof_fmu.py index c700aeb..5c86172 100644 --- a/tests/test_oscillator_6dof_fmu.py +++ b/tests/test_oscillator_6dof_fmu.py @@ -1,9 +1,11 @@ +# ruff: noqa: I001 import shutil import sys import xml.etree.ElementTree as ET # noqa: N817 from collections.abc import Iterable from pathlib import Path from typing import Any +from libcosimpy.CosimExecution import CosimExecution import matplotlib.pyplot as plt import numpy as np @@ -17,7 +19,7 @@ CosimVariableType, CosimVariableVariability, ) -from libcosimpy.CosimExecution import CosimExecution +#from libcosimpy.CosimExecution import CosimExecution from libcosimpy.CosimLogging import CosimLogLevel, log_output_level from libcosimpy.CosimManipulator import CosimManipulator from libcosimpy.CosimObserver import CosimObserver @@ -122,11 +124,6 @@ def do_show( # return np.array((0, 0, ampl * np.sin(omega * t)), dtype=float) -@pytest.fixture(scope="session") -def oscillator_fmu(): - return _oscillator_fmu() - - def _oscillator_fmu(): """Make FMU and return .fmu file with path.""" build_path = Path.cwd() @@ -147,11 +144,6 @@ def _oscillator_fmu(): return fmu_path -@pytest.fixture(scope="session") -def driver_fmu(): - return _driver_fmu() - - def _driver_fmu(): """Make FMU and return .fmu file with path.""" src = Path(__file__).parent.parent / "examples" / "driving_force_fmu.py" @@ -369,15 +361,17 @@ def _test_run_osp_sweep(system_structure: Path, show: bool = False, alg: str = " if __name__ == "__main__": - retcode = 0 # pytest.main(args=["-rA", "-v", __file__, "--show", "True"]) + retcode = pytest.main(args=["-rA", "-v", __file__, "--show", "True"]) assert retcode == 0, f"Non-zero return code {retcode}" import os os.chdir(Path(__file__).parent.absolute() / "test_working_directory") + drv = _driver_fmu() + osc = _oscillator_fmu() # test_system_structure_change(_system_structure()) - test_make_fmus(_oscillator_fmu(), _driver_fmu()) - # test_use_fmu(_oscillator_fmu(), _driver_fmu(), show=True) - # test_run_osp(_oscillator_fmu(), _driver_fmu()) + # test_make_fmus(osc, drv) + # test_use_fmu(osc, drv, show=True) + # test_run_osp(osc, drv) # test_run_osp_system_structure(_system_structure(), show=True) # _test_run_osp_sweep(_system_structure(), show=True, alg="fixedStep") # _test_run_osp_sweep( _system_structure(), show=True, alg='ecco') diff --git a/tests/test_oscillator_fmu.py b/tests/test_oscillator_fmu.py index a3d2c3d..408c240 100644 --- a/tests/test_oscillator_fmu.py +++ b/tests/test_oscillator_fmu.py @@ -142,11 +142,6 @@ def do_show(traces: dict[str, tuple[list[float], list[float]]]): # return np.array((0, 0, ampl * np.sin(omega * t)), dtype=float) -@pytest.fixture(scope="session") -def oscillator_fmu(): - return _oscillator_fmu() - - def _oscillator_fmu(): """Make FMU and return .fmu file with path.""" build_path = Path(__file__).parent.parent / "examples" @@ -158,11 +153,6 @@ def _oscillator_fmu(): return fmu_path -@pytest.fixture(scope="session") -def driver_fmu(): - return _driver_fmu() - - def _driver_fmu(): """Make FMU and return .fmu file with path.""" build_path = Path(__file__).parent.parent / "examples" @@ -436,10 +426,12 @@ def _test_run_osp_sweep(system_structure: Path, show: bool = False, alg: str = " os.chdir(Path(__file__).parent.absolute() / "test_working_directory") # test_system_structure_change(_system_structure()) - test_make_fmus(_oscillator_fmu(), _driver_fmu(), show=True) - # test_run_fmpy(_oscillator_fmu(), _driver_fmu(), show=True) - # test_run_fmpy2(_oscillator_fmu(), _driver_fmu(), show=True) - # test_run_osp(_oscillator_fmu(), _driver_fmu()) + osc = _oscillator_fmu() + drv = _driver_fmu() + test_make_fmus(osc, drv, show=True) + # test_run_fmpy(osc, drv, show=True) + # test_run_fmpy2(osc, drv, show=True) + # test_run_osp(osc, drv) # test_run_osp_system_structure(_system_structure(), show=True) # _test_run_osp_sweep(_system_structure(), show=True, alg="fixedStep") # _test_run_osp_sweep( _system_structure(), show=True, alg='ecco') From bb4c6b1fd2538122ede633113a76219db910d463 Mon Sep 17 00:00:00 2001 From: Eisinger Date: Thu, 4 Dec 2025 15:43:20 +0100 Subject: [PATCH 18/29] Updates in connection with finding pytest failures. This still fails until PythonFMU is fixed (where the root cause was found). --- examples/DrivingForce.fmu | Bin 792775 -> 793027 bytes examples/HarmonicOscillator.fmu | Bin 792357 -> 792556 bytes examples/HarmonicOscillator6D.fmu | Bin 796106 -> 800219 bytes examples/driving_force_fmu.py | 7 +- tests/conftest.py | 66 +++++++---- tests/test_oscillator_6dof_fmu.py | 184 ++++++++++++++---------------- tests/test_oscillator_fmu.py | 117 +++++++++---------- 7 files changed, 190 insertions(+), 184 deletions(-) diff --git a/examples/DrivingForce.fmu b/examples/DrivingForce.fmu index 2f741e2ccf627e9fe7fea4475a92a156a67d7356..e9109bc39655e8b5d48b769396fdb022b85ca15b 100644 GIT binary patch delta 1107 zcmZuxOKclO7~UBtX`G!nvDc4H)5q8qsgs~|9d8W;VI)cfX-a8DQ9wDAO}uV4Y`kXe zx>zmH&`2PWQnf4e7xmB!y`)@9L@ZS*ic|6EfdWNU#0_z%MHDKe#z++hn8`N0#7Lj! z|Gt0z$L##tFRv)q=N0+Du%{s?@b&hInbFlx?zgl`g3u~mkxz?aF5jowwpNbE6V74M z70BB9YxD{FAK6(Iz0M($m=BR`<9ykF33*tqQ&FiBmCyx{7LU)2p1t|_Gk4O0z_Cpb z;9Ec0Ub#DmehO?grt+3uG>x%7BUjQ)Bcm1d!{gfcgkFeuL+_`k|1HYal$AMZ>a6s4yxXi?|2HM>l+ zmdShtnz!|mVI3`)Wjb!_tdmSj3xz+fp`}jVMBX-X#dz$w=bwIY-(c+FjmnkkXXvGd z%I51R>w{-+p`j2pvaFe7nth}|b+e$;{Z>wg(syX7a_$y->aOS1iJ5nn7cql}p@E^1 zp^0G=Lo9-xCJb#Pqsxd`;Zj%;HDS%r3tHU$2R?R86X?notvogr=#A zvU}{YZ2zOXlgcBBF)h=d(xF@z_ekoYggTH+4JfHzMHx_(7%hSSLll9jF5FtVR~PTd zzJE^v)O;jOxpw>Aua1dqN;o$2ae1&7riUa)k+y)=Mtbn9M*EqJHwS=c75~{JOIf z&ihGZ`~S?*Mi;`ubPaC$$uRb=z&Cyph9`eUt&k3|R7YSjKsw;!CM>}ROu*lJp&B4V z__ECRFvKL**!!I>F(s11_x8Hvszj1_WS>h;v*h3lvIMZ-RSpD63iU!XL}a)aBr1B7 z+a2&rkcEA2xQBk9z#{Akv9OgqSRpck)z|qRgOw0b@WFE~Nrze8+Y2r^A7-ABs!LYG zBtrRfPhQOLe?L# zdgGzNjYpLKkzrxFyAF*q=~|z!KACjl$vO?wMa2CrtU`!O>wC~8*I>`en`F6=5x2Qx3-xpBocG* z*AS9x!83|Pn0|sLf)6Ip0clYF95pLpdJ2sxRuY4E3f147LtJnmf{GUy%eb#Q62?>v zm6R%pN=7B8QcxvRr352k_K-DN0oa$=irA!ciQT23``sBe$vqvz;YI}G2WhYDf993Gh;fWFG;-FGRs_}j;d<)?9FsLFTj^xC^Uzw|bpDJR(Gokou zm73Ua>3keOOkapV*+P5TJiUq zxFx_*U$-j`NJpW6cN}VU#Ey%bqhNr8EjR@Rb#$XIW^tiOc%vgGua`iNE&-2lq@wC3~$TG{_%ePZ(MN diff --git a/examples/HarmonicOscillator.fmu b/examples/HarmonicOscillator.fmu index c8c186ae5b74d9c13f9ba56603c13434aceb4ee0..f835da2c0b0e37099a306691ebb8b28c377cae57 100644 GIT binary patch delta 816 zcmY+BTSyd97{_;xmvx=9wikE25goS@wa{^IUNRw3N?8*@L6DN!oijQ#?%3?OVfs*M z_~L^Vb{=}^CCDC3P#OqvwaY^UW%SZ(1ZEMDt}jIoopF-rz>jnO|L^L$X9)az)UFk_anFjLxeGOcvfU&&bXS<262P zGPU2$Z5wLU463_#Kps&*u9bi7XZ{CdpNESV`zDlyPet2LQ zO#T(8y;M9OvLZD@fF<)@Sj}vcbua7(ffU*BLO*FPggoe;v&38>ZTWu15?{?oU)mBa zK3G(4{^Y>Y>;3|p&1OSi}d(?U}tL=1wHO}a4^s}>L1AlQy^4)U z1+hji4^jL$D4Ij7SY4DhDS}sRQ18}@7ZFVgB2;lEQx87)vG4b1-adBc>pXif$DRyn zjvX&c7jv4O)5tCKl_yK16Y9f(NWY>)?Da>)t>dvpqT}vG(uw6~#EBmkNzI;HA}88x z=W8(_P3hzO?A-2lhKi>E4Oi_sosdN0?RYYN~DtcQzGU@6{PX_FOj zdQCm(HFKuf$1;71jQws+JsTD;vk$bx4E_Hz@#7Xa>Dd2;;HPmKz(Ebt^o|qn>k!17 z8tkK9x0LT1B6;EI)i4FT?uR&yACk#?KR-KrSW36fZZ;rJ*72SW9XM=2if*06RRf|}F`$=DObHpLm#@k2YMJjG3-Fz=Ad{y7 zV05-B#TkT9>-R;1(B=4DrMOz7op?M5amPT7Vr-elup;czV;2@HuI>0I2+$hwh4@IZ zq2iII8s2#{OQ@k3iYRdnUE50CKHdwWBHXAK)0GCE3jyo-cUix}iXxD+^4JCPvw_PY Kh`FLZ;L%@<1I&W} diff --git a/examples/HarmonicOscillator6D.fmu b/examples/HarmonicOscillator6D.fmu index cb527deea5a656e77752d0038342e5e0075cb9de..b0fd490e06d324e98deed48f0fdd8d4c92a077e0 100644 GIT binary patch delta 7291 zcmb_BYj_*gmAWJOC5;k-AE{sQwQR>DTee5C6N+M+n3C{JV%UYH5venl$JP)@BhQR& zDI{y!!cs~MK);p0k?uX`-1EBUoO|Z_$3GE&dtNN>zSU|gve1ur%L9SC?aw_c7h5d!!Q@*N zd<_|Na)tJFF+)+KQrKx9>wG4Uy!tgRH+ea)p|jq*wk1;U!E^jJL|Qbd&&16!c6)Fz z`C*=Wv|eoXu5HjwHdh=+fGW+B(akpp))7DM537=aw_x4Sa0e*ZEN&VAZ6b;v8$+?M z9AE^VWPX16EfkA44Kzd=8hlLS`KQ3#P{Xn&MT2riNC)=HP2w^Sj*HZ!Yr!Oud1a2@ zNdv^}5%3*mtGn6i7MEZ!tRd$q3Goz z=X);=1}^El9y|S%#d3Q5ko%2AIezLS($$1Y$9JwcXD`6|P)ybYcXM)gYw7rRR+DEI z{3ZI8z!#jywi`z*7UKv}ClDsLokaP`!9N}uA9#B=O8&-EJHGM4nLD~9U6(ZjhvlfC z%i;d!9^H^M!;L$!=)ta4k(iM%cReJ?^xJhh&c#Bt$xe7AH5B*-}?+L}#t3cY{c`ak@y}xfj)C z7B~uYP@lyNr7t8qL#T=q-Q>C`a*=mKC=ZF`Q3c@~VHmcO{6azA)OrO4oaF1B$eDa~ zk&}!Lf-*lIM#mN=w`~quJtn@Z3(P1g9^ht)?6O@7>Sr^=-@Yl#UN{x=FeM4axB7KTplOYBAgfz;H zYj9KsCmSm6Q?U_(?0}&(tSSHt$+%yM_Q@KBg;jMER-)LDsccD0;C?j>I333{d00`0 z^l$)+6i7RNLtY6z}1>u$L7wpA`jG^UU{b?Eo#Zd)q%E!4k&v|U0s z7h|O#6k+j+<*+W}wQ5u*y?;VKAQ!D%VQSkYq?Onnhk#4vIl1*q%%R-Kg^)S?g@=26 zfjK;|f;-EZ!*6tQQj0mf7~wV|+cwJKPL*rt%;EMy?(tGaK-`aT>rB!uk8oc|vpw|< zt~!qe`r&6JAIh^>P#)R#61TKmg36cB6}>r@Fue;N8W>Qb15SJL(NiT!4tW;z$^BT4 z8=4#$iU@IXJ`Yu*1p6tLWKEkY6_C?zztc|6@Thb&rm4e9A8d-y>c%k@JgDQK8Z{*F zz7(Bg*lb4j9kMZ`MX`v5s5*&*bZ_!vA*_y=3}6mZLye`Uz{lKQ#@>Tf4GW@2bW@%* za$GK#$$eVh#@)E11GloJG>;SJ9OB=#DlP>L*nT6V$#OAA|bRP#|V`=r;A< zY;q4PuuB8Yx*8sq1<8XGX*5KeAoc3PX5i|^?X*;Tmodwv-T?~sO@ZCiJ0`IS%`C(X z<~p|@iV7Z`0jT(`S5Yz@d zqN(&-F+!ALzzFGB?Z*Zd?xPC~_f|KH!UPrqCWo!=biP0!m^J`&aL&rOEA1Sr*a&!R z#A~V|hqFnQ=q$<}Af@5=IkryB7@^5J@CiWlB`5AK8wEp}us5>@R16%BF`&rAnqUMJ zN`n1ZftaERnV11d=NnxNTFJP&11c3QJ|lw6{N!b`7>&wy4E#1sS_MZn{eV)VjR8EfXMpbUsB|`%pR&?4&-=P{FGwNVnx`#HgONf6zs%*!C22o>{AH|U27@%FO#F%-oJS<0P3d9ncG7vK0m4klMqOb|; zD!I516_yF$Q|5TO9Y&0vO&;jN0Y=LAqpC)*+epN~3S}UwYH~o6X&2aK7@E>MWXN5? zpsedR>-vzqf&ATmw8{#}NhbEA3UXpUDzZX~(%G9hec+ji$m-a%V^9Pq{qVbJBUyR? zt+&VNDNG{+ldm5@tESI?eE{JKv#x8Z(L*z+M~^|bu0ZQeeaebEX?_mn!+XK<=g>3c z-=E@&$;Ic;Yc^UU$%_Y3y+g?C6GX^=97IdIO#mEU@H$2t0>Fa~=~Q6_cZN_9WvCj< z9j~9pZM2Vd6)c#2elqwx+ExNY-gGXeYc(~TlpR9Dc3Q(g2)v9CatN(To~pg6X^j++U9KLK-Q!o>|6JY@S(w3QT&p_=RPFf)TG&=aIB4*jEN=44p(3`Lc} zA(^U|y;E{bC)ya&$@gDEi^-qIP%CM90hJYke(4Some4Kx$xw{lMhb;JoeOsdmX>OiM~9OdoMUT z+2G&>$1O#`F!1r`zV~07TP+s)&?}Ur_vs>1`HsWDt-))8*DmjEZCmdBe4E$TD)n8n z?AkW3*y;-|lUtWBBV+G4Ryuu6V%y((+x)Gq{?_HGkKS>t&mG;Qz^jEOMPM6s8Qui? zSbOa1v=>*2Wp-wyZu%cbx3y$4Au<}Wj38hB(BUKh z{J!JZ6$yUw!6y*Ba@ldrGb!QyuwHJ|M@ct+I~ClBj5as&0Fv-5Twpi-2nAqN?q_t$n628fLat9*blckuM~Fkei5I)Us>-M1pHi1h^)3%$$? z9ed((6)d1nb=JDzh4i{cC+GCcT=)EoH{4Q(KyP^5S?gZ2lh>;F67p~(wAw2$&zqN< zw_?5wY!jnyE6-nAa;{Qe(ufIWRGvqX%oHAxR-Z=`X%Deha7xaTRZDr~O% z7Z<;{nnudh)vv|Um?h!{ouk1~&Mp}8GgrdooC^Zy(aqCvn3m3@;pn+^R>Wx`}-unj$g_E-Z}vM q)B9v?9WT1(>VGo~??*KsI9NAa(tT@AE(K%wyHsGY{JfEe+5Z9eW0_q5 delta 3520 zcmZ`*4R9036~0{=|Hxd)%yj;0lEr=Qz1wqz9^boRxCt~jz$ zd#F9$oNg--1v9ab8A6hlC{M^8UtA%U*uKk?P6uh}@Jd0lh6I9sQbm~YJu}kjW*%p$ z9?R|<3VJ(Se#z71Qaw;xm$l+PjUJI1>%<4cA>H92)AtQoEbXf#|B8Tv%=4^{cpH6E zTc4=8-rY7jl6fp6`QJ+#owe2a3*)^UH{KiiCX>Mb1U`PW&oZ1EOB`$^qYag}wef+K zK9}EQE`iiKZPsXaO>bZ7d8x&DrT$dT-am01lj$L(%Y_?OJ~32tbzp9%6l^8#PM_Bw zBvO+%AP^}idF%oS<7U?izsp5Qcfjpw5r|)Ec1s@mO+V>Rmp=$yYjDsoT}$v^S7a;y0TG za&HLqJ8(_l#?S7T8q zdjlN{*RvUJ?#4yrNmtiO7p>7XOTEEJEm2d)Js#%m!6de5*|YUU0<`YIPw1C>y&YAH zB$`Guu~y$xlrQbUk7!be-?v&ICe^I~>*pXn)c4V#3!t|TAAxoIurb9X3SuNZY(pGy zXdf1FRTW&`hwZT}c$)*}twP55ppPb$60r1_*i`HBl|)L)M1npw??I|UN7zz>KDz7( z-$QACiRANjtTqu-AZVdF;XU&?Z7dIzf!Y0dYHk7XdxKKYMd)slCR$3a0Iga!YtZY5 z4gGi_vcfn0I0r8E;|x^H@ZSuT^fcQcRou{kSiNbu36dfd9-A zcI^Nz(vonaV9f$@-^PV&m2b(jqQgbgA`;zCet!NUw+9B^#yweKcag1-0s$9m7&K1=9Pi-nI4fO9BzJnL$ zD#q@XutH-~ad@nfwM(1mfYJe6ncLkd^$3lDm7R1`O;%f^icLjU+GmR3l>uBt^L=mt z*J_z2Q)(9=X%N?%tc)<1vfpp}HeHOz}RI)=&)HBX|*=hmF(MIjT-ycSo=g!`v_@ z^JCdb@wsU_kxqX7AU=%ZiPGr=dC4d~8zbkBVLgJ_5(!

yb|_{e`Sf!~}(Pso?f zp`Ynuo_p?Mz1%f{>WvCKqs6UAfpd6#Hd%q`27Cc4@T*)bl`7Er7(IOoJn=ZLj40f# z#hbH}ncqsiUeeKff{ufmZP)-~ZP=oy|FaD*h*(-Xa9*6;`vT6LBF{dGztC!Kbhm-^ zB(8~RpguPCoWxW#RAS_wlauCMRp{}d*p1^uaa7`|Bv8@H<3ox6c$ob=L*K2$>M*!? zEAi1}>d@JKZb`7Nl;i43Vb1Nu*NEM2Ti(*r+#=3unOQE*m^I5)Ubeh!_Kevxo0gZC z&zb>OZYR#w+33Njvz0k2DjfFN#iHmC#eC8Ydv_we{PdkfN347o^K&(jV&<#hr?I@O zpqJxRv^HKvZzQOwU#p_AM1EYcIKr#wP?Cy%m8_xy>nr&l}{%R+_Y3X`aZJWb_KQwd=9&LRcJDP zjY4@>3*bN7krwJocnf~!G^{J(%kmyLcu`JvoJNo;@|kd@gx3*P`>luHxHHwraZK`} zbnozxBJw(@DCM)msso39^yqX%Z-7+HQL4`rE2=*!<@3U-lN%lpcPDckliVoP_XS0@ zk;e%_?P7#8IOUSgg8BM-?TG`J3J)gnIyzf`R_@`-#__^`qzMqlhy z=(Uf1kN?5y}s8BX`DXMigbz+~5PHgyv zLigEty%NtjqdgJ&#ywhVr}W`Xigu-)*TaCFH){SG!XO239$dHcX;5EAsat~)YF8OA zYF3X$zHXNB`f&5nmh%S9WlC3_#8aY@$vTqurSm54s)l1y6tx2TQSg-W6?sf+QBC&g zy$?^~n3$rpR_%+0KMcj1;q!8y57+#E>3q0h0 None: + """ + Fixture that instantiates a CosimExecution object for the entire package. + This fixture is automatically used for the entire package. + """ -@pytest.fixture(scope="session") -def driver_fmu(): - """Make FMU and return .fmu file with path.""" - build_path = Path(__file__).parent.parent / "examples" - src = Path(__file__).parent.parent / "examples" / "driving_force_fmu.py" - fmu_path = Model.build( - script=src, - dest=build_path, - newargs={"ampl": ("3N", "2N", "1N"), "freq": ("3Hz", "2Hz", "1Hz")}, - ) - return fmu_path + from libcosimpy.CosimExecution import CosimExecution + + _ = CosimExecution.from_step_size(1) + return @pytest.fixture(scope="package", autouse=True) diff --git a/tests/test_oscillator_6dof_fmu.py b/tests/test_oscillator_6dof_fmu.py index 5c86172..2abd24a 100644 --- a/tests/test_oscillator_6dof_fmu.py +++ b/tests/test_oscillator_6dof_fmu.py @@ -1,7 +1,4 @@ # ruff: noqa: I001 -import shutil -import sys -import xml.etree.ElementTree as ET # noqa: N817 from collections.abc import Iterable from pathlib import Path from typing import Any @@ -19,14 +16,66 @@ CosimVariableType, CosimVariableVariability, ) -#from libcosimpy.CosimExecution import CosimExecution + +# from libcosimpy.CosimExecution import CosimExecution from libcosimpy.CosimLogging import CosimLogLevel, log_output_level from libcosimpy.CosimManipulator import CosimManipulator from libcosimpy.CosimObserver import CosimObserver from libcosimpy.CosimSlave import CosimLocalSlave from component_model.model import Model -from component_model.utils.xml import read_xml +from tests.conftest import system_structure_change + + +@pytest.fixture(scope="module") +def oscillator_6d_fmu(): + return _oscillator_6d_fmu() + + +def _oscillator_6d_fmu(): + """Make FMU and return .fmu file with path.""" + src = Path(__file__).parent.parent / "examples" / "oscillator_6dof_fmu.py" + assert src.exists(), f"Model file {src} not found." + fmu_path = Model.build( + script=str(src), + dest=Path(__file__).parent.parent / "examples" / "HarmonicOscillator6D.fmu", + newargs={ + "k": ("1N/m", "1N/m", "1N/m", "1N*m/rad", "1N*m/rad", "1N*m/rad"), + "c": ("0.1N*s/m", "0.1N*s/m", "0.1N*s/m", "0.1N*m*s/rad", "0.1N*m*s/rad", "0.1N*m*s/rad"), + "m": "1.0kg", + "mi": ("1.0 kg*m**2", "1.0 kg*m**2", "1.0 kg*m**2"), + "x0": ("0.0m", "0.0m", "0.0m", "0.0rad", "0.0rad", "0.0rad"), + "v0": ("0.0m/s", "0.0m/s", "0.0m/s", "0.0rad/s", "0.0rad/s", "0.0rad/s"), + }, + ) + return fmu_path + + +@pytest.fixture(scope="module") +def driver_6d_fmu(): + return _driver_6d_fmu() + + +def _driver_6d_fmu(): + """Make FMU and return .fmu file with path.""" + src = Path(__file__).parent.parent / "examples" / "driving_force_fmu.py" + assert src.exists(), f"Model file {src} not found." + fmu_path = Model.build( + script=str(src), + dest=Path(__file__).parent.parent / "examples" / "DrivingForce6D.fmu", + newargs={"ampl": ("1.0N", "1.0N", "1.0N", "1.0N*m", "1.0N*m", "1.0N*m"), "freq": ("1.0Hz",) * 6}, + ) + return fmu_path + + +@pytest.fixture(scope="session") +def system_structure(): + return _system_structure() + + +def _system_structure(): + """Make a OSP structure file and return the path""" + return Path(__file__).parent.parent / "examples" / "ForcedOscillator6D.xml" def _slave_index(simulator: CosimExecution, name: str): @@ -68,29 +117,6 @@ def _var_list(simulator: CosimExecution, slave_name: str, ret: str = "print"): } -def system_structure_change(structure_file: Path, tag: str, what: str, newval: str): - def register_all_namespaces(filename): - namespaces: dict = {} - for _, (ns, uri) in ET.iterparse(filename, events=["start-ns"]): - # print("TYPES", ns, type(ns), uri, type(uri)) - namespaces.update({ns: uri}) - ET.register_namespace(str(ns), str(uri)) - # namespaces: dict = dict([node )]) - # for ns in namespaces: - # ET.register_namespace(ns, namespaces[ns]) - return namespaces - - nss = register_all_namespaces(structure_file) - el = read_xml(structure_file) - elements = el.findall(f"ns:{tag}", {"ns": nss[""]}) - for e in elements: - if what == "text": - e.text = newval - else: # assume attribute name - e.attrib[what] = newval - ET.ElementTree(el).write(structure_file.name, encoding="utf-8") - - def arrays_equal( res: Iterable[Any], expected: Iterable[Any], @@ -124,61 +150,19 @@ def do_show( # return np.array((0, 0, ampl * np.sin(omega * t)), dtype=float) -def _oscillator_fmu(): - """Make FMU and return .fmu file with path.""" - build_path = Path.cwd() - build_path.mkdir(exist_ok=True) - src = Path(__file__).parent.parent / "examples" / "oscillator_6dof_fmu.py" - fmu_path = Model.build( - script=str(src), - dest=build_path, - newargs={ - "k": ("1N/m", "1N/m", "1N/m", "1N*m/rad", "1N*m/rad", "1N*m/rad"), - "c": ("0.1N*s/m", "0.1N*s/m", "0.1N*s/m", "0.1N*m*s/rad", "0.1N*m*s/rad", "0.1N*m*s/rad"), - "m": "1.0kg", - "mi": ("1.0 kg*m**2", "1.0 kg*m**2", "1.0 kg*m**2"), - "x0": ("0.0m", "0.0m", "0.0m", "0.0rad", "0.0rad", "0.0rad"), - "v0": ("0.0m/s", "0.0m/s", "0.0m/s", "0.0rad/s", "0.0rad/s", "0.0rad/s"), - }, - ) - return fmu_path - - -def _driver_fmu(): - """Make FMU and return .fmu file with path.""" - src = Path(__file__).parent.parent / "examples" / "driving_force_fmu.py" - fmu_path = Model.build( - script=src, - dest=Path.cwd() / "DrivingForce6D.fmu", - newargs={"ampl": ("1.0N", "1.0N", "1.0N", "1.0N*m", "1.0N*m", "1.0N*m"), "freq": ("1.0Hz",) * 6}, - ) - return fmu_path - - -@pytest.fixture(scope="session") -def system_structure(): - return _system_structure() - - -def _system_structure(): - """Make a OSP structure file and return the path""" - shutil.copy(Path(__file__).parent.parent / "examples" / "ForcedOscillator6D.xml", Path.cwd()) - return Path.cwd() / "ForcedOscillator6D.xml" - - def test_make_fmus( - oscillator_fmu: Path, - driver_fmu: Path, + oscillator_6d_fmu: Path, + driver_6d_fmu: Path, ): - info = fmu_info(filename=str(oscillator_fmu)) # this is a formatted string. Not easy to check + info = fmu_info(filename=str(oscillator_6d_fmu)) # this is a formatted string. Not easy to check print(f"Info Oscillator: {info}") - val = validate_fmu(filename=str(oscillator_fmu)) - assert not len(val), f"Validation of of {oscillator_fmu.name} was not successful. Errors: {val}" + val = validate_fmu(filename=str(oscillator_6d_fmu)) + assert not len(val), f"Validation of of {oscillator_6d_fmu.name} was not successful. Errors: {val}" - info = fmu_info(filename=str(driver_fmu)) # this is a formatted string. Not easy to check + info = fmu_info(filename=str(driver_6d_fmu)) # this is a formatted string. Not easy to check print(f"Info Driver: {info}") - val = validate_fmu(filename=str(driver_fmu)) - assert not len(val), f"Validation of of {driver_fmu.name} was not successful. Errors: {val}" + val = validate_fmu(filename=str(driver_6d_fmu)) + assert not len(val), f"Validation of of {driver_6d_fmu.name} was not successful. Errors: {val}" # def test_make_system_structure(system_structure: Path): @@ -195,11 +179,14 @@ def test_make_fmus( # -def test_use_fmu(oscillator_fmu: Path, driver_fmu: Path, show: bool = False): +pytest.mark.skip() + + +def test_use_fmu(oscillator_6d_fmu: Path, show: bool = False): # , driver_6d_fmu: Path, show: bool = False): """Test single FMUs.""" # sourcery skip: move-assign result = simulate_fmu( - oscillator_fmu, + oscillator_6d_fmu, stop_time=50, step_size=0.01, validate=True, @@ -207,22 +194,21 @@ def test_use_fmu(oscillator_fmu: Path, driver_fmu: Path, show: bool = False): debug_logging=True, logger=print, # fmi_call_logger=print, start_values={"x[2]": 1.0, "c[2]": 0.1}, - step_finished=None, # pyright: ignore[reportArgumentType] # (typing incorrect in fmpy) - fmu_instance=None, # pyright: ignore[reportArgumentType] # (typing incorrect in fmpy) ) if show: plot_result(result) -def test_run_osp(oscillator_fmu: Path, driver_fmu: Path): +# @pytest.mark.skip() +def test_run_osp(oscillator_6d_fmu: Path, driver_6d_fmu: Path): # sourcery skip: extract-duplicate-method sim = CosimExecution.from_step_size(step_size=1e8) # empty execution object with fixed time step in nanos - osc = CosimLocalSlave(fmu_path=str(oscillator_fmu), instance_name="osc") + osc = CosimLocalSlave(fmu_path=str(oscillator_6d_fmu), instance_name="osc") _osc = sim.add_local_slave(osc) assert _osc == 0, f"local slave number {_osc}" reference_dict = {var_ref.name.decode(): var_ref.reference for var_ref in sim.slave_variables(_osc)} - dri = CosimLocalSlave(fmu_path=str(driver_fmu), instance_name="dri") + dri = CosimLocalSlave(fmu_path=str(driver_6d_fmu), instance_name="dri") _dri = sim.add_local_slave(dri) assert _dri == 1, f"local slave number {_dri}" @@ -233,12 +219,11 @@ def test_run_osp(oscillator_fmu: Path, driver_fmu: Path): sim_status = sim.status() assert sim_status.current_time == 0 assert CosimExecutionState(sim_status.state) == CosimExecutionState.STOPPED - # Simulate for 1 second - _ = sim.simulate_until(target_time=15e9) + _ = sim.simulate_until(target_time=1.5e9) -@pytest.mark.skipif(sys.platform.startswith("linux"), reason="HarmonicOsciallatorFMU.fmu throws an error on Linux") +# @pytest.mark.skipif(sys.platform.startswith("linux"), reason="HarmonicOsciallatorFMU.fmu throws an error on Linux") def test_run_osp_system_structure(system_structure: Path, show: bool = False): "Run an OSP simulation in the same way as the SimulatorInterface of sim-explorer is implemented" log_output_level(CosimLogLevel.TRACE) @@ -294,10 +279,6 @@ def test_run_osp_system_structure(system_structure: Path, show: bool = False): print(f"@{t}, {p}, {v}, {np.sin(t)}") -def test_system_structure_change(system_structure): - system_structure_change(system_structure, "BaseStepSize", "text", str(0.99)) - - @pytest.mark.parametrize("alg", ["fixedStep", "ecco"]) def test_run_osp_sweep(system_structure: Path, alg: str, show: bool = False): _test_run_osp_sweep(system_structure, show, alg) @@ -305,16 +286,18 @@ def test_run_osp_sweep(system_structure: Path, alg: str, show: bool = False): def _test_run_osp_sweep(system_structure: Path, show: bool = False, alg: str = "fixedStep"): "Run an OSP simulation of the oscillator and the force sweep as co-simulation." - dt = 1.0 t_end = 100.0 log_output_level(CosimLogLevel.TRACE) - system_structure_change(system_structure, "BaseStepSize", "text", str(dt)) - system_structure_change(system_structure, "Algorithm", "text", alg) - print(f"Running Algorithm {alg} on {system_structure}") - assert system_structure.exists(), f"File {system_structure} not found" - sim = CosimExecution.from_osp_config_file(str(system_structure)) + structure = system_structure_change( + system_structure, + {"BaseStepSize": ("text", str(dt)), "Algorithm": ("text", alg)}, + f"{system_structure.stem}_{alg}.xml", + ) + print(f"Running Algorithm {alg} on {structure}") + assert structure.exists(), f"File {structure} not found" + sim = CosimExecution.from_osp_config_file(str(structure)) sim_status = sim.status() assert sim_status.error_code == 0 # manipulator = CosimManipulator.create_override() @@ -366,9 +349,8 @@ def _test_run_osp_sweep(system_structure: Path, show: bool = False, alg: str = " import os os.chdir(Path(__file__).parent.absolute() / "test_working_directory") - drv = _driver_fmu() - osc = _oscillator_fmu() - # test_system_structure_change(_system_structure()) + drv = _driver_6d_fmu() + osc = _oscillator_6d_fmu() # test_make_fmus(osc, drv) # test_use_fmu(osc, drv, show=True) # test_run_osp(osc, drv) diff --git a/tests/test_oscillator_fmu.py b/tests/test_oscillator_fmu.py index 408c240..8ae3ed5 100644 --- a/tests/test_oscillator_fmu.py +++ b/tests/test_oscillator_fmu.py @@ -1,7 +1,5 @@ # ruff: noqa: I001 -import shutil import sys -import xml.etree.ElementTree as ET # noqa: N817 from collections.abc import Iterable from pathlib import Path from typing import Any @@ -26,6 +24,36 @@ from component_model.model import Model from component_model.utils.xml import read_xml +from tests.conftest import system_structure_change + + +@pytest.fixture(scope="module") +def oscillator_fmu(): + return _oscillator_fmu() + + +def _oscillator_fmu(): + """Make FMU and return .fmu file with path.""" + fmu_path = Model.build( + script=Path(__file__).parent.parent / "examples" / "oscillator_fmu.py", + dest=Path(__file__).parent.parent / "examples" / "HarmonicOscillator.fmu", + ) + return fmu_path + + +@pytest.fixture(scope="module") +def driver_fmu(): + return _driver_fmu() + + +def _driver_fmu(): + """Make FMU and return .fmu file with path.""" + fmu_path = Model.build( + script=Path(__file__).parent.parent / "examples" / "driving_force_fmu.py", + dest=Path(__file__).parent.parent / "examples" / "DrivingForce.fmu", + newargs={"ampl": ("3N", "2N", "1N"), "freq": ("3Hz", "2Hz", "1Hz")}, + ) + return fmu_path def _slave_index(simulator: CosimExecution, name: str): @@ -94,29 +122,6 @@ def _var_list(simulator: CosimExecution, slave_name: str, ret: str = "print"): } -def system_structure_change(structure_file: Path, tag: str, what: str, newval: str): - def register_all_namespaces(filename): - namespaces: dict = {} - for _, (ns, uri) in ET.iterparse(filename, events=["start-ns"]): - # print("TYPES", ns, type(ns), uri, type(uri)) - namespaces.update({ns: uri}) - ET.register_namespace(str(ns), str(uri)) - # namespaces: dict = dict([node )]) - # for ns in namespaces: - # ET.register_namespace(ns, namespaces[ns]) - return namespaces - - nss = register_all_namespaces(structure_file) - el = read_xml(structure_file) - elements = el.findall(f"ns:{tag}", {"ns": nss[""]}) - for e in elements: - if what == "text": - e.text = newval - else: # assume attribute name - e.attrib[what] = newval - ET.ElementTree(el).write(structure_file.name, encoding="utf-8") - - def arrays_equal( res: Iterable[Any], expected: Iterable[Any], @@ -142,29 +147,6 @@ def do_show(traces: dict[str, tuple[list[float], list[float]]]): # return np.array((0, 0, ampl * np.sin(omega * t)), dtype=float) -def _oscillator_fmu(): - """Make FMU and return .fmu file with path.""" - build_path = Path(__file__).parent.parent / "examples" - src = Path(__file__).parent.parent / "examples" / "oscillator_fmu.py" - fmu_path = Model.build( - script=src, - dest=build_path, - ) - return fmu_path - - -def _driver_fmu(): - """Make FMU and return .fmu file with path.""" - build_path = Path(__file__).parent.parent / "examples" - src = Path(__file__).parent.parent / "examples" / "driving_force_fmu.py" - fmu_path = Model.build( - script=src, - dest=build_path, - newargs={"ampl": ("3N", "2N", "1N"), "freq": ("3Hz", "2Hz", "1Hz")}, - ) - return fmu_path - - @pytest.fixture(scope="session") def system_structure(): return _system_structure() @@ -172,8 +154,7 @@ def system_structure(): def _system_structure(): """Make a OSP structure file and return the path""" - shutil.copy(Path(__file__).parent.parent / "examples" / "ForcedOscillator.xml", Path.cwd()) - return Path.cwd() / "ForcedOscillator.xml" + return Path(__file__).parent.parent / "examples" / "ForcedOscillator.xml" def test_make_fmus( @@ -354,7 +335,24 @@ def test_run_osp_system_structure(system_structure: Path, show: bool = False): def test_system_structure_change(system_structure): - system_structure_change(system_structure, "BaseStepSize", "text", str(0.99)) + path = system_structure_change( + system_structure, + { + "BaseStepSize": ("text", str(0.99)), + "Algorithm": ("text", "ecco"), + "VariableConnection": ("powerBond", "myBond"), + }, + "changed_structure.xml", + ) + assert str(path) == str(Path(__file__).parent.parent / "examples" / "changed_structure.xml") + assert path.exists() + el = read_xml(path) + elements = el.findall("{*}BaseStepSize") + assert elements[0].text == "0.99", f"Found {elements[0].text}" + elements = el.findall("{*}Algorithm") + assert elements[0].text == "ecco", f"Found {elements[0].text}" + elements = el.findall(".//{*}VariableConnection") + assert elements[0].attrib["powerBond"] == "myBond", f"Found {elements[0].attrib}" @pytest.mark.parametrize("alg", ["fixedStep", "ecco"]) @@ -369,11 +367,14 @@ def _test_run_osp_sweep(system_structure: Path, show: bool = False, alg: str = " t_end = 100.0 log_output_level(CosimLogLevel.TRACE) - system_structure_change(system_structure, "BaseStepSize", "text", str(dt)) - system_structure_change(system_structure, "Algorithm", "text", alg) - print(f"Running Algorithm {alg} on {system_structure}") - assert system_structure.exists(), f"File {system_structure} not found" - sim = CosimExecution.from_osp_config_file(str(system_structure)) + structure = system_structure_change( + system_structure, + {"BaseStepSize": ("text", str(dt)), "Algorithm": ("text", alg)}, + f"{system_structure.stem}_{alg}.xml", + ) + print(f"Running Algorithm {alg} on {structure}") + assert structure.exists(), f"File {structure} not found" + sim = CosimExecution.from_osp_config_file(str(structure)) sim_status = sim.status() assert sim_status.error_code == 0 # manipulator = CosimManipulator.create_override() @@ -425,10 +426,10 @@ def _test_run_osp_sweep(system_structure: Path, show: bool = False, alg: str = " import os os.chdir(Path(__file__).parent.absolute() / "test_working_directory") - # test_system_structure_change(_system_structure()) osc = _oscillator_fmu() drv = _driver_fmu() - test_make_fmus(osc, drv, show=True) + test_system_structure_change(_system_structure()) + # test_make_fmus(osc, drv, show=True) # test_run_fmpy(osc, drv, show=True) # test_run_fmpy2(osc, drv, show=True) # test_run_osp(osc, drv) From ed53c06ba29dcb29f328c8d53e42308b2ca1f9f6 Mon Sep 17 00:00:00 2001 From: Eisinger Date: Fri, 12 Dec 2025 10:20:04 +0100 Subject: [PATCH 19/29] Added the controls module and temporarily removed usage of libcosimpy --- examples/DrivingForce.fmu | Bin 793027 -> 807231 bytes examples/HarmonicOscillator.fmu | Bin 792556 -> 806760 bytes examples/HarmonicOscillator6D.fmu | Bin 800219 -> 814423 bytes pyproject.toml | 2 +- pytest.ini | 4 +- src/component_model/utils/controls.py | 265 ++++++++++++++++ tests/conftest.py | 22 +- ...all_3d.py => test_bouncing_ball_3d_fmu.py} | 137 ++------- tests/test_controls.py | 138 +++++++++ tests/test_oscillator_6dof_fmu.py | 214 ------------- tests/test_oscillator_fmu.py | 285 +----------------- tests/test_time_table_fmu.py | 88 +----- tests/test_transform.py | 7 +- 13 files changed, 447 insertions(+), 715 deletions(-) create mode 100644 src/component_model/utils/controls.py rename tests/{test_bouncing_ball_3d.py => test_bouncing_ball_3d_fmu.py} (73%) create mode 100644 tests/test_controls.py diff --git a/examples/DrivingForce.fmu b/examples/DrivingForce.fmu index e9109bc39655e8b5d48b769396fdb022b85ca15b..35768c2af743e11834a60bf9d812dc472f616c48 100644 GIT binary patch delta 12160 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 diff --git a/examples/HarmonicOscillator.fmu b/examples/HarmonicOscillator.fmu index f835da2c0b0e37099a306691ebb8b28c377cae57..2bf2cbb5357c3218afa92ad6b249c2d499376478 100644 GIT binary patch delta 12287 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 Dr$jx? delta 1208 zcmY*XZEO@p7~YwqJ+7S|Hf`^Esii|v+g`zL+oNAnlnVCtK3YiH1apSE*PUxOZg=b6 zQG;AWDu$5ogHm-!Y7B;u@DZZ~;`)Qwiu$)yjR_G=xIb7?V+cglnBdH;{c)Q-n|Gh* znfIBQeOEtd|LtS>lf9Lm+5FMBMk<|=O0YmHmJS_F%`HDOcxQ(s-Esas54=_N`qZ># z=L|jlv~JF*hOVhOGB&P`Pm*kN3*CPObkMOYAV_RtSgO{Hr!4J&L0a%;Y!ag3oP{+D z=O)!0FVz`KXWS&g8Qs)~%{jxeUctJFbA%_U_CcJn45nrAv`uDo>p<2xh{tWhay8m^ z4SZdP*)whHX0Ew0IlOB$vAc20TCDTn8rZkaNqzw`etKvHC^cBmu${c7+7nq!j4Z)J zmPzP^8{j&FE8htUpEd>BTt^{icLVDX=~>HRR8 zZ|F#CJ(>3IOuAQ&boIz-ZCkh2lV((Jr`#2Z&~vS5ph}L6v`6|nyZYp9t+L!F%Z+$` zZ@!kEc>-2A4O`Gah_`pN-RKxBo(=lfAbM6DvM-A2Jl<763ks^CQ&Hre{Wzl`DW#Qh z6rd|5ux7)aIDpkswZtEOJGZ&x8?Wow?u9=3xPp-A*{U$wE2tW?1*zvVXP2AW zB#8ryHty@wBPT+x^}K>=g|+VecZ?PwNgVY>)_YOc`cjkyKH`Dw?@@n@1&+pCX-Q#H zEcU1lz^oI10m6lU`1sMmlD~a(Xk45baN(jj&_Jr4J{N42MthkA3jF zt1h`U$;i<~_v1(s1;v~!Bw4^$i}bT3YQk*lQe|)a)GXcgNE}ZV9l^ImA0<)eCN_ns z7Y0MiXQCw%$5TbF#Kls2qEOmIUms%E#RvNM5E}>iemR{EqYzzMqi=BfRYqS9qs{cO nVT3lV&FkMGN#bxP!)^a2_?&tBhf%#BOIZ5dCrQ6lAg28X51f38 diff --git a/examples/HarmonicOscillator6D.fmu b/examples/HarmonicOscillator6D.fmu index b0fd490e06d324e98deed48f0fdd8d4c92a077e0..f2f67bc33f8d57bfcb6cdaa260393976291cab7e 100644 GIT binary patch delta 12186 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 delta 1277 zcmY*XOKcNI7@nC;La-;1N^qXo8G|Av)Qul&JB1dIP-uV<@S&}!d?1_k+FpC(wbmvT zl&U06#SM{!GE#cs!Xau)W2t3TkWxa!p{g1<@Dgp67DQDMM4^XL$N^?{T`G_C?f(1y zkNIZjyLmhK^Kzgr)-P181$+gMjF;+8y9DO@TxC$Yo)@Jo1Sjawbg>qb#ihq~3=h@`0rsi+)~B{{7WI=kq36NWw_Yz^y)gPKCN zkpJrqMbo83XF1hHHjtd6BuG&w37r(vQjv$!h^{g&t&pUeODG2CG+oaUHAjjH-&-;c zk)*D%T!G{bWkl5v7PLb|HWa2L;cyUo{35*6#zo`}HCOChKd`U&ojp6&`~KV6jLpOS zRp!iPn5?Haufbg|q9&P`_a!4$Ac|H{$X-3C(CTaO61}qo8_e)^Xfz5Za%1MqGK_ny zXq5x0(25F9biLk+`diU&$cnn%Xvl9xQ+tpEg$dUCWB_eJR@4(i6D>STe^gP-D)VGe zuRTu5BCX!MP(qWtgwT<38e2hq!c=bT*z^iw5E!b=>6Pm3_k~HuF5RnPSjAAwu$sYP zF5Pq9-N|p3Uhc+Qt)Rw_ch|CU@#oFw|EBx>_#_eK9(hxANDN1VuZM#nQA+egHid%$ zF(gM6F&d#bfVVpX;kSd~&7!zjjGFHS@R^V`4$8Qn2j)H*A47E&zM*#&yv@r_7f*jP z`Lx{w0EgFdo&7jBDnEd9HjQ0$aT(V6V*L=U1-37F-xpu^?(Tp9aCBAl%{%Bs68G5u zE{WUFYnT(wr&73q@YZJ!W8cjn065lFv{twA%0`-RMYZ&njIlLjFvW)a>9$i-Dco$a zXE{4CV6(rcu!~_30BQJ*D9e%W88PZWC@W<`)h7Ep6PwheZH5Y}44?N8yHlozyps R-SvcB#XqY7xbiAyxfhUdm(u_M diff --git a/pyproject.toml b/pyproject.toml index 7704d5c..3e0f6d9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,7 +70,7 @@ dependencies = [ "scipy>=1.15.1", "matplotlib>=3.9.1", "plotly>=6.0.1", - "libcosimpy>=0.0.5", +# "libcosimpy>=0.0.5", # "libcosimpy @ file:///C:/Users/eis/Downloads/libcosimpy-0.0.4-cp312-cp312-win_amd64.whl", ] rest = ["docutils>=0.21"] diff --git a/pytest.ini b/pytest.ini index 89ecb94..4afd1a3 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,5 +1,5 @@ [pytest] -testpaths = - tests +testpaths = tests +norecursedirs = tests/osp addopts = --strict-markers --verbose xfail_strict = True diff --git a/src/component_model/utils/controls.py b/src/component_model/utils/controls.py new file mode 100644 index 0000000..20f08ba --- /dev/null +++ b/src/component_model/utils/controls.py @@ -0,0 +1,265 @@ +import logging + +import numpy as np + +logger = logging.getLogger(__name__) + + +class Controls(object): + """Keep track of float variable changes. + + * Store and check possible float variable changes, including first and second derivatives + * Set control goals. A goal is either None or a sequence of (time, acceleration) tuples. + In this way an acceleration can be set or the velocity or position can be changed through the step function. + + Args: + names (tuple[str]): Tuple of name strings for the control variables to use. + The names are only used internally an do not need to correlate with outside names and objects + limits: None or tuple of limits one per name. None means 'no limit' for variable(s), order or min/max. + In general the (min,max) is provided for all orders, i.e. 3 tuples of 2-tuples of float per name. + A given order can be fixed through min==max or by providing a single float instead of the tuple. + The sub-orders of a fixed order do not need to be provided and are internally set to (0.0, 0,0) + limit_err: Determines how limit errors are dealt with. + Anything below critical sets the value to the limit and provides a logger message. + Critical leads to a program run error. + """ + + def __init__( + self, + names: tuple[str, ...] = (), + limits: tuple[tuple[tuple[float | None, float | None] | float | None, ...], ...] | None = None, + limit_err: int = logging.WARNING, + ): + self.names = list(names) + self.dim = len(names) + self.goals: list[tuple | None] = [] if self.dim == 0 else [None] * self.dim # Initially no goals set + self.rows = [] if self.dim == 0 else [-1] * self.dim # current row in goal sequence + self.current: list[np.ndarray] = ( + [] if self.dim == 0 else [np.array((0.0, 0.0, 0.0), float) * self.dim] + ) # current positions, speeds, accelerations + self.nogoals: bool = True + self.limit_err = limit_err + self._limits: list[list[tuple[float, float]]] = [] + if isinstance(limits, tuple): + for idx in range(self.dim): + self._limits.append(self._prepare_limits(limits[idx])) + + def _prepare_limits( + self, limits: tuple[tuple[float | None, float | None] | float | None, ...] + ) -> list[tuple[float, float]]: + """Prepare and check 'limits', so that they can be appended to Controls. + + Args: + limits : optional specification of limits for var, d_var/dt and d2_var/dt2 of single float variable: + + * None: Denotes 'no specified limits' => -inf ...inf + * single number: The variable is fixed to this single value + * tuple(min,max): minimum and maximum value + """ + _limits = [(float("-inf"), float("inf"))] * 3 # default limits for value, 1.deriv., 2.deriv + + if limits is None: # default values for all orders + return _limits + + for order in range(3): + if len(limits) <= order: + if _limits[order - 1][0] == _limits[order - 1][1]: # order-1 fixed + _limits[order] = (0.0, 0.0) # derivative fixed zero + else: + raise ValueError(f"Explicit limit needed for order {order} in {limits}.") from None + else: + lim = limits[order] + if lim is None: # use the default value + pass + elif isinstance(lim, (float, int)): # single value provided + assert lim + fixed = float(lim) + _limits[order] = (fixed, fixed) + elif isinstance(lim, tuple): # both values provided + assert len(lim) == 2, f"Need both minimum and maximum. Found {limits[order]}" + if lim[0] is not None and lim[1] is not None: + _limits[order] = (lim[0], lim[1]) + assert _limits[order][0] <= _limits[order][1], f"Wrong order of limits: {limits[order]}" + elif lim[0] is not None: + _limits[order] = (float(lim[0]), float("inf")) + elif lim[1] is not None: + _limits[order] = (float("-inf"), float(lim[1])) + else: + raise ValueError(f"Unknown type of limits[{order}]: {lim}") from None + return _limits + + def append(self, name: str, limits: tuple[tuple[float | None, float | None] | float | None, ...]): + self.names.append(name) + self.dim += 1 + self.goals.append(None) + self.rows.append(-1) + self.current.append(np.array((0.0, 0.0, 0.0), float)) + self._limits.append(self._prepare_limits(limits)) + + def idx(self, name: str) -> int: + """Find index from name.""" + return self.names.index(name) + + def limit(self, ident: int | str, order: int, minmax: int, value: float | None = None) -> float: + """Get/Set the single limit for 'idx', 'order', 'minmax'.""" + idx = ident if isinstance(ident, int) else self.names.index(ident) + assert 0 <= idx < 3, f"Only idx = 0,1,2 allowed. Found {idx}" + assert 0 <= order < 3, f"Only order = 0,1,2 allowed. Found {order}" + assert 0 <= minmax < 2, f"Only minmax = 0,1 allowed. Found {minmax}" + if value is not None: + lim = self._limits[idx][order] + self.limits(idx, order, (value if minmax == 0 else lim[0], value if minmax == 1 else lim[1])) + return self._limits[idx][order][minmax] + + def limits(self, ident: int | str, order: int, value: tuple | None = None) -> tuple[float, float]: + """Get/Set the min/max limit for 'idx', 'order'.""" + idx = ident if isinstance(ident, int) else self.names.index(ident) + assert 0 <= idx < 3, f"Only idx = 0,1,2 allowed. Found {idx}" + assert 0 <= order < 3, f"Only order = 0,1,2 allowed. Found {order}" + if value is not None: + assert value[0] <= value[1], f"Wrong order:{value}" + self._limits[idx][order] = value + return self._limits[idx][order] + + def check_limit(self, ident: int | str, order: int, value: float) -> float | None: + idx = ident if isinstance(ident, int) else self.names.index(ident) + if value < self.limit(idx, order, 0): # check goal value wrt. limits + msg = f"Goal value {value} is below the limit {self.limit(idx, order, 0)}." + if self.limit_err == logging.CRITICAL: + raise ValueError(msg + "Stopping execution.") from None + else: + logger.log(self.limit_err, msg + " Setting value to minimum.") + return self.limit(idx, order, 0) # corrected value + if value > self.limit(idx, order, 1): + msg = f"Goal value {value} is above the limit {self.limit(idx, order, 1)}." + if self.limit_err == logging.CRITICAL: + raise ValueError(msg + "Stopping execution.") from None + else: + logger.log(self.limit_err, msg + " Setting value to maximum.") + return self.limit(idx, order, 1) # corrected value + return value + + def setgoal(self, ident: int | str, order: int, value: float | None, t0: float = 0.0): + """Set a new goal for 'ident', i.e. set the required time-acceleration sequence + to reach value with all derivatives = 0.0. + + Args: + ident (int|str): the identificator of the control element (as integer or name) + order (int): the order 0,1,2 of the goal to be set + value (float|None): the goal value (acceleration, velocity or position) to be reached. + None to unset the goal. + t0 (float): the current time + """ + idx = ident if isinstance(ident, int) else self.names.index(ident) + # check the index, the order and the value with respect to limits + if not 0 <= idx < 3: + raise ValueError(f"Only idx = 0,1,2 allowed. Found {idx}") from None + if not 0 <= order < 3: + raise ValueError(f"Only order = 0,1,2 allowed. Found {order}") from None + # assert value is None or self.goals[idx] is None, "Change of goals is currently not implemented." + if value is None: # unset goal + self.goals[idx] = None + else: + value = self.check_limit(idx, order, value) + # print(f"SET {idx}, {order}: {value}. Current:{current}. Limits:{self.limits(idx, order)}") + if ( + ( + order == 0 and abs(self.current[idx][0] - value) < 1e-13 + ) # (adjusted) position goal is already reached + or (order == 2 and value == 0.0) + ): # zero acceleration requested + self.goals[idx] = None + elif order == 2: # set the acceleration from now and 'forever' + self.goals[idx] = ((float("inf"), value),) + elif order == 1: # accelerate to another velocity and keep that 'forever' + _speed = self.current[idx][1] + acc = self.limit(idx, 2, int(_speed < value)) # maximum acceleration or deceleration + self.goals[idx] = ((t0 + (value - _speed) / acc, acc), (float("inf"), 0.0)) + elif order == 0: # sequence of acceleration and deceleration to reach a new position + _pos = self.current[idx][0] + if abs(self.current[idx][1]) > 1e-12: # the initial velocity is not zero. Need to decelerate + v0 = self.current[idx][1] + a = self.limit(idx, 2, int(bool(-np.sign(v0) + 1))) + goal0 = (t0, a) + t0 += -v0 / a + _pos = -v0 * v0 / 2 / a # updated position when the velocity is zero + else: + goal0 = None + acc1 = self.limit(idx, 2, int(_pos < value)) # maximum acceleration on first leg + acc2 = self.limit(idx, 2, int(_pos > value)) # maximum acceleration on last leg + if acc1 == 0 or acc2 == 0: + _acc = np.sign(int(_pos < value) + 1) * float("inf") + else: + _acc = 0.5 * (1.0 / acc1 - 1.0 / acc2) + vmax = self.limit(idx, 1, int(_pos < value)) # maximum velocity towards goal + dx1_dx3 = vmax**2 * _acc + dx2 = value - _pos - dx1_dx3 + if np.sign(value - _pos) != np.sign(dx2): # maximum velocity is not reached + v1 = np.sign(value - _pos) * np.sqrt(_acc * (value - _pos)) + dt1 = v1 / acc1 + dt2 = -v1 / acc2 + if goal0 is None: + self.goals[idx] = ((t0 + dt1, acc1), (t0 + dt1 + dt2, acc2), (float("inf"), 0.0)) + else: + self.goals[idx] = (goal0, (t0 + dt1, acc1), (t0 + dt1 + dt2, acc2), (float("inf"), 0.0)) + else: + dt1 = vmax / acc1 + dt2 = dx2 / vmax + dt3 = -vmax / acc2 + if goal0 is None: + self.goals[idx] = ( + (t0 + dt1, acc1), + (t0 + dt1 + dt2, 0.0), + (t0 + dt1 + dt2 + dt3, acc2), + (float("inf"), 0.0), + ) + else: + self.goals[idx] = ( + goal0, + (t0 + dt1, acc1), + (t0 + dt1 + dt2, 0.0), + (t0 + dt1 + dt2 + dt3, acc2), + (float("inf"), 0.0), + ) + logger.info(f"New goals: {self.goals}") + self.nogoals = all(x is None for x in self.goals) + self.rows[idx] = -1 if self.goals[idx] is None else 0 # set start row + + def getgoal(self, ident: int | str) -> tuple: + idx = ident if isinstance(ident, int) else self.names.index(ident) + assert 0 <= idx < 3, f"Only idx = 0,1,2 allowed. Found {idx}" + return (self.current[idx], self.goals[idx]) + + def step(self, time: float, dt: float): + """Step towards the goals (if goals are set).""" + if not self.nogoals: + for idx in range(self.dim): + goals = self.goals[idx] + if goals is not None: + _current = self.current[idx] + + _t, _current[2] = goals[self.rows[idx]] + while time > _t: # move row so that it starts in the right time-acc row + self.rows[idx] += 1 + _t, _current[2] = goals[self.rows[idx]] + while dt > 0: + if time + dt < _t: # covers the whole + _current[0] = self.check_limit( + idx, 0, _current[0] + _current[1] * dt + 0.5 * _current[2] * dt * dt + ) + _current[1] = self.check_limit(idx, 1, _current[1] + _current[2] * dt) + dt = 0 + else: # dt must be split + dt1 = _t - time + _current[0] = self.check_limit( + idx, 0, _current[0] + _current[1] * dt1 + 0.5 * _current[2] * dt1 * dt1 + ) + _current[1] = self.check_limit(idx, 1, _current[1] + _current[2] * dt1) + time = _t + dt -= dt1 + self.rows[idx] += 1 + _t, _current[2] = goals[self.rows[idx]] + + if np.isinf(_t) and abs(_current[2]) < 1e-12 and abs(_current[1]) < 1e-12: + self.goals[idx] = None + self.nogoals = all(x is None for x in self.goals) diff --git a/tests/conftest.py b/tests/conftest.py index 10a3a24..893c43b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -42,17 +42,17 @@ def register_all_namespaces(filename: Path): return path -@pytest.fixture(scope="session", autouse=True) -def instantiate_cosim_execution() -> None: - """ - Fixture that instantiates a CosimExecution object for the entire package. - This fixture is automatically used for the entire package. - """ - - from libcosimpy.CosimExecution import CosimExecution - - _ = CosimExecution.from_step_size(1) - return +# @pytest.fixture(scope="session", autouse=True) +# def instantiate_cosim_execution() -> None: +# """ +# Fixture that instantiates a CosimExecution object for the entire package. +# This fixture is automatically used for the entire package. +# """ +# +# from libcosimpy.CosimExecution import CosimExecution +# +# _ = CosimExecution.from_step_size(1) +# return @pytest.fixture(scope="package", autouse=True) diff --git a/tests/test_bouncing_ball_3d.py b/tests/test_bouncing_ball_3d_fmu.py similarity index 73% rename from tests/test_bouncing_ball_3d.py rename to tests/test_bouncing_ball_3d_fmu.py index d1faa54..216fab2 100644 --- a/tests/test_bouncing_ball_3d.py +++ b/tests/test_bouncing_ball_3d_fmu.py @@ -7,15 +7,11 @@ from zipfile import ZipFile import matplotlib.pyplot as plt +import numpy as np import pytest from fmpy import plot_result, simulate_fmu # type: ignore from fmpy.util import fmu_info # type: ignore from fmpy.validation import validate_fmu # type: ignore -from libcosimpy.CosimEnums import CosimErrorCode, CosimExecutionState -from libcosimpy.CosimExecution import CosimExecution -from libcosimpy.CosimManipulator import CosimManipulator -from libcosimpy.CosimObserver import CosimObserver -from libcosimpy.CosimSlave import CosimLocalSlave from pythonfmu.default_experiment import DefaultExperiment from component_model.model import Model @@ -26,14 +22,6 @@ def _in_interval(x: float, x0: float, x1: float): return x0 <= x <= x1 or x1 <= x <= x0 -def arrays_equal(arr1, arr2, eps=1e-7): - assert len(arr1) == len(arr2), "Length not equal!" - - for i in range(len(arr1)): - # assert type(arr1[i]) == type(arr2[i]), f"Array element {i} type {type(arr1[i])} != {type(arr2[i])}" - assert abs(arr1[i] - arr2[i]) < eps, f"Component {i}: {arr1[i]} != {arr2[i]}" - - def _to_et(file: str, sub: str = "modelDescription.xml"): with ZipFile(file) as zp: xml = zp.read(sub) @@ -96,8 +84,8 @@ def get_result(): h_fac = 1.0 if len(bb._pos) > 1 and bb._pos.unit[2].du is not None: # the main test settings - arrays_equal(bb.pos, (0, 0, 10 * 0.0254)) # was provided as inch - arrays_equal(bb.speed, (1, 0, 0)) + assert np.allclose(bb.pos, (0, 0, 10 * 0.0254)) # was provided as inch + assert np.allclose(bb.speed, (1, 0, 0)) assert bb.g == 9.81 assert bb.e == 0.9 h_fac = 0.0254 @@ -115,13 +103,13 @@ def get_result(): t_b, p_b = bb.next_bounce() assert t_bounce == t_b # print("Bounce", t_bounce, x_bounce, p_b) - arrays_equal((x_bounce, 0, 0), p_b), f"x_bounce:{x_bounce} != {p_b[0]}" # type: ignore ##?? + assert np.allclose((x_bounce, 0, 0), p_b), f"x_bounce:{x_bounce} != {p_b[0]}" # type: ignore ##?? get_result() # after one step bb.do_step(time, dt) get_result() # print("After one step", result(bb)) - arrays_equal( + assert np.allclose( result[-1], ( 0.01, # time @@ -144,7 +132,7 @@ def get_result(): bb.do_step(time, dt) get_result() # print(f"Just before bounce @{t_bounce}, {t_before}: {result[-1]}") - arrays_equal( + assert np.allclose( result[-1], ( t_before, @@ -158,7 +146,7 @@ def get_result(): 0, 0, ), - eps=0.003, + atol=0.003, ) # just after bounce # print(f"Step {len(z)}, time {bb.time}, pos:{bb.pos}, speed:{bb.speed}, t_bounce:{bb.t_bounce}, p_bounce:{bb.p_bounce}") @@ -166,7 +154,7 @@ def get_result(): get_result() ddt = t_before + dt - t_bounce # time from bounce to end of step x_bounce2 = x_bounce + 2 * v_bounce * bb.e * 1.0 * bb.e / bb.g - arrays_equal( + assert np.allclose( result[-1], ( t_before + dt, @@ -180,7 +168,7 @@ def get_result(): 0, 0, ), - eps=0.03, + atol=0.03, ) # from bounce to bounce v_x, v_z, t_b, x_b = ( @@ -228,6 +216,12 @@ def test_make_bouncing_ball(bouncing_ball_fmu): def test_use_fmu(bouncing_ball_fmu, show): """Test and validate the basic BouncingBall using fmpy and not using OSP or case_study.""" + + def check_result(res: np.ndarray, expected: tuple, eps=1e-10): # res is a structured array + assert len(res) == len(expected), f"Lengths {res} != {expected}" + for r, e in zip(res, expected, strict=True): + assert abs(r - e) < eps, f"{r} != {e}" + assert bouncing_ball_fmu.exists(), f"File {bouncing_ball_fmu} does not exist" dt = 0.01 result = simulate_fmu( # type: ignore[reportArgumentType] @@ -257,24 +251,11 @@ def test_use_fmu(bouncing_ball_fmu, show): v_bounce = g * t_bounce # speed in z-direction x_bounce = t_bounce / 1.0 # x-position where it bounces in m # Note: default values are reported at time 0! - arrays_equal(list(result[0])[:7], [0, 0, 0, 10, 1, 0, 0]) # time,pos-3, speed-3(, p_bounce-3 not calculated) + assert np.allclose(list(result[0])[:7], [0, 0, 0, 10, 1, 0, 0]) # time,pos-3, speed-3(, p_bounce-3 not calculated) # print(f"Result[1]: {result[1]}") - arrays_equal( - result[1], - ( - 0.01, # time - 0.01, # pos - 0, - (h0 - 0.5 * g * 0.01**2) / h_fac, - 1, # speed - 0, - -g * 0.01, - x_bounce, # p_bounce - 0, - 0, - ), - ) - arrays_equal( + check_result(result[1], (0.01, 0.01, 0, (h0 - 0.5 * g * 0.01**2) / h_fac, 1, 0, -g * 0.01, x_bounce, 0, 0)) + + check_result( result[1], ( 0.01, @@ -295,7 +276,7 @@ def test_use_fmu(bouncing_ball_fmu, show): if t_before == t_bounce: # at the interval border t_before -= dt # print(f"Just before bounce @{t_bounce}, {t_before}: {result[-1]}") - arrays_equal( + check_result( result[int(t_before / dt)], ( t_before, @@ -315,7 +296,7 @@ def test_use_fmu(bouncing_ball_fmu, show): # print(f"Step {len(z)}, time {bb.time}, pos:{bb.pos}, speed:{bb.speed}, t_bounce:{bb.t_bounce}, p_bounce:{bb.p_bounce}") ddt = t_before + dt - t_bounce # time from bounce to end of step x_bounce2 = x_bounce + 2 * v_bounce * e * 1.0 * e / g - arrays_equal( + check_result( result[int((t_before + dt) / dt)], ( t_before + dt, @@ -360,77 +341,6 @@ def test_use_fmu(bouncing_ball_fmu, show): assert abs(result[row - 1][4] * e - result[row][4]) < 1e-15, "Reduced speed in x-direction" -def test_from_osp(bouncing_ball_fmu): - def get_status(sim): - status = sim.status() - return { - "currentTime": status.current_time, - "state": CosimExecutionState(status.state).name, - "error_code": CosimErrorCode(status.error_code).name, - "real_time_factor": status.real_time_factor, - "rolling_average_real_time_factor": status.rolling_average_real_time_factor, - "real_time_factor_target": status.real_time_factor_target, - "is_real_time_simulation": status.is_real_time_simulation, - "steps_to_monitor": status.steps_to_monitor, - } - - sim = CosimExecution.from_step_size(step_size=1e7) # empty execution object with fixed time step in nanos - bb = CosimLocalSlave(fmu_path=str(bouncing_ball_fmu.absolute()), instance_name="bb") - - ibb = sim.add_local_slave(bb) - assert ibb == 0, f"local slave number {ibb}" - info = sim.slave_infos() - assert info[0].name.decode() == "bb", "The name of the component instance" - assert info[0].index == 0, "The index of the component instance" - assert sim.slave_index_from_instance_name("bb") == 0 - assert sim.num_slaves() == 1 - assert sim.num_slave_variables(0) == 11, "3*pos, 3*speed, g, e, 3*p_bounce" - variables = {var_ref.name.decode(): var_ref.reference for var_ref in sim.slave_variables(ibb)} - assert variables == { - "pos[0]": 0, - "pos[1]": 1, - "pos[2]": 2, - "speed[0]": 3, - "speed[1]": 4, - "speed[2]": 5, - "g": 6, - "e": 7, - "p_bounce[0]": 8, - "p_bounce[1]": 9, - "p_bounce[2]": 10, - } - - # Set initial values - sim.real_initial_value(ibb, variables["g"], 1.5) # actual setting will only happen after start_initialization_mode - - assert get_status(sim)["state"] == "STOPPED" - - observer = CosimObserver.create_last_value() - assert sim.add_observer(observer) - manipulator = CosimManipulator.create_override() - assert sim.add_manipulator(manipulator) - - values = observer.last_real_values(0, list(range(11))) - assert values == [0.0] * 11, "No initial values yet! - as expected" - - # that does not seem to work (not clear why): assert sim.step()==True - assert sim.simulate_until(target_time=1e7), "Simulate for one base step did not work" - assert get_status(sim)["currentTime"] == 1e7, "Time after simulation not correct" - values = observer.last_real_values(0, list(range(11))) - assert values[6] == 1.5, "Initial setting did not work" - assert values[5] == -0.015, "Initial setting did not have the expected effect on speed" - - manipulator.slave_real_values(ibb, [0, 1, 2], [1.0, 2.0, 3.0]) - - -# values = observer.last_real_values(0, list(range(11))) -# print("VALUES2", values) -# -# manipulator.reset_variables(0, CosimVariableType.REAL, [6]) - -# sim.simulate_until(target_time=3e9) - - def test_from_fmu(bouncing_ball_fmu): assert bouncing_ball_fmu.exists(), "FMU not found" model = model_from_fmu(bouncing_ball_fmu) @@ -453,13 +363,12 @@ def test_from_fmu(bouncing_ball_fmu): if __name__ == "__main__": - retcode = pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__]) + retcode = 0 # pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__]) assert retcode == 0, f"Non-zero return code {retcode}" import os os.chdir(Path(__file__).parent / "test_working_directory") # test_bouncing_ball_class(show=False) # test_make_bouncing_ball(_bouncing_ball_fmu()) - # test_use_fmu(_bouncing_ball_fmu(), True) + test_use_fmu(_bouncing_ball_fmu(), True) # test_from_fmu( _bouncing_ball_fmu()) - # test_from_osp(_bouncing_ball_fmu()) diff --git a/tests/test_controls.py b/tests/test_controls.py new file mode 100644 index 0000000..662a0e4 --- /dev/null +++ b/tests/test_controls.py @@ -0,0 +1,138 @@ +import logging + +import matplotlib.pyplot as plt +import numpy as np +import pytest + +from component_model.utils.controls import Controls + +logger = logging.getLogger(__name__) +logging.basicConfig(level=logging.INFO) + + +def test_limits(): + _b = Controls( + ("len", "polar", "azimuth"), # bcrane boom control + (((1, 20), None, (100, 100)), ((-2, 2), (-1, 1), (0, 0)), ((-1, 1), (-2, 2), (0, 0))), + ) + assert _b.limits(0, 1) == _b.limits("len", 1) == (float("-inf"), float("inf")), ( + f"Found {_b.limits(0, 1)}, {_b.limits('len', 1)}" + ) + assert _b.limits(1, 2) == _b.limits("polar", 2) == (0, 0), f"Found {_b.limits(1, 2)}, {_b.limits('polar', 2)}" + with pytest.raises(AssertionError) as err: + _b.append("additional", ((1, 0), (0, 1), 0, 1)) + assert err.value.args[0].startswith("Wrong order of limits:") + # Controls can also be built step by step: + _b = Controls() + _b.append("len", ((1, 20), None, (1, 100))) + _b.append("polar", ((-2, 2), 1)) # fixed velocity and the acceleration limits are not needed + _b.append("azimuth", ((-1, 1), (-2, 2), (0, 0))) + assert _b.limit(1, 2, 0) == _b.limit(1, 2, 1) == 0.0, "No polar acceleration allowed" + assert _b.nogoals, "No goals yet set" + # try to set goal outside limits + _b.limit_err = logging.CRITICAL + with pytest.raises(ValueError) as err: # type: ignore[assignment] #it is a 'ValueError' + _b.setgoal(1, 2, 9.9, 0.0) + assert err.value.args[0] == "Goal value 9.9 is above the limit 0.0.Stopping execution." + _b.limit_err = logging.WARNING + _b.setgoal(1, 2, 9.9, 0.0) + assert _b.nogoals, f"No goals expected, because the adjusted goal is already reached. Found {_b.goals}" + _b.setgoal(0, 2, 1.1, 0.0) + assert _b.goals == [((float("inf"), 1.1),), None, None], f"Found {_b.goals}" + assert not _b.nogoals, "At least one goal set" + + +def test_goal(show: bool = False): + def do_goal(order: int, value: float, current: np.ndarray | None = None, t_end: float = 10.0): + time = 0.0 + if current is not None: + _b.current[0] = current + if order == 2: + _b.setgoal("len", order, value, time) + elif order == 1: + _b.setgoal("len", order, value, time) + else: + _b.setgoal("len", order, value, time) + dt = 0.1 + res: list[tuple] = [] + assert _b.goals[0] is not None + res.append((time, _b.current[0][0], _b.current[0][1], _b.goals[0][0][1])) + while time + dt < t_end: + _b.step(time, dt) + time += dt + res.append((time, *_b.current[0])) + if show: + do_show(res) + return res + + _b = Controls(limit_err=logging.CRITICAL) + _b.append("len", ((-100.0, 90.0), None, (-1.0, 0.5))) + _b.append("polar", ((-2.0, 2.0), 1.0)) # fixed velocity and the acceleration limits are not needed + _b.append("azimuth", ((-1.0, 1.0), (-2.0, 2.0), (0.0, 0.0))) + + # get from one position to another (zero velocity and acceleration on both ends) + res = do_goal(0, 0.0, current=np.array((10.0, 0.0, 0.0), float), t_end=11.7) + assert abs(_b.current[0][0] + 12.5) < 1e-13, f"Found {_b.current[0][0]}" + assert abs(_b.current[0][1]) < 1e-13 + assert abs(_b.current[0][2]) < 1e-13 + + # get from one position to another (non-zero velocity on start) + res = do_goal(0, 0.0, current=np.array((10.0, 1.0, 0.0), float), t_end=3.65) + assert np.allclose(_b.current[0], (9.375, 0.0, 0.0)), f"Found {_b.current[0]}" + + # get from one velocity to another + res = do_goal(1, -2.0, current=np.array((10.0, 1.0, 0.0), float), t_end=3.1) + assert np.allclose(_b.current[0], (8.5, -2.0, 0.0)) + + # set an acceleration (non-zero position and velocity) + res = do_goal(2, -0.1, current=np.array((10.0, 1.0, 0.0), float), t_end=2.01) + expected = (10 + 1.0 * 2.0 - 0.5 * 0.1 * 2.0**2, 1.0 - 0.1 * 2.0, -0.1) + assert np.allclose(_b.current[0], expected), f"{_b.current[0]} != {expected} " + + _b.limit_err = logging.WARNING # allow corrections from now on + _b.current = [np.array((0.0, 0.0, 0.0), float)] * 3 + + # accelerate in 10 time units + res = do_goal(2, 1.1) + for t, x, v, a in res: + assert np.allclose((x, v, a), (0.5 * 0.5 * t * t, 0.5 * t, 0.5)), f"@{t}: Found {(x, v, a)}" + + assert abs(_b.current[0][1] - 5.0) < 1e-9, f"Found {_b.current[0][1]}" + assert abs(_b.current[0][0] - 25.0) < 1e-9, f"Found {_b.current[0][0]}" + + # Speed from 5 to 2.2 + res = do_goal(1, 2.2) + tgoal = (2.2 - 5) / (-1.0) + for t, x, v, a in res: + if t < tgoal: + assert abs(a + 1) < 1e-9, f"@{t} Acc.: {a}" + assert abs(v - 5.0 - a * t) < 1e-9, f"@{t} Velocity: {v}" + assert abs(x - 25.0 - 5 * t - 0.5 * a * t * t) < 1e-9, f"@{t} Pos.: {x} != {25 + 5 * t - 0.5 * 1 * t * t}" + else: + _x = 25.0 + 5.0 * tgoal - 0.5 * 1.0 * tgoal**2 + 2.2 * (t - tgoal) + assert np.allclose((x, v, a), (_x, 2.2, 0.0)), f"@{t}: {(x, v, a)} != {(_x, 2.2, 0)}" + + res = do_goal(0, 0.0) + + +def do_show(results: list[tuple]): + """Plot selected traces.""" + times = [row[0] for row in results] + fig, (ax1, ax2, ax3) = plt.subplots(1, 3) + fig.set_size_inches(10, 6) + _ = ax1.plot(times, [row[1] for row in results], label="position") + _ = ax2.plot(times, [row[2] for row in results], label="velocity") + _ = ax3.plot(times, [row[3] for row in results], label="acceleration") + _ = ax1.legend() + _ = ax2.legend() + _ = ax3.legend() + plt.show() + + +if __name__ == "__main__": + retcode = pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__]) + assert retcode == 0, f"Non-zero return code {retcode}" + logging.basicConfig(level=logging.DEBUG) + plt.set_loglevel(level="warning") + # test_limits() + # test_goal(show=True) diff --git a/tests/test_oscillator_6dof_fmu.py b/tests/test_oscillator_6dof_fmu.py index 2abd24a..4b501b6 100644 --- a/tests/test_oscillator_6dof_fmu.py +++ b/tests/test_oscillator_6dof_fmu.py @@ -2,29 +2,12 @@ from collections.abc import Iterable from pathlib import Path from typing import Any -from libcosimpy.CosimExecution import CosimExecution - import matplotlib.pyplot as plt -import numpy as np import pytest from fmpy.simulation import simulate_fmu from fmpy.util import fmu_info, plot_result from fmpy.validation import validate_fmu -from libcosimpy.CosimEnums import ( - CosimExecutionState, - CosimVariableCausality, - CosimVariableType, - CosimVariableVariability, -) - -# from libcosimpy.CosimExecution import CosimExecution -from libcosimpy.CosimLogging import CosimLogLevel, log_output_level -from libcosimpy.CosimManipulator import CosimManipulator -from libcosimpy.CosimObserver import CosimObserver -from libcosimpy.CosimSlave import CosimLocalSlave - from component_model.model import Model -from tests.conftest import system_structure_change @pytest.fixture(scope="module") @@ -68,55 +51,6 @@ def _driver_6d_fmu(): return fmu_path -@pytest.fixture(scope="session") -def system_structure(): - return _system_structure() - - -def _system_structure(): - """Make a OSP structure file and return the path""" - return Path(__file__).parent.parent / "examples" / "ForcedOscillator6D.xml" - - -def _slave_index(simulator: CosimExecution, name: str): - """Get the slave index from the name.""" - return simulator.slave_index_from_instance_name(name) - - -def _var_ref(simulator: CosimExecution, slave_name: str, var_name: str): - """Get the variable value reference from slave and variable name. - Return both the slave index and the value reference as tuple.""" - slave = _slave_index(simulator, slave_name) - if slave is None: - return (-1, -1) - for idx in range(simulator.num_slave_variables(slave)): - struct = simulator.slave_variables(slave)[idx] - if struct.name.decode() == var_name: - return (slave, struct.reference) - return (-1, -1) - - -def _var_list(simulator: CosimExecution, slave_name: str, ret: str = "print"): - slave = _slave_index(simulator, slave_name) - assert slave is not None, f"Slave {slave_name} not found in system" - variables = {} - for idx in range(simulator.num_slave_variables(slave)): - struct = simulator.slave_variables(slave)[idx] - if ret == "print": - print( - f"Slave {slave_name}({slave}), var {struct.name.decode()}({struct.reference}), type: {CosimVariableType(struct.type).name}, causality: {CosimVariableCausality(struct.causality).name}, variability: {CosimVariableVariability(struct.variability)}" - ) - else: - variables[struct.name.decode()] = { - "slave": slave, - "idx": idx, - "reference": struct.reference, - "type": CosimVariableType(struct.type).name, - "causality": CosimVariableCausality(struct.causality).name, - "variability": CosimVariableVariability(struct.variability), - } - - def arrays_equal( res: Iterable[Any], expected: Iterable[Any], @@ -199,150 +133,6 @@ def test_use_fmu(oscillator_6d_fmu: Path, show: bool = False): # , driver_6d_fm plot_result(result) -# @pytest.mark.skip() -def test_run_osp(oscillator_6d_fmu: Path, driver_6d_fmu: Path): - # sourcery skip: extract-duplicate-method - sim = CosimExecution.from_step_size(step_size=1e8) # empty execution object with fixed time step in nanos - osc = CosimLocalSlave(fmu_path=str(oscillator_6d_fmu), instance_name="osc") - _osc = sim.add_local_slave(osc) - assert _osc == 0, f"local slave number {_osc}" - reference_dict = {var_ref.name.decode(): var_ref.reference for var_ref in sim.slave_variables(_osc)} - - dri = CosimLocalSlave(fmu_path=str(driver_6d_fmu), instance_name="dri") - _dri = sim.add_local_slave(dri) - assert _dri == 1, f"local slave number {_dri}" - - # Set initial values - sim.real_initial_value(slave_index=_osc, variable_reference=reference_dict["x[2]"], value=1.0) - sim.real_initial_value(slave_index=_osc, variable_reference=reference_dict["c[2]"], value=0.1) - - sim_status = sim.status() - assert sim_status.current_time == 0 - assert CosimExecutionState(sim_status.state) == CosimExecutionState.STOPPED - # Simulate for 1 second - _ = sim.simulate_until(target_time=1.5e9) - - -# @pytest.mark.skipif(sys.platform.startswith("linux"), reason="HarmonicOsciallatorFMU.fmu throws an error on Linux") -def test_run_osp_system_structure(system_structure: Path, show: bool = False): - "Run an OSP simulation in the same way as the SimulatorInterface of sim-explorer is implemented" - log_output_level(CosimLogLevel.TRACE) - print("STRUCTURE", system_structure) - try: - sim = CosimExecution.from_osp_config_file(str(system_structure)) - except Exception as err: - print("ERR", err) - return - sim_status = sim.status() - assert sim_status.current_time == 0 - assert CosimExecutionState(sim_status.state) == CosimExecutionState.STOPPED - assert _slave_index(sim, "osc") == 0 - assert _slave_index(sim, "drv") == 1 - - _var_list(sim, "osc") - assert _var_ref(sim, "osc", "f[5]") == (0, 35), f"Found {_var_ref(sim, 'osc', 'f[5]')}" # last variable - assert _var_ref(sim, "drv", "v_osc[5]") == (1, 29), f"Found {_var_ref(sim, 'drv', 'v_osc[5]')}" # last variable - - # Instantiate a suitable observer for collecting results. - # Instantiate a suitable manipulator for changing variables. - manipulator = CosimManipulator.create_override() - sim.add_manipulator(manipulator=manipulator) - sim.real_initial_value(*_var_ref(sim, "osc", "c[2]"), value=0.1) - observer = CosimObserver.create_last_value() - sim.add_observer(observer=observer) - times = [] - pos = [] - speed = [] - for i in range(6): - sim.real_initial_value(*_var_ref(sim, "osc", f"m[{i}]"), value=10000.0) - sim.real_initial_value(*_var_ref(sim, "osc", f"k[{i}]"), value=10000.0) - sim.real_initial_value(*_var_ref(sim, "osc", "v[0]"), value=1.0) - slave, x0_ref = _var_ref(sim, "osc", "x[0]") - slave, v0_ref = _var_ref(sim, "osc", "v[0]") - slave, x2_ref = _var_ref(sim, "osc", "x[2]") - slave, v2_ref = _var_ref(sim, "osc", "v[2]") - for step in range(1, 100): - time = step * 0.01 - _ = sim.simulate_until(step * 1e7) - values = observer.last_real_values(slave_index=0, variable_references=[x0_ref, v0_ref, x2_ref, v2_ref]) - times.append(time) - pos.append(values[0]) - speed.append(values[1]) - if show: - do_show( - traces={"z-pos": (times, pos), "z-speed": (times, speed)}, - xlabel="time", - ylabel="pos/speed", - title="Oscillator excited through initial speed", - ) - for t, p, v in zip(times, pos, speed, strict=True): - print(f"@{t}, {p}, {v}, {np.sin(t)}") - - -@pytest.mark.parametrize("alg", ["fixedStep", "ecco"]) -def test_run_osp_sweep(system_structure: Path, alg: str, show: bool = False): - _test_run_osp_sweep(system_structure, show, alg) - - -def _test_run_osp_sweep(system_structure: Path, show: bool = False, alg: str = "fixedStep"): - "Run an OSP simulation of the oscillator and the force sweep as co-simulation." - dt = 1.0 - t_end = 100.0 - - log_output_level(CosimLogLevel.TRACE) - structure = system_structure_change( - system_structure, - {"BaseStepSize": ("text", str(dt)), "Algorithm": ("text", alg)}, - f"{system_structure.stem}_{alg}.xml", - ) - print(f"Running Algorithm {alg} on {structure}") - assert structure.exists(), f"File {structure} not found" - sim = CosimExecution.from_osp_config_file(str(structure)) - sim_status = sim.status() - assert sim_status.error_code == 0 - # manipulator = CosimManipulator.create_override() - # sim.add_manipulator(manipulator=manipulator) - sim.real_initial_value(*_var_ref(sim, "osc", "k[5]"), value=1.0) - sim.real_initial_value(*_var_ref(sim, "osc", "c[5]"), value=0.1) - sim.real_initial_value(*_var_ref(sim, "osc", "m[5]"), value=1.0) - sim.real_initial_value(*_var_ref(sim, "drv", "ampl[5]"), value=1.0) - sim.real_initial_value(*_var_ref(sim, "drv", "freq[5]"), value=0.0) # freq (start frequency) - sim.real_initial_value(*_var_ref(sim, "drv", "d_freq[5]"), value=0.1 / 2 / np.pi) - sim.real_initial_value(*_var_ref(sim, "osc", "x0[2]"), value=0.0) - sim.real_initial_value(*_var_ref(sim, "osc", "v0[2]"), value=0.0) - observer = CosimObserver.create_last_value() - sim.add_observer(observer=observer) - _osc, _x5_ref = _var_ref(sim, "osc", "x[5]") - _osc, _v5_ref = _var_ref(sim, "osc", "v[5]") - times = [] - pos = [] - speed = [] - time = 0.0 - while time < t_end: - time += dt - times.append(time) - _ = sim.simulate_until(int(time * 1e9)) - values = observer.last_real_values(slave_index=_osc, variable_references=[_x5_ref, _v5_ref]) - pos.append(values[0]) - speed.append(values[1]) - if Path("oscillator_sweep0.dat").exists(): - times0, pos0, speed0, force0 = [], [], [], [] - with open("oscillator_sweep0.dat", "r") as fp: - for line in fp: - t, p, v, f = line.split("\t") - times0.append(float(t)) - pos0.append(float(p)) - speed0.append(float(v)) - force0.append(float(f)) - if show: - freq0 = [0.1 * t / 2 / np.pi for t in times0] - freq = [0.1 * t / 2 / np.pi for t in times] - do_show({"monolithic model": (freq0, pos0), "co-simulation": (freq, pos)}) - - elif show: - do_show({"z-pos": (times, pos), "z-speed": (times, speed)}) - - if __name__ == "__main__": retcode = pytest.main(args=["-rA", "-v", __file__, "--show", "True"]) assert retcode == 0, f"Non-zero return code {retcode}" @@ -353,7 +143,3 @@ def _test_run_osp_sweep(system_structure: Path, show: bool = False, alg: str = " osc = _oscillator_6d_fmu() # test_make_fmus(osc, drv) # test_use_fmu(osc, drv, show=True) - # test_run_osp(osc, drv) - # test_run_osp_system_structure(_system_structure(), show=True) - # _test_run_osp_sweep(_system_structure(), show=True, alg="fixedStep") - # _test_run_osp_sweep( _system_structure(), show=True, alg='ecco') diff --git a/tests/test_oscillator_fmu.py b/tests/test_oscillator_fmu.py index 8ae3ed5..cbfd32f 100644 --- a/tests/test_oscillator_fmu.py +++ b/tests/test_oscillator_fmu.py @@ -1,30 +1,13 @@ # ruff: noqa: I001 -import sys -from collections.abc import Iterable from pathlib import Path -from typing import Any -from libcosimpy.CosimExecution import CosimExecution import matplotlib.pyplot as plt -import numpy as np import pytest from fmpy.simulation import simulate_fmu from fmpy.util import fmu_info, plot_result from fmpy.validation import validate_fmu -from libcosimpy.CosimEnums import ( - CosimExecutionState, - CosimVariableCausality, - CosimVariableType, - CosimVariableVariability, -) -from libcosimpy.CosimLogging import CosimLogLevel, log_output_level -from libcosimpy.CosimManipulator import CosimManipulator -from libcosimpy.CosimObserver import CosimObserver -from libcosimpy.CosimSlave import CosimLocalSlave from component_model.model import Model -from component_model.utils.xml import read_xml -from tests.conftest import system_structure_change @pytest.fixture(scope="module") @@ -56,85 +39,6 @@ def _driver_fmu(): return fmu_path -def _slave_index(simulator: CosimExecution, name: str): - """Get the slave index from the name.""" - return simulator.slave_index_from_instance_name(name) - - -def _var_ref(simulator: CosimExecution, slave_name: str, var_name: str): - """Get the variable value reference from slave and variable name. - Return both the slave index and the value reference as tuple.""" - slave = _slave_index(simulator, slave_name) - if slave is None: - return (-1, -1) - for idx in range(simulator.num_slave_variables(slave)): - struct = simulator.slave_variables(slave)[idx] - if struct.name.decode() == var_name: - return (slave, struct.reference) - return (-1, -1) - - -def _check_var(simulator: CosimExecution, slave_name: str, var_name: str, expected: int | tuple | None): - if var_name == "": - idx = _slave_index(simulator, slave_name) - assert idx == expected, f"Slave {slave_name}. Index: {idx}. Expected: {expected}" - else: - idx, vref = _var_ref(simulator, slave_name, var_name) - assert (idx, vref) == expected, f"Variable {slave_name}.{var_name}: ({idx},{vref}). Expected:{expected}" - - -def _var_info(simulator: CosimExecution, slave_name: str, var_name: str): - slave = _slave_index(simulator, slave_name) - if slave is None: - return {} - for idx in range(simulator.num_slave_variables(slave)): - struct = simulator.slave_variables(slave)[idx] - if struct.name.decode() == var_name: - return { - "slave": 0, - "idx": idx, - "reference": struct.reference, - "type": CosimVariableType(struct.type).name, - "causality": CosimVariableCausality(struct.causality).name, - "variability": CosimVariableVariability(struct.variability).name, - } - return {} - - -def _var_list(simulator: CosimExecution, slave_name: str, ret: str = "print"): - slave = _slave_index(simulator, slave_name) - assert slave is not None, f"Slave {slave_name} not found in system" - variables = {} - for idx in range(simulator.num_slave_variables(slave)): - struct = simulator.slave_variables(slave)[idx] - if ret == "print": - print( - f"Slave {slave_name}({slave}), var {struct.name.decode()}({struct.reference}), type: {CosimVariableType(struct.type).name}, causality: {CosimVariableCausality(struct.causality).name}, variability: {CosimVariableVariability(struct.variability)}" - ) - else: - variables[struct.name.decode()] = { - "slave": slave, - "idx": idx, - "reference": struct.reference, - "type": CosimVariableType(struct.type).name, - "causality": CosimVariableCausality(struct.causality).name, - "variability": CosimVariableVariability(struct.variability), - } - - -def arrays_equal( - res: Iterable[Any], - expected: Iterable[Any], - eps: float = 1e-7, -): - len_res = len(list(res)) - len_exp = len(list(expected)) - if len_res != len_exp: - raise ValueError(f"Arrays of different lengths cannot be equal. Found {len_res} != {len_exp}") - for i, (x, y) in enumerate(zip(res, expected, strict=False)): - assert abs(x - y) < eps, f"Element {i} not nearly equal in {x}, {y}" - - def do_show(traces: dict[str, tuple[list[float], list[float]]]): fig, ax = plt.subplots() for label, trace in traces.items(): @@ -238,201 +142,14 @@ def test_run_fmpy2(oscillator_fmu: Path, driver_fmu: Path, show: bool = False): plot_result(result) -def test_run_osp(oscillator_fmu: Path, driver_fmu: Path): - # sourcery skip: extract-duplicate-method - sim = CosimExecution.from_step_size(step_size=1e8) # empty execution object with fixed time step in nanos - osc = CosimLocalSlave(fmu_path=str(oscillator_fmu), instance_name="osc") - _osc = sim.add_local_slave(osc) - assert _osc == 0, f"local slave number {_osc}" - - dri = CosimLocalSlave(fmu_path=str(driver_fmu), instance_name="dri") - _dri = sim.add_local_slave(dri) - assert _dri == 1, f"local slave number {_dri}" - - _check_var(sim, "dri", "", _dri) - _check_var(sim, "dri", "d_freq[2]", (1, 8)) - _check_var(sim, "dri", "f[2]", (1, 11)) - info = _var_info(sim, "dri", "f[2]") - assert info["causality"] == "OUTPUT" - assert info["variability"] == "CONTINUOUS" - assert info["type"] == "REAL" - - # Set initial values - sim.real_initial_value(*_var_ref(sim, "osc", "x[2]"), value=1.0) - sim.real_initial_value(*_var_ref(sim, "osc", "c[2]"), value=0.1) - - sim_status = sim.status() - assert sim_status.current_time == 0 - assert CosimExecutionState(sim_status.state) == CosimExecutionState.STOPPED - - # Simulate for 1 second - _ = sim.simulate_until(target_time=15e9) - - -@pytest.mark.skipif(sys.platform.startswith("linux"), reason="HarmonicOsciallatorFMU.fmu throws an error on Linux") -def test_run_osp_system_structure(system_structure: Path, show: bool = False): - "Run an OSP simulation in the same way as the SimulatorInterface of sim-explorer is implemented" - log_output_level(CosimLogLevel.TRACE) - print("STRUCTURE", system_structure) - try: - sim = CosimExecution.from_osp_config_file(str(system_structure)) - except Exception as err: - print("ERR", err) - return - sim_status = sim.status() - assert sim_status.current_time == 0 - assert CosimExecutionState(sim_status.state).name == "STOPPED" - _check_var(sim, "osc", "", 0) - _check_var(sim, "drv", "", 1) - # _var_list(sim, 'drv') - _check_var(sim, "drv", "ampl[0]", (1, 0)) - _check_var(sim, "drv", "ampl[1]", (1, 1)) - _check_var(sim, "drv", "ampl[2]", (1, 2)) - _check_var(sim, "drv", "d_freq[2]", (1, 8)) - _check_var(sim, "drv", "f[2]", (1, 11)) - info = _var_info(sim, "drv", "f[2]") - assert info["causality"] == "OUTPUT" - assert info["variability"] == "CONTINUOUS" - assert info["type"] == "REAL" - - info = _var_info(sim, "osc", "c[2]") - assert info["type"] == "REAL" - assert info["causality"] == "PARAMETER" - assert info["variability"] == "FIXED" - - info = _var_info(sim, "osc", "x[2]") - assert info["type"] == "REAL" - assert info["causality"] == "OUTPUT" - assert info["variability"] == "CONTINUOUS" - - info = _var_info(sim, "osc", "v[2]") - assert info["type"] == "REAL" - assert info["causality"] == "OUTPUT" - assert info["variability"] == "CONTINUOUS" - - # Instantiate a suitable observer for collecting results. - # Instantiate a suitable manipulator for changing variables. - manipulator = CosimManipulator.create_override() - sim.add_manipulator(manipulator=manipulator) - sim.real_initial_value(*_var_ref(sim, "osc", "c[2]"), value=0.5) # c[2] - sim.real_initial_value(*_var_ref(sim, "osc", "x[2]"), value=1.0) # x[2] - observer = CosimObserver.create_last_value() - sim.add_observer(observer=observer) - times: list[float] = [] - pos: list[float] = [] - speed: list[float] = [] - slave, x2_ref = _var_ref(sim, "osc", "x[2]") - slave, v2_ref = _var_ref(sim, "osc", "v[2]") - for step in range(1, 1000): - time = step * 0.01 - _ = sim.simulate_until(step * 1e8) - values = observer.last_real_values(slave_index=0, variable_references=[x2_ref, v2_ref]) - times.append(time) - pos.append(values[0]) - speed.append(values[1]) - if show: - do_show(traces={"z-pos": (times, pos), "z-speed": (times, speed)}) - - -def test_system_structure_change(system_structure): - path = system_structure_change( - system_structure, - { - "BaseStepSize": ("text", str(0.99)), - "Algorithm": ("text", "ecco"), - "VariableConnection": ("powerBond", "myBond"), - }, - "changed_structure.xml", - ) - assert str(path) == str(Path(__file__).parent.parent / "examples" / "changed_structure.xml") - assert path.exists() - el = read_xml(path) - elements = el.findall("{*}BaseStepSize") - assert elements[0].text == "0.99", f"Found {elements[0].text}" - elements = el.findall("{*}Algorithm") - assert elements[0].text == "ecco", f"Found {elements[0].text}" - elements = el.findall(".//{*}VariableConnection") - assert elements[0].attrib["powerBond"] == "myBond", f"Found {elements[0].attrib}" - - -@pytest.mark.parametrize("alg", ["fixedStep", "ecco"]) -def test_run_osp_sweep(system_structure: Path, alg: str, show: bool = False): - _test_run_osp_sweep(system_structure, show, alg) - - -def _test_run_osp_sweep(system_structure: Path, show: bool = False, alg: str = "fixedStep"): - "Run an OSP simulation of the oscillator and the force sweep as co-simulation." - - dt = 1.0 - t_end = 100.0 - - log_output_level(CosimLogLevel.TRACE) - structure = system_structure_change( - system_structure, - {"BaseStepSize": ("text", str(dt)), "Algorithm": ("text", alg)}, - f"{system_structure.stem}_{alg}.xml", - ) - print(f"Running Algorithm {alg} on {structure}") - assert structure.exists(), f"File {structure} not found" - sim = CosimExecution.from_osp_config_file(str(structure)) - sim_status = sim.status() - assert sim_status.error_code == 0 - # manipulator = CosimManipulator.create_override() - # sim.add_manipulator(manipulator=manipulator) - sim.real_initial_value(*_var_ref(sim, "osc", "k[2]"), value=1.0) - sim.real_initial_value(*_var_ref(sim, "osc", "c[2]"), value=0.1) - sim.real_initial_value(*_var_ref(sim, "osc", "m"), value=1.0) - sim.real_initial_value(*_var_ref(sim, "drv", "ampl[2]"), value=1.0) - sim.real_initial_value(*_var_ref(sim, "drv", "freq[2]"), value=0.0) # start frequency - sim.real_initial_value(*_var_ref(sim, "drv", "d_freq[2]"), value=0.1 / 2 / np.pi) - sim.real_initial_value(*_var_ref(sim, "osc", "x[2]"), value=0.0) - sim.real_initial_value(*_var_ref(sim, "osc", "v[2]"), value=0.0) - observer = CosimObserver.create_last_value() - sim.add_observer(observer=observer) - times = [] - pos = [] - speed = [] - time = 0.0 - slave, x2_ref = _var_ref(sim, "osc", "x[2]") - slave, v2_ref = _var_ref(sim, "osc", "v[2]") - while time < t_end: - time += dt - times.append(time) - _ = sim.simulate_until(int(time * 1e9)) - values = observer.last_real_values(slave_index=0, variable_references=[x2_ref, v2_ref]) - pos.append(values[0]) - speed.append(values[1]) - if Path("oscillator_sweep0.dat").exists(): - times0, pos0, speed0, force0 = [], [], [], [] - with open("oscillator_sweep0.dat", "r") as fp: - for line in fp: - t, p, v, f = line.split("\t") - times0.append(float(t)) - pos0.append(float(p)) - speed0.append(float(v)) - force0.append(float(f)) - if show: - freq0 = [0.1 * t / 2 / np.pi for t in times0] - freq = [0.1 * t / 2 / np.pi for t in times] - do_show({"z-pos0": (freq0, pos0), "z-pos": (freq, pos)}) - - elif show: - do_show({"z-pos": (times, pos), "z-speed": (times, speed)}) - - if __name__ == "__main__": - retcode = 0 # pytest.main(args=["-rA", "-v", __file__, "--show", "True"]) + retcode = pytest.main(args=["-rA", "-v", __file__, "--show", "True"]) assert retcode == 0, f"Non-zero return code {retcode}" import os os.chdir(Path(__file__).parent.absolute() / "test_working_directory") osc = _oscillator_fmu() drv = _driver_fmu() - test_system_structure_change(_system_structure()) # test_make_fmus(osc, drv, show=True) # test_run_fmpy(osc, drv, show=True) # test_run_fmpy2(osc, drv, show=True) - # test_run_osp(osc, drv) - # test_run_osp_system_structure(_system_structure(), show=True) - # _test_run_osp_sweep(_system_structure(), show=True, alg="fixedStep") - # _test_run_osp_sweep( _system_structure(), show=True, alg='ecco') diff --git a/tests/test_time_table_fmu.py b/tests/test_time_table_fmu.py index 74f03da..8a79be8 100644 --- a/tests/test_time_table_fmu.py +++ b/tests/test_time_table_fmu.py @@ -9,12 +9,6 @@ from fmpy.simulation import simulate_fmu # type: ignore from fmpy.util import fmu_info, plot_result # type: ignore from fmpy.validation import validate_fmu # type: ignore -from libcosimpy.CosimEnums import CosimExecutionState -from libcosimpy.CosimExecution import CosimExecution -from libcosimpy.CosimLogging import CosimLogLevel, log_output_level -from libcosimpy.CosimManipulator import CosimManipulator # type: ignore -from libcosimpy.CosimObserver import CosimObserver # type: ignore -from libcosimpy.CosimSlave import CosimLocalSlave from pythonfmu.enums import Fmi2Causality as Causality from pythonfmu.enums import Fmi2Variability as Variability @@ -149,81 +143,6 @@ def test_use_fmu(time_table_fmu, show: bool = False): assert abs(result[i][3] - t**2) < 1e-10, f"Result for {ipol}, time={t}: {result[i][1]} != {i**2}" -def test_run_osp(time_table_fmu): - log_output_level(CosimLogLevel.DEBUG) - sim = CosimExecution.from_step_size(step_size=1e8) # empty execution object with fixed time step in nanos - st = CosimLocalSlave(fmu_path=str(time_table_fmu), instance_name="st") - - ist = sim.add_local_slave(st) - assert ist == 0, f"local slave number {ist}" - - reference_dict = {var_ref.name.decode(): var_ref.reference for var_ref in sim.slave_variables(ist)} - - # Set initial values - sim.boolean_initial_value(ist, reference_dict["interpolate"], True) - - sim_status = sim.status() - assert sim_status.current_time == 0 - assert CosimExecutionState(sim_status.state) == CosimExecutionState.STOPPED - - # Simulate for 1 second - sim.simulate_until(target_time=15e9) - - -def test_check_osp_system_structure(time_table_system_structure): - "Instantiate OSP from system structure" - log_output_level(CosimLogLevel.DEBUG) - simulator = CosimExecution.from_osp_config_file(str(time_table_system_structure)) - comps = [] - for comp in list(simulator.slave_infos()): - name = comp.name.decode() - comps.append(name) - assert comps == ["tab"], f"Components: {comps}" - variables = {} - for idx in range(simulator.num_slave_variables(0)): - struct = simulator.slave_variables(0)[idx] - variables.update( - { - struct.name.decode(): { - "reference": struct.reference, - "type": struct.type, - "causality": struct.causality, - "variability": struct.variability, - } - } - ) - assert variables["outs[0]"] == {"reference": 1, "type": 0, "causality": 2, "variability": 4} # similar: [1],[2] - assert variables["interpolate"] == {"reference": 0, "type": 1, "causality": 1, "variability": 1} - - -def test_run_osp_system_structure(time_table_system_structure): - "Run an OSP simulation in the same way as the SystemInterface of sim-explorer is implemented" - log_output_level(CosimLogLevel.TRACE) - for ipol in range(4): - simulator = CosimExecution.from_osp_config_file(str(time_table_system_structure)) # reset - simulator.integer_initial_value(0, 0, ipol) # set 'interpolate' - # manipulator and obeserver - manipulator = CosimManipulator.create_override() - simulator.add_manipulator(manipulator=manipulator) - observer = CosimObserver.create_last_value() - simulator.add_observer(observer=observer) - for time in np.linspace(0.1, 10, 100): - simulator.simulate_until(time * 1e9) - if time == 0.1: - assert observer.last_integer_values(0, [0]) == [ipol], ( - f"iPol {observer.last_integer_values(0, [0])} != {ipol}" - ) - if ipol == 0: - _x = observer.last_real_values(0, [1])[0] - assert _x == 1.0, f"Result for {ipol}: {_x} != 1.0" - elif ipol == 1: - _x = observer.last_real_values(0, [2])[0] - assert abs(_x - time) < 1e-10, f"Result for {ipol}: {_x} != {time}" - elif ipol == 2: - _x = observer.last_real_values(0, [3])[0] - assert abs(_x - time**2) < 1e-10, f"Result for {ipol}: {_x} != {time**2}" - - def test_make_with_new_data(): """Test and example how keyword arguments of the Model class can be used (changed) when building FMU.""" times = np.linspace(0, 2 * np.pi, 100) @@ -243,7 +162,7 @@ def test_make_with_new_data(): ) -@pytest.mark.skip(reason="Does so far not work within pytest, only stand-alone") +# @pytest.mark.skip(reason="Does so far not work within pytest, only stand-alone") def test_use_with_new_data(show): fmu_path = Path(__file__).parent / "test_working_directory" / "TimeTableFMU.fmu" result = simulate_fmu( # type: ignore[reportArgumentType] @@ -268,7 +187,7 @@ def test_use_with_new_data(show): if __name__ == "__main__": - retcode = 0 # pytest.main(["-rA", "-v", __file__]) + retcode = pytest.main(["-rA", "-v", __file__]) assert retcode == 0, f"Non-zero return code {retcode}" import os @@ -276,8 +195,5 @@ def test_use_with_new_data(show): # test_time_table_fmu() # test_make_time_table(_time_table_fmu()) # test_use_fmu(_time_table_fmu(), show=True) - # test_run_osp(_time_table_fmu()) - test_check_osp_system_structure(_time_table_system_structure(_time_table_fmu())) - # test_run_osp_system_structure(_time_table_system_structure(_time_table_fmu())) # test_make_with_new_data() # test_use_with_new_data(show=True) diff --git a/tests/test_transform.py b/tests/test_transform.py index 7161a56..2e27d1c 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -152,7 +152,8 @@ def test_euler_rot(): ) _rot2 = _rot.from_euler("XYZ", (0, 90, 0), degrees=True) * _rot # + pitch 90 deg print(_rot2.as_euler(seq="XYZ", degrees=True)) - print(Rot.from_euler("XYZ", (90, 90, 0), degrees=True).as_euler(seq="XYZ", degrees=True)) + with pytest.warns(UserWarning, match="Gimbal lock detected"): + print(Rot.from_euler("XYZ", (90, 90, 0), degrees=True).as_euler(seq="XYZ", degrees=True)) _rot3 = Rot.from_euler("XYZ", (0, 0, 90), degrees=True) * _rot2 # +yaw 90 deg assert np.allclose(_rot3.apply((1, 0, 0)), (0, 0, -1)) assert np.allclose(_rot3.apply((0, 1, 0)), (0, 1, 0)) @@ -168,12 +169,12 @@ def test_normalized(): if __name__ == "__main__": - retcode = pytest.main(["-rP -s -v", __file__]) + retcode = 0 # pytest.main(["-rP -s -v", __file__]) assert retcode == 0, f"Return code {retcode}" # test_spherical_cartesian() # test_spherical_unique() # test_rot_from_spherical() # test_rot_from_vectors() # test_euler_rot_spherical() - # test_euler_rot() + test_euler_rot() # test_normalized() From 9ba156c9ee93d9d18fff9e0730a552b26d191d96 Mon Sep 17 00:00:00 2001 From: Mendez Date: Fri, 12 Dec 2025 10:58:19 +0100 Subject: [PATCH 20/29] Fix floating point number missmatch and upgrade pythonfmu --- pyproject.toml | 2 +- tests/test_analysis.py | 2 +- tests/test_variable.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3e0f6d9..bc782cb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,7 +60,7 @@ dependencies = [ "numpy>=1.26,<2.0", "pint>=0.24", "jsonpath-ng>=1.7.0", - "pythonfmu>=0.6.7", + "pythonfmu>=0.7.0", "flexparser<0.4", ] diff --git a/tests/test_analysis.py b/tests/test_analysis.py index 34dffb1..8bd08ea 100644 --- a/tests/test_analysis.py +++ b/tests/test_analysis.py @@ -21,7 +21,7 @@ def test_extremum(): # for i in range(100): # print(i, t[i], x[i]) e, p = extremum(t[17:20], x[17:20]) - assert e == -1 and abs(p[0] - np.pi) < 1e-10 and p[1] == -1 + assert e == -1 and abs(p[0] - np.pi) < 1e-10 and np.isclose(p[1], -1) ex = extremum_series(t, x, "all") assert len(ex) == 2 assert np.allclose(ex[0], (12.566370614359142, 1.0)) diff --git a/tests/test_variable.py b/tests/test_variable.py index edc4b50..b3c2cb6 100644 --- a/tests/test_variable.py +++ b/tests/test_variable.py @@ -753,7 +753,7 @@ def test_extremum(): # for i in range(100): # print(i, t[i], x[i]) e, p = extremum(t[17:20], x[17:20]) - assert e == -1 and abs(p[0] - np.pi) < 1e-10 and p[1] == -1 + assert e == -1 and abs(p[0] - np.pi) < 1e-10 and p[1] == np.isclose(p[1], -1) ex = extremum_series(t, x, "all") assert len(ex) == 2 assert np.allclose(ex[0], (12.566370614359142, 1.0)) From 10a22fd8cb3044fd46e2d1019c6ac9029323ddf8 Mon Sep 17 00:00:00 2001 From: Mendez Date: Fri, 12 Dec 2025 12:55:22 +0100 Subject: [PATCH 21/29] Revert pythonfmu version --- pyproject.toml | 2 +- uv.lock | 355 +++++-------------------------------------------- 2 files changed, 37 insertions(+), 320 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index bc782cb..3d5a9d6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,7 +60,7 @@ dependencies = [ "numpy>=1.26,<2.0", "pint>=0.24", "jsonpath-ng>=1.7.0", - "pythonfmu>=0.7.0", + "pythonfmu==0.6.9", "flexparser<0.4", ] diff --git a/uv.lock b/uv.lock index c7b4585..925f478 100644 --- a/uv.lock +++ b/uv.lock @@ -1,10 +1,5 @@ version = 1 -requires-python = ">=3.10" -resolution-markers = [ - "python_full_version >= '3.12'", - "python_full_version == '3.11.*'", - "python_full_version < '3.11'", -] +requires-python = ">=3.11" [[package]] name = "alabaster" @@ -79,19 +74,6 @@ version = "3.4.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/58/5580c1716040bc89206c77d8f74418caf82ce519aae06450393ca73475d1/charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", size = 198013 }, - { url = "https://files.pythonhosted.org/packages/d0/11/00341177ae71c6f5159a08168bcb98c6e6d196d372c94511f9f6c9afe0c6/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", size = 141285 }, - { url = "https://files.pythonhosted.org/packages/01/09/11d684ea5819e5a8f5100fb0b38cf8d02b514746607934134d31233e02c8/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", size = 151449 }, - { url = "https://files.pythonhosted.org/packages/08/06/9f5a12939db324d905dc1f70591ae7d7898d030d7662f0d426e2286f68c9/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", size = 143892 }, - { url = "https://files.pythonhosted.org/packages/93/62/5e89cdfe04584cb7f4d36003ffa2936681b03ecc0754f8e969c2becb7e24/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", size = 146123 }, - { url = "https://files.pythonhosted.org/packages/a9/ac/ab729a15c516da2ab70a05f8722ecfccc3f04ed7a18e45c75bbbaa347d61/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", size = 147943 }, - { url = "https://files.pythonhosted.org/packages/03/d2/3f392f23f042615689456e9a274640c1d2e5dd1d52de36ab8f7955f8f050/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", size = 142063 }, - { url = "https://files.pythonhosted.org/packages/f2/e3/e20aae5e1039a2cd9b08d9205f52142329f887f8cf70da3650326670bddf/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", size = 150578 }, - { url = "https://files.pythonhosted.org/packages/8d/af/779ad72a4da0aed925e1139d458adc486e61076d7ecdcc09e610ea8678db/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", size = 153629 }, - { url = "https://files.pythonhosted.org/packages/c2/b6/7aa450b278e7aa92cf7732140bfd8be21f5f29d5bf334ae987c945276639/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", size = 150778 }, - { url = "https://files.pythonhosted.org/packages/39/f4/d9f4f712d0951dcbfd42920d3db81b00dd23b6ab520419626f4023334056/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", size = 146453 }, - { url = "https://files.pythonhosted.org/packages/49/2b/999d0314e4ee0cff3cb83e6bc9aeddd397eeed693edb4facb901eb8fbb69/charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", size = 95479 }, - { url = "https://files.pythonhosted.org/packages/2d/ce/3cbed41cff67e455a386fb5e5dd8906cdda2ed92fbc6297921f2e4419309/charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", size = 102790 }, { url = "https://files.pythonhosted.org/packages/72/80/41ef5d5a7935d2d3a773e3eaebf0a9350542f2cab4eac59a7a4741fbbbbe/charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", size = 194995 }, { url = "https://files.pythonhosted.org/packages/7a/28/0b9fefa7b8b080ec492110af6d88aa3dea91c464b17d53474b6e9ba5d2c5/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", size = 139471 }, { url = "https://files.pythonhosted.org/packages/71/64/d24ab1a997efb06402e3fc07317e94da358e2585165930d9d59ad45fcae2/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", size = 149831 }, @@ -150,24 +132,20 @@ source = { editable = "." } dependencies = [ { name = "flexparser" }, { name = "jsonpath-ng" }, - { name = "libcosimpy" }, - { name = "matplotlib" }, { name = "numpy" }, { name = "pint" }, - { name = "plotly" }, { name = "pythonfmu" }, - { name = "scipy" }, - { name = "sympy" }, ] [package.optional-dependencies] -modeltest = [ +rest = [ + { name = "docutils" }, +] +tests = [ { name = "fmpy" }, { name = "matplotlib" }, { name = "plotly" }, -] -rest = [ - { name = "docutils" }, + { name = "scipy" }, ] [package.dev-dependencies] @@ -181,11 +159,9 @@ dev = [ { name = "pytest-cov" }, { name = "ruff" }, { name = "sourcery" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx" }, { name = "sphinx-argparse-cli" }, - { name = "sphinx-autodoc-typehints", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx-autodoc-typehints", version = "3.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx-autodoc-typehints" }, { name = "sphinxcontrib-mermaid" }, ] @@ -193,18 +169,14 @@ dev = [ requires-dist = [ { name = "docutils", marker = "extra == 'rest'", specifier = ">=0.21" }, { name = "flexparser", specifier = "<0.4" }, - { name = "fmpy", marker = "extra == 'modeltest'", specifier = "==0.3.21" }, + { name = "fmpy", marker = "extra == 'tests'", specifier = "==0.3.21" }, { name = "jsonpath-ng", specifier = ">=1.7.0" }, - { name = "libcosimpy", git = "https://github.com/open-simulation-platform/libcosimpy?rev=cosimc-loading-fix" }, - { name = "matplotlib", specifier = ">=3.9.1" }, - { name = "matplotlib", marker = "extra == 'modeltest'", specifier = ">=3.9.1" }, + { name = "matplotlib", marker = "extra == 'tests'", specifier = ">=3.9.1" }, { name = "numpy", specifier = ">=1.26,<2.0" }, { name = "pint", specifier = ">=0.24" }, - { name = "plotly", specifier = ">=6.0.1" }, - { name = "plotly", marker = "extra == 'modeltest'", specifier = ">=6.0.1" }, - { name = "pythonfmu", specifier = ">=0.6.7" }, - { name = "scipy", specifier = ">=1.15.1" }, - { name = "sympy", specifier = ">=1.13.3" }, + { name = "plotly", marker = "extra == 'tests'", specifier = ">=6.0.1" }, + { name = "pythonfmu", specifier = "==0.6.9" }, + { name = "scipy", marker = "extra == 'tests'", specifier = ">=1.15.1" }, ] [package.metadata.requires-dev] @@ -233,16 +205,6 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/25/c2/fc7193cc5383637ff390a712e88e4ded0452c9fbcf84abe3de5ea3df1866/contourpy-1.3.1.tar.gz", hash = "sha256:dfd97abd83335045a913e3bcc4a09c0ceadbe66580cf573fe961f4a825efa699", size = 13465753 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/a3/80937fe3efe0edacf67c9a20b955139a1a622730042c1ea991956f2704ad/contourpy-1.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a045f341a77b77e1c5de31e74e966537bba9f3c4099b35bf4c2e3939dd54cdab", size = 268466 }, - { url = "https://files.pythonhosted.org/packages/82/1d/e3eaebb4aa2d7311528c048350ca8e99cdacfafd99da87bc0a5f8d81f2c2/contourpy-1.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:500360b77259914f7805af7462e41f9cb7ca92ad38e9f94d6c8641b089338124", size = 253314 }, - { url = "https://files.pythonhosted.org/packages/de/f3/d796b22d1a2b587acc8100ba8c07fb7b5e17fde265a7bb05ab967f4c935a/contourpy-1.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2f926efda994cdf3c8d3fdb40b9962f86edbc4457e739277b961eced3d0b4c1", size = 312003 }, - { url = "https://files.pythonhosted.org/packages/bf/f5/0e67902bc4394daee8daa39c81d4f00b50e063ee1a46cb3938cc65585d36/contourpy-1.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adce39d67c0edf383647a3a007de0a45fd1b08dedaa5318404f1a73059c2512b", size = 351896 }, - { url = "https://files.pythonhosted.org/packages/1f/d6/e766395723f6256d45d6e67c13bb638dd1fa9dc10ef912dc7dd3dcfc19de/contourpy-1.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abbb49fb7dac584e5abc6636b7b2a7227111c4f771005853e7d25176daaf8453", size = 320814 }, - { url = "https://files.pythonhosted.org/packages/a9/57/86c500d63b3e26e5b73a28b8291a67c5608d4aa87ebd17bd15bb33c178bc/contourpy-1.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0cffcbede75c059f535725c1680dfb17b6ba8753f0c74b14e6a9c68c29d7ea3", size = 324969 }, - { url = "https://files.pythonhosted.org/packages/b8/62/bb146d1289d6b3450bccc4642e7f4413b92ebffd9bf2e91b0404323704a7/contourpy-1.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab29962927945d89d9b293eabd0d59aea28d887d4f3be6c22deaefbb938a7277", size = 1265162 }, - { url = "https://files.pythonhosted.org/packages/18/04/9f7d132ce49a212c8e767042cc80ae390f728060d2eea47058f55b9eff1c/contourpy-1.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:974d8145f8ca354498005b5b981165b74a195abfae9a8129df3e56771961d595", size = 1324328 }, - { url = "https://files.pythonhosted.org/packages/46/23/196813901be3f97c83ababdab1382e13e0edc0bb4e7b49a7bff15fcf754e/contourpy-1.3.1-cp310-cp310-win32.whl", hash = "sha256:ac4578ac281983f63b400f7fe6c101bedc10651650eef012be1ccffcbacf3697", size = 173861 }, - { url = "https://files.pythonhosted.org/packages/e0/82/c372be3fc000a3b2005061ca623a0d1ecd2eaafb10d9e883a2fc8566e951/contourpy-1.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:174e758c66bbc1c8576992cec9599ce8b6672b741b5d336b5c74e35ac382b18e", size = 218566 }, { url = "https://files.pythonhosted.org/packages/12/bb/11250d2906ee2e8b466b5f93e6b19d525f3e0254ac8b445b56e618527718/contourpy-1.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3e8b974d8db2c5610fb4e76307e265de0edb655ae8169e8b21f41807ccbeec4b", size = 269555 }, { url = "https://files.pythonhosted.org/packages/67/71/1e6e95aee21a500415f5d2dbf037bf4567529b6a4e986594d7026ec5ae90/contourpy-1.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:20914c8c973f41456337652a6eeca26d2148aa96dd7ac323b74516988bea89fc", size = 254549 }, { url = "https://files.pythonhosted.org/packages/31/2c/b88986e8d79ac45efe9d8801ae341525f38e087449b6c2f2e6050468a42c/contourpy-1.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19d40d37c1c3a4961b4619dd9d77b12124a453cc3d02bb31a07d58ef684d3d86", size = 313000 }, @@ -283,9 +245,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f0/ed/92d86f183a8615f13f6b9cbfc5d4298a509d6ce433432e21da838b4b63f4/contourpy-1.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:89785bb2a1980c1bd87f0cb1517a71cde374776a5f150936b82580ae6ead44a1", size = 1318403 }, { url = "https://files.pythonhosted.org/packages/b3/0e/c8e4950c77dcfc897c71d61e56690a0a9df39543d2164040301b5df8e67b/contourpy-1.3.1-cp313-cp313t-win32.whl", hash = "sha256:8eb96e79b9f3dcadbad2a3891672f81cdcab7f95b27f28f1c67d75f045b6b4f1", size = 185117 }, { url = "https://files.pythonhosted.org/packages/c1/31/1ae946f11dfbd229222e6d6ad8e7bd1891d3d48bde5fbf7a0beb9491f8e3/contourpy-1.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:287ccc248c9e0d0566934e7d606201abd74761b5703d804ff3df8935f523d546", size = 236668 }, - { url = "https://files.pythonhosted.org/packages/3e/4f/e56862e64b52b55b5ddcff4090085521fc228ceb09a88390a2b103dccd1b/contourpy-1.3.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b457d6430833cee8e4b8e9b6f07aa1c161e5e0d52e118dc102c8f9bd7dd060d6", size = 265605 }, - { url = "https://files.pythonhosted.org/packages/b0/2e/52bfeeaa4541889f23d8eadc6386b442ee2470bd3cff9baa67deb2dd5c57/contourpy-1.3.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb76c1a154b83991a3cbbf0dfeb26ec2833ad56f95540b442c73950af2013750", size = 315040 }, - { url = "https://files.pythonhosted.org/packages/52/94/86bfae441707205634d80392e873295652fc313dfd93c233c52c4dc07874/contourpy-1.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:44a29502ca9c7b5ba389e620d44f2fbe792b1fb5734e8b931ad307071ec58c53", size = 218221 }, ] [[package]] @@ -294,16 +253,6 @@ version = "7.8.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/19/4f/2251e65033ed2ce1e68f00f91a0294e0f80c80ae8c3ebbe2f12828c4cd53/coverage-7.8.0.tar.gz", hash = "sha256:7a3d62b3b03b4b6fd41a085f3574874cf946cb4604d2b4d3e8dca8cd570ca501", size = 811872 } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/01/1c5e6ee4ebaaa5e079db933a9a45f61172048c7efa06648445821a201084/coverage-7.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2931f66991175369859b5fd58529cd4b73582461877ecfd859b6549869287ffe", size = 211379 }, - { url = "https://files.pythonhosted.org/packages/e9/16/a463389f5ff916963471f7c13585e5f38c6814607306b3cb4d6b4cf13384/coverage-7.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52a523153c568d2c0ef8826f6cc23031dc86cffb8c6aeab92c4ff776e7951b28", size = 211814 }, - { url = "https://files.pythonhosted.org/packages/b8/b1/77062b0393f54d79064dfb72d2da402657d7c569cfbc724d56ac0f9c67ed/coverage-7.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c8a5c139aae4c35cbd7cadca1df02ea8cf28a911534fc1b0456acb0b14234f3", size = 240937 }, - { url = "https://files.pythonhosted.org/packages/d7/54/c7b00a23150083c124e908c352db03bcd33375494a4beb0c6d79b35448b9/coverage-7.8.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a26c0c795c3e0b63ec7da6efded5f0bc856d7c0b24b2ac84b4d1d7bc578d676", size = 238849 }, - { url = "https://files.pythonhosted.org/packages/f7/ec/a6b7cfebd34e7b49f844788fda94713035372b5200c23088e3bbafb30970/coverage-7.8.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:821f7bcbaa84318287115d54becb1915eece6918136c6f91045bb84e2f88739d", size = 239986 }, - { url = "https://files.pythonhosted.org/packages/21/8c/c965ecef8af54e6d9b11bfbba85d4f6a319399f5f724798498387f3209eb/coverage-7.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a321c61477ff8ee705b8a5fed370b5710c56b3a52d17b983d9215861e37b642a", size = 239896 }, - { url = "https://files.pythonhosted.org/packages/40/83/070550273fb4c480efa8381735969cb403fa8fd1626d74865bfaf9e4d903/coverage-7.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ed2144b8a78f9d94d9515963ed273d620e07846acd5d4b0a642d4849e8d91a0c", size = 238613 }, - { url = "https://files.pythonhosted.org/packages/07/76/fbb2540495b01d996d38e9f8897b861afed356be01160ab4e25471f4fed1/coverage-7.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:042e7841a26498fff7a37d6fda770d17519982f5b7d8bf5278d140b67b61095f", size = 238909 }, - { url = "https://files.pythonhosted.org/packages/a3/7e/76d604db640b7d4a86e5dd730b73e96e12a8185f22b5d0799025121f4dcb/coverage-7.8.0-cp310-cp310-win32.whl", hash = "sha256:f9983d01d7705b2d1f7a95e10bbe4091fabc03a46881a256c2787637b087003f", size = 213948 }, - { url = "https://files.pythonhosted.org/packages/5c/a7/f8ce4aafb4a12ab475b56c76a71a40f427740cf496c14e943ade72e25023/coverage-7.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:5a570cd9bd20b85d1a0d7b009aaf6c110b52b5755c17be6962f8ccd65d1dbd23", size = 214844 }, { url = "https://files.pythonhosted.org/packages/2b/77/074d201adb8383addae5784cb8e2dac60bb62bfdf28b2b10f3a3af2fda47/coverage-7.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7ac22a0bb2c7c49f441f7a6d46c9c80d96e56f5a8bc6972529ed43c8b694e27", size = 211493 }, { url = "https://files.pythonhosted.org/packages/a9/89/7a8efe585750fe59b48d09f871f0e0c028a7b10722b2172dfe021fa2fdd4/coverage-7.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf13d564d310c156d1c8e53877baf2993fb3073b2fc9f69790ca6a732eb4bfea", size = 211921 }, { url = "https://files.pythonhosted.org/packages/e9/ef/96a90c31d08a3f40c49dbe897df4f1fd51fb6583821a1a1c5ee30cc8f680/coverage-7.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5761c70c017c1b0d21b0815a920ffb94a670c8d5d409d9b38857874c21f70d7", size = 244556 }, @@ -380,15 +329,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 }, ] -[[package]] -name = "exceptiongroup" -version = "1.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, -] - [[package]] name = "filelock" version = "3.18.0" @@ -445,14 +385,6 @@ version = "4.56.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/1c/8c/9ffa2a555af0e5e5d0e2ed7fdd8c9bef474ed676995bb4c57c9cd0014248/fonttools-4.56.0.tar.gz", hash = "sha256:a114d1567e1a1586b7e9e7fc2ff686ca542a82769a296cef131e4c4af51e58f4", size = 3462892 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/5e/6ac30c2cc6a29454260f13c9c6422fc509b7982c13cd4597041260d8f482/fonttools-4.56.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:331954d002dbf5e704c7f3756028e21db07097c19722569983ba4d74df014000", size = 2752190 }, - { url = "https://files.pythonhosted.org/packages/92/3a/ac382a8396d1b420ee45eeb0f65b614a9ca7abbb23a1b17524054f0f2200/fonttools-4.56.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8d1613abd5af2f93c05867b3a3759a56e8bf97eb79b1da76b2bc10892f96ff16", size = 2280624 }, - { url = "https://files.pythonhosted.org/packages/8a/ae/00b58bfe20e9ff7fbc3dda38f5d127913942b5e252288ea9583099a31bf5/fonttools-4.56.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:705837eae384fe21cee5e5746fd4f4b2f06f87544fa60f60740007e0aa600311", size = 4562074 }, - { url = "https://files.pythonhosted.org/packages/46/d0/0004ca8f6a200252e5bd6982ed99b5fe58c4c59efaf5f516621c4cd8f703/fonttools-4.56.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc871904a53a9d4d908673c6faa15689874af1c7c5ac403a8e12d967ebd0c0dc", size = 4604747 }, - { url = "https://files.pythonhosted.org/packages/45/ea/c8862bd3e09d143ef8ed8268ec8a7d477828f960954889e65288ac050b08/fonttools-4.56.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:38b947de71748bab150259ee05a775e8a0635891568e9fdb3cdd7d0e0004e62f", size = 4559025 }, - { url = "https://files.pythonhosted.org/packages/8f/75/bb88a9552ec1de31a414066257bfd9f40f4ada00074f7a3799ea39b5741f/fonttools-4.56.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:86b2a1013ef7a64d2e94606632683f07712045ed86d937c11ef4dde97319c086", size = 4728482 }, - { url = "https://files.pythonhosted.org/packages/2a/5f/80a2b640df1e1bb7d459d62c8b3f37fe83fd413897e549106d4ebe6371f5/fonttools-4.56.0-cp310-cp310-win32.whl", hash = "sha256:133bedb9a5c6376ad43e6518b7e2cd2f866a05b1998f14842631d5feb36b5786", size = 2155557 }, - { url = "https://files.pythonhosted.org/packages/8f/85/0904f9dbe51ac70d878d3242a8583b9453a09105c3ed19c6301247fd0d3a/fonttools-4.56.0-cp310-cp310-win_amd64.whl", hash = "sha256:17f39313b649037f6c800209984a11fc256a6137cbe5487091c6c7187cae4685", size = 2200017 }, { url = "https://files.pythonhosted.org/packages/35/56/a2f3e777d48fcae7ecd29de4d96352d84e5ea9871e5f3fc88241521572cf/fonttools-4.56.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ef04bc7827adb7532be3d14462390dd71287644516af3f1e67f1e6ff9c6d6df", size = 2753325 }, { url = "https://files.pythonhosted.org/packages/71/85/d483e9c4e5ed586b183bf037a353e8d766366b54fd15519b30e6178a6a6e/fonttools-4.56.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ffda9b8cd9cb8b301cae2602ec62375b59e2e2108a117746f12215145e3f786c", size = 2281554 }, { url = "https://files.pythonhosted.org/packages/09/67/060473b832b2fade03c127019794df6dc02d9bc66fa4210b8e0d8a99d1e5/fonttools-4.56.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e993e8db36306cc3f1734edc8ea67906c55f98683d6fd34c3fc5593fdbba4c", size = 4869260 }, @@ -487,8 +419,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "beautifulsoup4" }, { name = "pygments" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx" }, { name = "sphinx-basic-ng" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a0/e2/d351d69a9a9e4badb4a5be062c2d0e87bd9e6c23b5e57337fef14bef34c8/furo-2024.8.6.tar.gz", hash = "sha256:b63e4cee8abfc3136d3bc03a3d45a76a850bada4d6374d24c1716b0e01394a01", size = 1661506 } @@ -562,21 +493,6 @@ version = "1.4.8" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/82/59/7c91426a8ac292e1cdd53a63b6d9439abd573c875c3f92c146767dd33faf/kiwisolver-1.4.8.tar.gz", hash = "sha256:23d5f023bdc8c7e54eb65f03ca5d5bb25b601eac4d7f1a042888a1f45237987e", size = 97538 } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/5f/4d8e9e852d98ecd26cdf8eaf7ed8bc33174033bba5e07001b289f07308fd/kiwisolver-1.4.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88c6f252f6816a73b1f8c904f7bbe02fd67c09a69f7cb8a0eecdbf5ce78e63db", size = 124623 }, - { url = "https://files.pythonhosted.org/packages/1d/70/7f5af2a18a76fe92ea14675f8bd88ce53ee79e37900fa5f1a1d8e0b42998/kiwisolver-1.4.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c72941acb7b67138f35b879bbe85be0f6c6a70cab78fe3ef6db9c024d9223e5b", size = 66720 }, - { url = "https://files.pythonhosted.org/packages/c6/13/e15f804a142353aefd089fadc8f1d985561a15358c97aca27b0979cb0785/kiwisolver-1.4.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce2cf1e5688edcb727fdf7cd1bbd0b6416758996826a8be1d958f91880d0809d", size = 65413 }, - { url = "https://files.pythonhosted.org/packages/ce/6d/67d36c4d2054e83fb875c6b59d0809d5c530de8148846b1370475eeeece9/kiwisolver-1.4.8-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c8bf637892dc6e6aad2bc6d4d69d08764166e5e3f69d469e55427b6ac001b19d", size = 1650826 }, - { url = "https://files.pythonhosted.org/packages/de/c6/7b9bb8044e150d4d1558423a1568e4f227193662a02231064e3824f37e0a/kiwisolver-1.4.8-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:034d2c891f76bd3edbdb3ea11140d8510dca675443da7304205a2eaa45d8334c", size = 1628231 }, - { url = "https://files.pythonhosted.org/packages/b6/38/ad10d437563063eaaedbe2c3540a71101fc7fb07a7e71f855e93ea4de605/kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47b28d1dfe0793d5e96bce90835e17edf9a499b53969b03c6c47ea5985844c3", size = 1408938 }, - { url = "https://files.pythonhosted.org/packages/52/ce/c0106b3bd7f9e665c5f5bc1e07cc95b5dabd4e08e3dad42dbe2faad467e7/kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb158fe28ca0c29f2260cca8c43005329ad58452c36f0edf298204de32a9a3ed", size = 1422799 }, - { url = "https://files.pythonhosted.org/packages/d0/87/efb704b1d75dc9758087ba374c0f23d3254505edaedd09cf9d247f7878b9/kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5536185fce131780ebd809f8e623bf4030ce1b161353166c49a3c74c287897f", size = 1354362 }, - { url = "https://files.pythonhosted.org/packages/eb/b3/fd760dc214ec9a8f208b99e42e8f0130ff4b384eca8b29dd0efc62052176/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:369b75d40abedc1da2c1f4de13f3482cb99e3237b38726710f4a793432b1c5ff", size = 2222695 }, - { url = "https://files.pythonhosted.org/packages/a2/09/a27fb36cca3fc01700687cc45dae7a6a5f8eeb5f657b9f710f788748e10d/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:641f2ddf9358c80faa22e22eb4c9f54bd3f0e442e038728f500e3b978d00aa7d", size = 2370802 }, - { url = "https://files.pythonhosted.org/packages/3d/c3/ba0a0346db35fe4dc1f2f2cf8b99362fbb922d7562e5f911f7ce7a7b60fa/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d561d2d8883e0819445cfe58d7ddd673e4015c3c57261d7bdcd3710d0d14005c", size = 2334646 }, - { url = "https://files.pythonhosted.org/packages/41/52/942cf69e562f5ed253ac67d5c92a693745f0bed3c81f49fc0cbebe4d6b00/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1732e065704b47c9afca7ffa272f845300a4eb959276bf6970dc07265e73b605", size = 2467260 }, - { url = "https://files.pythonhosted.org/packages/32/26/2d9668f30d8a494b0411d4d7d4ea1345ba12deb6a75274d58dd6ea01e951/kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bcb1ebc3547619c3b58a39e2448af089ea2ef44b37988caf432447374941574e", size = 2288633 }, - { url = "https://files.pythonhosted.org/packages/98/99/0dd05071654aa44fe5d5e350729961e7bb535372935a45ac89a8924316e6/kiwisolver-1.4.8-cp310-cp310-win_amd64.whl", hash = "sha256:89c107041f7b27844179ea9c85d6da275aa55ecf28413e87624d033cf1f6b751", size = 71885 }, - { url = "https://files.pythonhosted.org/packages/6c/fc/822e532262a97442989335394d441cd1d0448c2e46d26d3e04efca84df22/kiwisolver-1.4.8-cp310-cp310-win_arm64.whl", hash = "sha256:b5773efa2be9eb9fcf5415ea3ab70fc785d598729fd6057bea38d539ead28271", size = 65175 }, { url = "https://files.pythonhosted.org/packages/da/ed/c913ee28936c371418cb167b128066ffb20bbf37771eecc2c97edf8a6e4c/kiwisolver-1.4.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a4d3601908c560bdf880f07d94f31d734afd1bb71e96585cace0e38ef44c6d84", size = 124635 }, { url = "https://files.pythonhosted.org/packages/4c/45/4a7f896f7467aaf5f56ef093d1f329346f3b594e77c6a3c327b2d415f521/kiwisolver-1.4.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:856b269c4d28a5c0d5e6c1955ec36ebfd1651ac00e1ce0afa3e28da95293b561", size = 66717 }, { url = "https://files.pythonhosted.org/packages/5f/b4/c12b3ac0852a3a68f94598d4c8d569f55361beef6159dce4e7b624160da2/kiwisolver-1.4.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c2b9a96e0f326205af81a15718a9073328df1173a2619a68553decb7097fd5d7", size = 65413 }, @@ -635,12 +551,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/05/f9/27e94c1b3eb29e6933b6986ffc5fa1177d2cd1f0c8efc5f02c91c9ac61de/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:151dffc4865e5fe6dafce5480fab84f950d14566c480c08a53c663a0020504b6", size = 2390661 }, { url = "https://files.pythonhosted.org/packages/d9/d4/3c9735faa36ac591a4afcc2980d2691000506050b7a7e80bcfe44048daa7/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:577facaa411c10421314598b50413aa1ebcf5126f704f1e5d72d7e4e9f020d90", size = 2546710 }, { url = "https://files.pythonhosted.org/packages/4c/fa/be89a49c640930180657482a74970cdcf6f7072c8d2471e1babe17a222dc/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:be4816dc51c8a471749d664161b434912eee82f2ea66bd7628bd14583a833e85", size = 2349213 }, - { url = "https://files.pythonhosted.org/packages/1f/f9/ae81c47a43e33b93b0a9819cac6723257f5da2a5a60daf46aa5c7226ea85/kiwisolver-1.4.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e7a019419b7b510f0f7c9dceff8c5eae2392037eae483a7f9162625233802b0a", size = 60403 }, - { url = "https://files.pythonhosted.org/packages/58/ca/f92b5cb6f4ce0c1ebfcfe3e2e42b96917e16f7090e45b21102941924f18f/kiwisolver-1.4.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:286b18e86682fd2217a48fc6be6b0f20c1d0ed10958d8dc53453ad58d7be0bf8", size = 58657 }, - { url = "https://files.pythonhosted.org/packages/80/28/ae0240f732f0484d3a4dc885d055653c47144bdf59b670aae0ec3c65a7c8/kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4191ee8dfd0be1c3666ccbac178c5a05d5f8d689bbe3fc92f3c4abec817f8fe0", size = 84948 }, - { url = "https://files.pythonhosted.org/packages/5d/eb/78d50346c51db22c7203c1611f9b513075f35c4e0e4877c5dde378d66043/kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cd2785b9391f2873ad46088ed7599a6a71e762e1ea33e87514b1a441ed1da1c", size = 81186 }, - { url = "https://files.pythonhosted.org/packages/43/f8/7259f18c77adca88d5f64f9a522792e178b2691f3748817a8750c2d216ef/kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c07b29089b7ba090b6f1a669f1411f27221c3662b3a1b7010e67b59bb5a6f10b", size = 80279 }, - { url = "https://files.pythonhosted.org/packages/3a/1d/50ad811d1c5dae091e4cf046beba925bcae0a610e79ae4c538f996f63ed5/kiwisolver-1.4.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:65ea09a5a3faadd59c2ce96dc7bf0f364986a315949dc6374f04396b0d60e09b", size = 71762 }, ] [[package]] @@ -652,34 +562,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2d/00/d90b10b962b4277f5e64a78b6609968859ff86889f5b898c1a778c06ec00/lark-1.2.2-py3-none-any.whl", hash = "sha256:c2276486b02f0f1b90be155f2c8ba4a8e194d42775786db622faccd652d8e80c", size = 111036 }, ] -[[package]] -name = "libcosimpy" -version = "0.0.3.post2" -source = { git = "https://github.com/open-simulation-platform/libcosimpy?rev=cosimc-loading-fix#f7c6eb5ad2bdf67807834c1321927468c188753f" } - [[package]] name = "lxml" version = "5.3.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/ef/f6/c15ca8e5646e937c148e147244817672cf920b56ac0bf2cc1512ae674be8/lxml-5.3.1.tar.gz", hash = "sha256:106b7b5d2977b339f1e97efe2778e2ab20e99994cbb0ec5e55771ed0795920c8", size = 3678591 } wheels = [ - { url = "https://files.pythonhosted.org/packages/80/4b/73426192004a643c11a644ed2346dbe72da164c8e775ea2e70f60e63e516/lxml-5.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a4058f16cee694577f7e4dd410263cd0ef75644b43802a689c2b3c2a7e69453b", size = 8142766 }, - { url = "https://files.pythonhosted.org/packages/30/c2/3b28f642b43fdf9580d936e8fdd3ec43c01a97ecfe17fd67f76ce9099752/lxml-5.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:364de8f57d6eda0c16dcfb999af902da31396949efa0e583e12675d09709881b", size = 4422744 }, - { url = "https://files.pythonhosted.org/packages/1f/a5/45279e464174b99d72d25bc018b097f9211c0925a174ca582a415609f036/lxml-5.3.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:528f3a0498a8edc69af0559bdcf8a9f5a8bf7c00051a6ef3141fdcf27017bbf5", size = 5229609 }, - { url = "https://files.pythonhosted.org/packages/f0/e7/10cd8b9e27ffb6b3465b76604725b67b7c70d4e399750ff88de1b38ab9eb/lxml-5.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db4743e30d6f5f92b6d2b7c86b3ad250e0bad8dee4b7ad8a0c44bfb276af89a3", size = 4943509 }, - { url = "https://files.pythonhosted.org/packages/ce/54/2d6f634924920b17122445136345d44c6d69178c9c49e161aa8f206739d6/lxml-5.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:17b5d7f8acf809465086d498d62a981fa6a56d2718135bb0e4aa48c502055f5c", size = 5561495 }, - { url = "https://files.pythonhosted.org/packages/a2/fe/7f5ae8fd1f357fcb21b0d4e20416fae870d654380b6487adbcaaf0df9b31/lxml-5.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:928e75a7200a4c09e6efc7482a1337919cc61fe1ba289f297827a5b76d8969c2", size = 4998970 }, - { url = "https://files.pythonhosted.org/packages/af/70/22fecb6f2ca8dc77d14ab6be3cef767ff8340040bc95dca384b5b1cb333a/lxml-5.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a997b784a639e05b9d4053ef3b20c7e447ea80814a762f25b8ed5a89d261eac", size = 5114205 }, - { url = "https://files.pythonhosted.org/packages/63/91/21619cc14f7fd1de3f1bdf86cc8106edacf4d685b540d658d84247a3a32a/lxml-5.3.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:7b82e67c5feb682dbb559c3e6b78355f234943053af61606af126df2183b9ef9", size = 4940823 }, - { url = "https://files.pythonhosted.org/packages/50/0f/27183248fa3cdd2040047ceccd320ff1ed1344167f38a4ac26aed092268b/lxml-5.3.1-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:f1de541a9893cf8a1b1db9bf0bf670a2decab42e3e82233d36a74eda7822b4c9", size = 5585725 }, - { url = "https://files.pythonhosted.org/packages/c6/8d/9b7388d5b23ed2f239a992a478cbd0ce313aaa2d008dd73c4042b190b6a9/lxml-5.3.1-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:de1fc314c3ad6bc2f6bd5b5a5b9357b8c6896333d27fdbb7049aea8bd5af2d79", size = 5082641 }, - { url = "https://files.pythonhosted.org/packages/65/8e/590e20833220eac55b6abcde71d3ae629d38ac1c3543bcc2bfe1f3c2f5d1/lxml-5.3.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:7c0536bd9178f754b277a3e53f90f9c9454a3bd108b1531ffff720e082d824f2", size = 5161219 }, - { url = "https://files.pythonhosted.org/packages/4e/77/cabdf5569fd0415a88ebd1d62d7f2814e71422439b8564aaa03e7eefc069/lxml-5.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:68018c4c67d7e89951a91fbd371e2e34cd8cfc71f0bb43b5332db38497025d51", size = 5019293 }, - { url = "https://files.pythonhosted.org/packages/49/bd/f0b6d50ea7b8b54aaa5df4410cb1d5ae6ffa016b8e0503cae08b86c24674/lxml-5.3.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:aa826340a609d0c954ba52fd831f0fba2a4165659ab0ee1a15e4aac21f302406", size = 5651232 }, - { url = "https://files.pythonhosted.org/packages/fa/69/1793d00a4e3da7f27349edb5a6f3da947ed921263cd9a243fab11c6cbc07/lxml-5.3.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:796520afa499732191e39fc95b56a3b07f95256f2d22b1c26e217fb69a9db5b5", size = 5489527 }, - { url = "https://files.pythonhosted.org/packages/d3/c9/e2449129b6cb2054c898df8d850ea4dadd75b4c33695a6c4b0f35082f1e7/lxml-5.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3effe081b3135237da6e4c4530ff2a868d3f80be0bda027e118a5971285d42d0", size = 5227050 }, - { url = "https://files.pythonhosted.org/packages/ed/63/e5da540eba6ab9a0d4188eeaa5c85767b77cafa8efeb70da0593d6cd3b81/lxml-5.3.1-cp310-cp310-win32.whl", hash = "sha256:a22f66270bd6d0804b02cd49dae2b33d4341015545d17f8426f2c4e22f557a23", size = 3475345 }, - { url = "https://files.pythonhosted.org/packages/08/71/853a3ad812cd24c35b7776977cb0ae40c2b64ff79ad6d6c36c987daffc49/lxml-5.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:0bcfadea3cdc68e678d2b20cb16a16716887dd00a881e16f7d806c2138b8ff0c", size = 3805093 }, { url = "https://files.pythonhosted.org/packages/57/bb/2faea15df82114fa27f2a86eec220506c532ee8ce211dff22f48881b353a/lxml-5.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e220f7b3e8656ab063d2eb0cd536fafef396829cafe04cb314e734f87649058f", size = 8161781 }, { url = "https://files.pythonhosted.org/packages/9f/d3/374114084abb1f96026eccb6cd48b070f85de82fdabae6c2f1e198fa64e5/lxml-5.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f2cfae0688fd01f7056a17367e3b84f37c545fb447d7282cf2c242b16262607", size = 4432571 }, { url = "https://files.pythonhosted.org/packages/0f/fb/44a46efdc235c2dd763c1e929611d8ff3b920c32b8fcd9051d38f4d04633/lxml-5.3.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67d2f8ad9dcc3a9e826bdc7802ed541a44e124c29b7d95a679eeb58c1c14ade8", size = 5028919 }, @@ -731,12 +619,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/39/25/ad4ac8fac488505a2702656550e63c2a8db3a4fd63db82a20dad5689cecb/lxml-5.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dbf7bebc2275016cddf3c997bf8a0f7044160714c64a9b83975670a04e6d2252", size = 5050951 }, { url = "https://files.pythonhosted.org/packages/82/74/f7d223c704c87e44b3d27b5e0dde173a2fcf2e89c0524c8015c2b3554876/lxml-5.3.1-cp313-cp313-win32.whl", hash = "sha256:d0751528b97d2b19a388b302be2a0ee05817097bab46ff0ed76feeec24951f78", size = 3485357 }, { url = "https://files.pythonhosted.org/packages/80/83/8c54533b3576f4391eebea88454738978669a6cad0d8e23266224007939d/lxml-5.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:91fb6a43d72b4f8863d21f347a9163eecbf36e76e2f51068d59cd004c506f332", size = 3814484 }, - { url = "https://files.pythonhosted.org/packages/d2/b4/89a68d05f267f05cc1b8b2f289a8242955705b1b0a9d246198227817ee46/lxml-5.3.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:afa578b6524ff85fb365f454cf61683771d0170470c48ad9d170c48075f86725", size = 3936118 }, - { url = "https://files.pythonhosted.org/packages/7f/0d/c034a541e7a1153527d7880c62493a74f2277f38e64de2480cadd0d4cf96/lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67f5e80adf0aafc7b5454f2c1cb0cde920c9b1f2cbd0485f07cc1d0497c35c5d", size = 4233690 }, - { url = "https://files.pythonhosted.org/packages/35/5c/38e183c2802f14fbdaa75c3266e11d0ca05c64d78e8cdab2ee84e954a565/lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dd0b80ac2d8f13ffc906123a6f20b459cb50a99222d0da492360512f3e50f84", size = 4349569 }, - { url = "https://files.pythonhosted.org/packages/18/5b/14f93b359b3c29673d5d282bc3a6edb3a629879854a77541841aba37607f/lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:422c179022ecdedbe58b0e242607198580804253da220e9454ffe848daa1cfd2", size = 4236731 }, - { url = "https://files.pythonhosted.org/packages/f6/08/8471de65f3dee70a3a50e7082fd7409f0ac7a1ace777c13fca4aea1a5759/lxml-5.3.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:524ccfded8989a6595dbdda80d779fb977dbc9a7bc458864fc9a0c2fc15dc877", size = 4373119 }, - { url = "https://files.pythonhosted.org/packages/83/29/00b9b0322a473aee6cda87473401c9abb19506cd650cc69a8aa38277ea74/lxml-5.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:48fd46bf7155def2e15287c6f2b133a2f78e2d22cdf55647269977b873c65499", size = 3487718 }, ] [[package]] @@ -757,16 +639,6 @@ version = "3.0.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357 }, - { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393 }, - { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732 }, - { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866 }, - { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964 }, - { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977 }, - { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366 }, - { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091 }, - { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065 }, - { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514 }, { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 }, { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 }, { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 }, @@ -826,12 +698,6 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/2f/08/b89867ecea2e305f408fbb417139a8dd941ecf7b23a2e02157c36da546f0/matplotlib-3.10.1.tar.gz", hash = "sha256:e8d2d0e3881b129268585bf4765ad3ee73a4591d77b9a18c214ac7e3a79fb2ba", size = 36743335 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/b1/f70e27cf1cd76ce2a5e1aa5579d05afe3236052c6d9b9a96325bc823a17e/matplotlib-3.10.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ff2ae14910be903f4a24afdbb6d7d3a6c44da210fc7d42790b87aeac92238a16", size = 8163654 }, - { url = "https://files.pythonhosted.org/packages/26/af/5ec3d4636106718bb62503a03297125d4514f98fe818461bd9e6b9d116e4/matplotlib-3.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0721a3fd3d5756ed593220a8b86808a36c5031fce489adb5b31ee6dbb47dd5b2", size = 8037943 }, - { url = "https://files.pythonhosted.org/packages/a1/3d/07f9003a71b698b848c9925d05979ffa94a75cd25d1a587202f0bb58aa81/matplotlib-3.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0673b4b8f131890eb3a1ad058d6e065fb3c6e71f160089b65f8515373394698", size = 8449510 }, - { url = "https://files.pythonhosted.org/packages/12/87/9472d4513ff83b7cd864311821793ab72234fa201ab77310ec1b585d27e2/matplotlib-3.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e875b95ac59a7908978fe307ecdbdd9a26af7fa0f33f474a27fcf8c99f64a19", size = 8586585 }, - { url = "https://files.pythonhosted.org/packages/31/9e/fe74d237d2963adae8608faeb21f778cf246dbbf4746cef87cffbc82c4b6/matplotlib-3.10.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2589659ea30726284c6c91037216f64a506a9822f8e50592d48ac16a2f29e044", size = 9397911 }, - { url = "https://files.pythonhosted.org/packages/b6/1b/025d3e59e8a4281ab463162ad7d072575354a1916aba81b6a11507dfc524/matplotlib-3.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:a97ff127f295817bc34517255c9db6e71de8eddaab7f837b7d341dee9f2f587f", size = 8052998 }, { url = "https://files.pythonhosted.org/packages/a5/14/a1b840075be247bb1834b22c1e1d558740b0f618fe3a823740181ca557a1/matplotlib-3.10.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:057206ff2d6ab82ff3e94ebd94463d084760ca682ed5f150817b859372ec4401", size = 8174669 }, { url = "https://files.pythonhosted.org/packages/0a/e4/300b08e3e08f9c98b0d5635f42edabf2f7a1d634e64cb0318a71a44ff720/matplotlib-3.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a144867dd6bf8ba8cb5fc81a158b645037e11b3e5cf8a50bd5f9917cb863adfe", size = 8047996 }, { url = "https://files.pythonhosted.org/packages/75/f9/8d99ff5a2498a5f1ccf919fb46fb945109623c6108216f10f96428f388bc/matplotlib-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56c5d9fcd9879aa8040f196a235e2dcbdf7dd03ab5b07c0696f80bc6cf04bedd", size = 8461612 }, @@ -856,9 +722,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c9/db/b05bf463689134789b06dea85828f8ebe506fa1e37593f723b65b86c9582/matplotlib-3.10.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3dfb036f34873b46978f55e240cff7a239f6c4409eac62d8145bad3fc6ba5a3", size = 8613864 }, { url = "https://files.pythonhosted.org/packages/c2/04/41ccec4409f3023a7576df3b5c025f1a8c8b81fbfe922ecfd837ac36e081/matplotlib-3.10.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dc6ab14a7ab3b4d813b88ba957fc05c79493a037f54e246162033591e770de6f", size = 9409487 }, { url = "https://files.pythonhosted.org/packages/ac/c2/0d5aae823bdcc42cc99327ecdd4d28585e15ccd5218c453b7bcd827f3421/matplotlib-3.10.1-cp313-cp313t-win_amd64.whl", hash = "sha256:bc411ebd5889a78dabbc457b3fa153203e22248bfa6eedc6797be5df0164dbf9", size = 8134832 }, - { url = "https://files.pythonhosted.org/packages/c8/f6/10adb696d8cbeed2ab4c2e26ecf1c80dd3847bbf3891f4a0c362e0e08a5a/matplotlib-3.10.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:648406f1899f9a818cef8c0231b44dcfc4ff36f167101c3fd1c9151f24220fdc", size = 8158685 }, - { url = "https://files.pythonhosted.org/packages/3f/84/0603d917406072763e7f9bb37747d3d74d7ecd4b943a8c947cc3ae1cf7af/matplotlib-3.10.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:02582304e352f40520727984a5a18f37e8187861f954fea9be7ef06569cf85b4", size = 8035491 }, - { url = "https://files.pythonhosted.org/packages/fd/7d/6a8b31dd07ed856b3eae001c9129670ef75c4698fa1c2a6ac9f00a4a7054/matplotlib-3.10.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3809916157ba871bcdd33d3493acd7fe3037db5daa917ca6e77975a94cef779", size = 8590087 }, ] [[package]] @@ -882,32 +745,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, ] -[[package]] -name = "mpmath" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, -] - [[package]] name = "msgpack" version = "1.1.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/cb/d0/7555686ae7ff5731205df1012ede15dd9d927f6227ea151e901c7406af4f/msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e", size = 167260 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/f9/a892a6038c861fa849b11a2bb0502c07bc698ab6ea53359e5771397d883b/msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd", size = 150428 }, - { url = "https://files.pythonhosted.org/packages/df/7a/d174cc6a3b6bb85556e6a046d3193294a92f9a8e583cdbd46dc8a1d7e7f4/msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d", size = 84131 }, - { url = "https://files.pythonhosted.org/packages/08/52/bf4fbf72f897a23a56b822997a72c16de07d8d56d7bf273242f884055682/msgpack-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5", size = 81215 }, - { url = "https://files.pythonhosted.org/packages/02/95/dc0044b439b518236aaf012da4677c1b8183ce388411ad1b1e63c32d8979/msgpack-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5", size = 371229 }, - { url = "https://files.pythonhosted.org/packages/ff/75/09081792db60470bef19d9c2be89f024d366b1e1973c197bb59e6aabc647/msgpack-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e", size = 378034 }, - { url = "https://files.pythonhosted.org/packages/32/d3/c152e0c55fead87dd948d4b29879b0f14feeeec92ef1fd2ec21b107c3f49/msgpack-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b", size = 363070 }, - { url = "https://files.pythonhosted.org/packages/d9/2c/82e73506dd55f9e43ac8aa007c9dd088c6f0de2aa19e8f7330e6a65879fc/msgpack-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f", size = 359863 }, - { url = "https://files.pythonhosted.org/packages/cb/a0/3d093b248837094220e1edc9ec4337de3443b1cfeeb6e0896af8ccc4cc7a/msgpack-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68", size = 368166 }, - { url = "https://files.pythonhosted.org/packages/e4/13/7646f14f06838b406cf5a6ddbb7e8dc78b4996d891ab3b93c33d1ccc8678/msgpack-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b", size = 370105 }, - { url = "https://files.pythonhosted.org/packages/67/fa/dbbd2443e4578e165192dabbc6a22c0812cda2649261b1264ff515f19f15/msgpack-1.1.0-cp310-cp310-win32.whl", hash = "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044", size = 68513 }, - { url = "https://files.pythonhosted.org/packages/24/ce/c2c8fbf0ded750cb63cbcbb61bc1f2dfd69e16dca30a8af8ba80ec182dcd/msgpack-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f", size = 74687 }, { url = "https://files.pythonhosted.org/packages/b7/5e/a4c7154ba65d93be91f2f1e55f90e76c5f91ccadc7efc4341e6f04c8647f/msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7", size = 150803 }, { url = "https://files.pythonhosted.org/packages/60/c2/687684164698f1d51c41778c838d854965dd284a4b9d3a44beba9265c931/msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa", size = 84343 }, { url = "https://files.pythonhosted.org/packages/42/ae/d3adea9bb4a1342763556078b5765e666f8fdf242e00f3f6657380920972/msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701", size = 81408 }, @@ -949,17 +792,10 @@ version = "1.15.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717 } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/f8/65a7ce8d0e09b6329ad0c8d40330d100ea343bd4dd04c4f8ae26462d0a17/mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13", size = 10738433 }, - { url = "https://files.pythonhosted.org/packages/b4/95/9c0ecb8eacfe048583706249439ff52105b3f552ea9c4024166c03224270/mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559", size = 9861472 }, - { url = "https://files.pythonhosted.org/packages/84/09/9ec95e982e282e20c0d5407bc65031dfd0f0f8ecc66b69538296e06fcbee/mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b", size = 11611424 }, - { url = "https://files.pythonhosted.org/packages/78/13/f7d14e55865036a1e6a0a69580c240f43bc1f37407fe9235c0d4ef25ffb0/mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3", size = 12365450 }, - { url = "https://files.pythonhosted.org/packages/48/e1/301a73852d40c241e915ac6d7bcd7fedd47d519246db2d7b86b9d7e7a0cb/mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b", size = 12551765 }, - { url = "https://files.pythonhosted.org/packages/77/ba/c37bc323ae5fe7f3f15a28e06ab012cd0b7552886118943e90b15af31195/mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828", size = 9274701 }, { url = "https://files.pythonhosted.org/packages/03/bc/f6339726c627bd7ca1ce0fa56c9ae2d0144604a319e0e339bdadafbbb599/mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f", size = 10662338 }, { url = "https://files.pythonhosted.org/packages/e2/90/8dcf506ca1a09b0d17555cc00cd69aee402c203911410136cd716559efe7/mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5", size = 9787540 }, { url = "https://files.pythonhosted.org/packages/05/05/a10f9479681e5da09ef2f9426f650d7b550d4bafbef683b69aad1ba87457/mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e", size = 11538051 }, @@ -1000,8 +836,7 @@ dependencies = [ { name = "markdown-it-py" }, { name = "mdit-py-plugins" }, { name = "pyyaml" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx" }, ] sdist = { url = "https://files.pythonhosted.org/packages/66/a5/9626ba4f73555b3735ad86247a8077d4603aa8628537687c839ab08bfe44/myst_parser-4.0.1.tar.gz", hash = "sha256:5cfea715e4f3574138aecbf7d54132296bfd72bb614d31168f48c477a830a7c4", size = 93985 } wheels = [ @@ -1032,14 +867,6 @@ version = "1.26.4" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468 }, - { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411 }, - { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016 }, - { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889 }, - { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746 }, - { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620 }, - { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659 }, - { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905 }, { url = "https://files.pythonhosted.org/packages/11/57/baae43d14fe163fa0e4c47f307b6b2511ab8d7d30177c491960504252053/numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71", size = 20630554 }, { url = "https://files.pythonhosted.org/packages/1a/2e/151484f49fd03944c4a3ad9c418ed193cfd02724e138ac8a9505d056c582/numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef", size = 13997127 }, { url = "https://files.pythonhosted.org/packages/79/ae/7e5b85136806f9dadf4878bf73cf223fe5c2636818ba3ab1c585d0403164/numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e", size = 14222994 }, @@ -1073,17 +900,6 @@ version = "11.1.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f3/af/c097e544e7bd278333db77933e535098c259609c4eb3b85381109602fb5b/pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20", size = 46742715 } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/1c/2dcea34ac3d7bc96a1fd1bd0a6e06a57c67167fec2cff8d95d88229a8817/pillow-11.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:e1abe69aca89514737465752b4bcaf8016de61b3be1397a8fc260ba33321b3a8", size = 3229983 }, - { url = "https://files.pythonhosted.org/packages/14/ca/6bec3df25e4c88432681de94a3531cc738bd85dea6c7aa6ab6f81ad8bd11/pillow-11.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c640e5a06869c75994624551f45e5506e4256562ead981cce820d5ab39ae2192", size = 3101831 }, - { url = "https://files.pythonhosted.org/packages/d4/2c/668e18e5521e46eb9667b09e501d8e07049eb5bfe39d56be0724a43117e6/pillow-11.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a07dba04c5e22824816b2615ad7a7484432d7f540e6fa86af60d2de57b0fcee2", size = 4314074 }, - { url = "https://files.pythonhosted.org/packages/02/80/79f99b714f0fc25f6a8499ecfd1f810df12aec170ea1e32a4f75746051ce/pillow-11.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e267b0ed063341f3e60acd25c05200df4193e15a4a5807075cd71225a2386e26", size = 4394933 }, - { url = "https://files.pythonhosted.org/packages/81/aa/8d4ad25dc11fd10a2001d5b8a80fdc0e564ac33b293bdfe04ed387e0fd95/pillow-11.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:bd165131fd51697e22421d0e467997ad31621b74bfc0b75956608cb2906dda07", size = 4353349 }, - { url = "https://files.pythonhosted.org/packages/84/7a/cd0c3eaf4a28cb2a74bdd19129f7726277a7f30c4f8424cd27a62987d864/pillow-11.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:abc56501c3fd148d60659aae0af6ddc149660469082859fa7b066a298bde9482", size = 4476532 }, - { url = "https://files.pythonhosted.org/packages/8f/8b/a907fdd3ae8f01c7670dfb1499c53c28e217c338b47a813af8d815e7ce97/pillow-11.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:54ce1c9a16a9561b6d6d8cb30089ab1e5eb66918cb47d457bd996ef34182922e", size = 4279789 }, - { url = "https://files.pythonhosted.org/packages/6f/9a/9f139d9e8cccd661c3efbf6898967a9a337eb2e9be2b454ba0a09533100d/pillow-11.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:73ddde795ee9b06257dac5ad42fcb07f3b9b813f8c1f7f870f402f4dc54b5269", size = 4413131 }, - { url = "https://files.pythonhosted.org/packages/a8/68/0d8d461f42a3f37432203c8e6df94da10ac8081b6d35af1c203bf3111088/pillow-11.1.0-cp310-cp310-win32.whl", hash = "sha256:3a5fe20a7b66e8135d7fd617b13272626a28278d0e578c98720d9ba4b2439d49", size = 2291213 }, - { url = "https://files.pythonhosted.org/packages/14/81/d0dff759a74ba87715509af9f6cb21fa21d93b02b3316ed43bda83664db9/pillow-11.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:b6123aa4a59d75f06e9dd3dac5bf8bc9aa383121bb3dd9a7a612e05eabc9961a", size = 2625725 }, - { url = "https://files.pythonhosted.org/packages/ce/1f/8d50c096a1d58ef0584ddc37e6f602828515219e9d2428e14ce50f5ecad1/pillow-11.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:a76da0a31da6fcae4210aa94fd779c65c75786bc9af06289cd1c184451ef7a65", size = 2375213 }, { url = "https://files.pythonhosted.org/packages/dd/d6/2000bfd8d5414fb70cbbe52c8332f2283ff30ed66a9cde42716c8ecbe22c/pillow-11.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e06695e0326d05b06833b40b7ef477e475d0b1ba3a6d27da1bb48c23209bf457", size = 3229968 }, { url = "https://files.pythonhosted.org/packages/d9/45/3fe487010dd9ce0a06adf9b8ff4f273cc0a44536e234b0fad3532a42c15b/pillow-11.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96f82000e12f23e4f29346e42702b6ed9a2f2fea34a740dd5ffffcc8c539eb35", size = 3101806 }, { url = "https://files.pythonhosted.org/packages/e3/72/776b3629c47d9d5f1c160113158a7a7ad177688d3a1159cd3b62ded5a33a/pillow-11.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3cd561ded2cf2bbae44d4605837221b987c216cff94f49dfeed63488bb228d2", size = 4322283 }, @@ -1125,13 +941,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e5/fb/a7960e838bc5df57a2ce23183bfd2290d97c33028b96bde332a9057834d3/pillow-11.1.0-cp313-cp313t-win32.whl", hash = "sha256:dda60aa465b861324e65a78c9f5cf0f4bc713e4309f83bc387be158b077963d9", size = 2295494 }, { url = "https://files.pythonhosted.org/packages/d7/6c/6ec83ee2f6f0fda8d4cf89045c6be4b0373ebfc363ba8538f8c999f63fcd/pillow-11.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ad5db5781c774ab9a9b2c4302bbf0c1014960a0a7be63278d13ae6fdf88126fe", size = 2631595 }, { url = "https://files.pythonhosted.org/packages/cf/6c/41c21c6c8af92b9fea313aa47c75de49e2f9a467964ee33eb0135d47eb64/pillow-11.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:67cd427c68926108778a9005f2a04adbd5e67c442ed21d95389fe1d595458756", size = 2377651 }, - { url = "https://files.pythonhosted.org/packages/fa/c5/389961578fb677b8b3244fcd934f720ed25a148b9a5cc81c91bdf59d8588/pillow-11.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8c730dc3a83e5ac137fbc92dfcfe1511ce3b2b5d7578315b63dbbb76f7f51d90", size = 3198345 }, - { url = "https://files.pythonhosted.org/packages/c4/fa/803c0e50ffee74d4b965229e816af55276eac1d5806712de86f9371858fd/pillow-11.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:7d33d2fae0e8b170b6a6c57400e077412240f6f5bb2a342cf1ee512a787942bb", size = 3072938 }, - { url = "https://files.pythonhosted.org/packages/dc/67/2a3a5f8012b5d8c63fe53958ba906c1b1d0482ebed5618057ef4d22f8076/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8d65b38173085f24bc07f8b6c505cbb7418009fa1a1fcb111b1f4961814a442", size = 3400049 }, - { url = "https://files.pythonhosted.org/packages/e5/a0/514f0d317446c98c478d1872497eb92e7cde67003fed74f696441e647446/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:015c6e863faa4779251436db398ae75051469f7c903b043a48f078e437656f83", size = 3422431 }, - { url = "https://files.pythonhosted.org/packages/cd/00/20f40a935514037b7d3f87adfc87d2c538430ea625b63b3af8c3f5578e72/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d44ff19eea13ae4acdaaab0179fa68c0c6f2f45d66a4d8ec1eda7d6cecbcc15f", size = 3446208 }, - { url = "https://files.pythonhosted.org/packages/28/3c/7de681727963043e093c72e6c3348411b0185eab3263100d4490234ba2f6/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d3d8da4a631471dfaf94c10c85f5277b1f8e42ac42bade1ac67da4b4a7359b73", size = 3509746 }, - { url = "https://files.pythonhosted.org/packages/41/67/936f9814bdd74b2dfd4822f1f7725ab5d8ff4103919a1664eb4874c58b2f/pillow-11.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4637b88343166249fe8aa94e7c4a62a180c4b3898283bb5d3d2fd5fe10d8e4e0", size = 2626353 }, ] [[package]] @@ -1242,11 +1051,9 @@ version = "8.3.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "iniconfig" }, { name = "packaging" }, { name = "pluggy" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 } wheels = [ @@ -1292,9 +1099,6 @@ name = "pywin32" version = "310" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/da/a5f38fffbba2fb99aa4aa905480ac4b8e83ca486659ac8c95bce47fb5276/pywin32-310-cp310-cp310-win32.whl", hash = "sha256:6dd97011efc8bf51d6793a82292419eba2c71cf8e7250cfac03bba284454abc1", size = 8848240 }, - { url = "https://files.pythonhosted.org/packages/aa/fe/d873a773324fa565619ba555a82c9dabd677301720f3660a731a5d07e49a/pywin32-310-cp310-cp310-win_amd64.whl", hash = "sha256:c3e78706e4229b915a0821941a84e7ef420bf2b77e08c9dae3c76fd03fd2ae3d", size = 9601854 }, - { url = "https://files.pythonhosted.org/packages/3c/84/1a8e3d7a15490d28a5d816efa229ecb4999cdc51a7c30dd8914f669093b8/pywin32-310-cp310-cp310-win_arm64.whl", hash = "sha256:33babed0cf0c92a6f94cc6cc13546ab24ee13e3e800e61ed87609ab91e4c8213", size = 8522963 }, { url = "https://files.pythonhosted.org/packages/f7/b1/68aa2986129fb1011dabbe95f0136f44509afaf072b12b8f815905a39f33/pywin32-310-cp311-cp311-win32.whl", hash = "sha256:1e765f9564e83011a63321bb9d27ec456a0ed90d3732c4b2e312b855365ed8bd", size = 8784284 }, { url = "https://files.pythonhosted.org/packages/b3/bd/d1592635992dd8db5bb8ace0551bc3a769de1ac8850200cfa517e72739fb/pywin32-310-cp311-cp311-win_amd64.whl", hash = "sha256:126298077a9d7c95c53823934f000599f66ec9296b09167810eb24875f32689c", size = 9520748 }, { url = "https://files.pythonhosted.org/packages/90/b1/ac8b1ffce6603849eb45a91cf126c0fa5431f186c2e768bf56889c46f51c/pywin32-310-cp311-cp311-win_arm64.whl", hash = "sha256:19ec5fc9b1d51c4350be7bb00760ffce46e6c95eaf2f0b2f1150657b1a43c582", size = 8455941 }, @@ -1312,15 +1116,6 @@ version = "6.0.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 }, - { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 }, - { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 }, - { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 }, - { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 }, - { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 }, - { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 }, - { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 }, - { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 }, { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, @@ -1408,15 +1203,6 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/b7/b9/31ba9cd990e626574baf93fbc1ac61cf9ed54faafd04c479117517661637/scipy-1.15.2.tar.gz", hash = "sha256:cd58a314d92838f7e6f755c8a2167ead4f27e1fd5c1251fd54289569ef3495ec", size = 59417316 } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/df/ef233fff6838fe6f7840d69b5ef9f20d2b5c912a8727b21ebf876cb15d54/scipy-1.15.2-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a2ec871edaa863e8213ea5df811cd600734f6400b4af272e1c011e69401218e9", size = 38692502 }, - { url = "https://files.pythonhosted.org/packages/5c/20/acdd4efb8a68b842968f7bc5611b1aeb819794508771ad104de418701422/scipy-1.15.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:6f223753c6ea76983af380787611ae1291e3ceb23917393079dcc746ba60cfb5", size = 30085508 }, - { url = "https://files.pythonhosted.org/packages/42/55/39cf96ca7126f1e78ee72a6344ebdc6702fc47d037319ad93221063e6cf4/scipy-1.15.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:ecf797d2d798cf7c838c6d98321061eb3e72a74710e6c40540f0e8087e3b499e", size = 22359166 }, - { url = "https://files.pythonhosted.org/packages/51/48/708d26a4ab8a1441536bf2dfcad1df0ca14a69f010fba3ccbdfc02df7185/scipy-1.15.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:9b18aa747da280664642997e65aab1dd19d0c3d17068a04b3fe34e2559196cb9", size = 25112047 }, - { url = "https://files.pythonhosted.org/packages/dd/65/f9c5755b995ad892020381b8ae11f16d18616208e388621dfacc11df6de6/scipy-1.15.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87994da02e73549dfecaed9e09a4f9d58a045a053865679aeb8d6d43747d4df3", size = 35536214 }, - { url = "https://files.pythonhosted.org/packages/de/3c/c96d904b9892beec978562f64d8cc43f9cca0842e65bd3cd1b7f7389b0ba/scipy-1.15.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69ea6e56d00977f355c0f84eba69877b6df084516c602d93a33812aa04d90a3d", size = 37646981 }, - { url = "https://files.pythonhosted.org/packages/3d/74/c2d8a24d18acdeae69ed02e132b9bc1bb67b7bee90feee1afe05a68f9d67/scipy-1.15.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:888307125ea0c4466287191e5606a2c910963405ce9671448ff9c81c53f85f58", size = 37230048 }, - { url = "https://files.pythonhosted.org/packages/42/19/0aa4ce80eca82d487987eff0bc754f014dec10d20de2f66754fa4ea70204/scipy-1.15.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9412f5e408b397ff5641080ed1e798623dbe1ec0d78e72c9eca8992976fa65aa", size = 40010322 }, - { url = "https://files.pythonhosted.org/packages/d0/d2/f0683b7e992be44d1475cc144d1f1eeae63c73a14f862974b4db64af635e/scipy-1.15.2-cp310-cp310-win_amd64.whl", hash = "sha256:b5e025e903b4f166ea03b109bb241355b9c42c279ea694d8864d033727205e65", size = 41233385 }, { url = "https://files.pythonhosted.org/packages/40/1f/bf0a5f338bda7c35c08b4ed0df797e7bafe8a78a97275e9f439aceb46193/scipy-1.15.2-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:92233b2df6938147be6fa8824b8136f29a18f016ecde986666be5f4d686a91a4", size = 38703651 }, { url = "https://files.pythonhosted.org/packages/de/54/db126aad3874601048c2c20ae3d8a433dbfd7ba8381551e6f62606d9bd8e/scipy-1.15.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:62ca1ff3eb513e09ed17a5736929429189adf16d2d740f44e53270cc800ecff1", size = 30102038 }, { url = "https://files.pythonhosted.org/packages/61/d8/84da3fffefb6c7d5a16968fe5b9f24c98606b165bb801bb0b8bc3985200f/scipy-1.15.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4c6676490ad76d1c2894d77f976144b41bd1a4052107902238047fb6a473e971", size = 22375518 }, @@ -1492,63 +1278,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/35/32/c2e9dbce03a3c64c9cf8b5c667368e9b6d9f236a43659cf112b6a86446e1/sourcery-1.35.0-py2.py3-none-win_amd64.whl", hash = "sha256:61db72f1183abd231bc448a4d03a74738f3b1013ad4396dc2935c080c3863c36", size = 78356050 }, ] -[[package]] -name = "sphinx" -version = "8.1.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "alabaster", marker = "python_full_version < '3.11'" }, - { name = "babel", marker = "python_full_version < '3.11'" }, - { name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, - { name = "docutils", marker = "python_full_version < '3.11'" }, - { name = "imagesize", marker = "python_full_version < '3.11'" }, - { name = "jinja2", marker = "python_full_version < '3.11'" }, - { name = "packaging", marker = "python_full_version < '3.11'" }, - { name = "pygments", marker = "python_full_version < '3.11'" }, - { name = "requests", marker = "python_full_version < '3.11'" }, - { name = "snowballstemmer", marker = "python_full_version < '3.11'" }, - { name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.11'" }, - { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.11'" }, - { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.11'" }, - { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.11'" }, - { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.11'" }, - { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.11'" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125 }, -] - [[package]] name = "sphinx" version = "8.2.3" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.12'", - "python_full_version == '3.11.*'", -] dependencies = [ - { name = "alabaster", marker = "python_full_version >= '3.11'" }, - { name = "babel", marker = "python_full_version >= '3.11'" }, - { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, - { name = "docutils", marker = "python_full_version >= '3.11'" }, - { name = "imagesize", marker = "python_full_version >= '3.11'" }, - { name = "jinja2", marker = "python_full_version >= '3.11'" }, - { name = "packaging", marker = "python_full_version >= '3.11'" }, - { name = "pygments", marker = "python_full_version >= '3.11'" }, - { name = "requests", marker = "python_full_version >= '3.11'" }, - { name = "roman-numerals-py", marker = "python_full_version >= '3.11'" }, - { name = "snowballstemmer", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.11'" }, + { name = "alabaster" }, + { name = "babel" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "docutils" }, + { name = "imagesize" }, + { name = "jinja2" }, + { name = "packaging" }, + { name = "pygments" }, + { name = "requests" }, + { name = "roman-numerals-py" }, + { name = "snowballstemmer" }, + { name = "sphinxcontrib-applehelp" }, + { name = "sphinxcontrib-devhelp" }, + { name = "sphinxcontrib-htmlhelp" }, + { name = "sphinxcontrib-jsmath" }, + { name = "sphinxcontrib-qthelp" }, + { name = "sphinxcontrib-serializinghtml" }, ] sdist = { url = "https://files.pythonhosted.org/packages/38/ad/4360e50ed56cb483667b8e6dadf2d3fda62359593faabbe749a27c4eaca6/sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348", size = 8321876 } wheels = [ @@ -1560,39 +1311,19 @@ name = "sphinx-argparse-cli" version = "1.19.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx" }, ] sdist = { url = "https://files.pythonhosted.org/packages/32/5f/ae6043738a48408ce1371303958fa02943d29619dd5d202e147b1dc8552d/sphinx_argparse_cli-1.19.0.tar.gz", hash = "sha256:0374b23560fd4246234c0ef2d8bb97703088c8ae721bf63a84b9fd0d32f87a78", size = 12667 } wheels = [ { url = "https://files.pythonhosted.org/packages/00/1f/7d40169591a70fb1a8ba57345e22c3f6dca5b06ee6efddafe5ccee91336c/sphinx_argparse_cli-1.19.0-py3-none-any.whl", hash = "sha256:c0e069deed1db44d289f90469c04320f1c2249b3dcbd3c3e093bf9a66e4bd8e4", size = 9941 }, ] -[[package]] -name = "sphinx-autodoc-typehints" -version = "3.0.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/26/f0/43c6a5ff3e7b08a8c3b32f81b859f1b518ccc31e45f22e2b41ced38be7b9/sphinx_autodoc_typehints-3.0.1.tar.gz", hash = "sha256:b9b40dd15dee54f6f810c924f863f9cf1c54f9f3265c495140ea01be7f44fa55", size = 36282 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/dc/dc46c5c7c566b7ec5e8f860f9c89533bf03c0e6aadc96fb9b337867e4460/sphinx_autodoc_typehints-3.0.1-py3-none-any.whl", hash = "sha256:4b64b676a14b5b79cefb6628a6dc8070e320d4963e8ff640a2f3e9390ae9045a", size = 20245 }, -] - [[package]] name = "sphinx-autodoc-typehints" version = "3.1.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.12'", - "python_full_version == '3.11.*'", -] dependencies = [ - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cb/cc/d38e7260b1bd3af0c84ad8285dfd78236584b74544510584e07963e000ec/sphinx_autodoc_typehints-3.1.0.tar.gz", hash = "sha256:a6b7b0b6df0a380783ce5b29150c2d30352746f027a3e294d37183995d3f23ed", size = 36528 } wheels = [ @@ -1604,8 +1335,7 @@ name = "sphinx-basic-ng" version = "1.0.0b2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx" }, ] sdist = { url = "https://files.pythonhosted.org/packages/98/0b/a866924ded68efec7a1759587a4e478aec7559d8165fac8b2ad1c0e774d6/sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9", size = 20736 } wheels = [ @@ -1654,8 +1384,7 @@ version = "1.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyyaml" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx" }, ] sdist = { url = "https://files.pythonhosted.org/packages/97/69/bf039237ad260073e8c02f820b3e00dc34f3a2de20aff7861e6b19d2f8c5/sphinxcontrib_mermaid-1.0.0.tar.gz", hash = "sha256:2e8ab67d3e1e2816663f9347d026a8dee4a858acdd4ad32dd1c808893db88146", size = 15153 } wheels = [ @@ -1680,18 +1409,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072 }, ] -[[package]] -name = "sympy" -version = "1.13.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mpmath" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/11/8a/5a7fd6284fa8caac23a26c9ddf9c30485a48169344b4bd3b0f02fef1890f/sympy-1.13.3.tar.gz", hash = "sha256:b27fd2c6530e0ab39e275fc9b683895367e51d5da91baa8d3d64db2565fec4d9", size = 7533196 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/ff/c87e0622b1dadea79d2fb0b25ade9ed98954c9033722eb707053d310d4f3/sympy-1.13.3-py3-none-any.whl", hash = "sha256:54612cf55a62755ee71824ce692986f23c88ffa77207b30c1368eda4a7060f73", size = 6189483 }, -] - [[package]] name = "tomli" version = "2.2.1" From c519591c500654886bbfb476052ed7b6eb2b8c0f Mon Sep 17 00:00:00 2001 From: Mendez Date: Fri, 12 Dec 2025 13:07:20 +0100 Subject: [PATCH 22/29] Fix last test --- tests/test_variable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_variable.py b/tests/test_variable.py index b3c2cb6..d8a8b03 100644 --- a/tests/test_variable.py +++ b/tests/test_variable.py @@ -753,7 +753,7 @@ def test_extremum(): # for i in range(100): # print(i, t[i], x[i]) e, p = extremum(t[17:20], x[17:20]) - assert e == -1 and abs(p[0] - np.pi) < 1e-10 and p[1] == np.isclose(p[1], -1) + assert e == -1 and abs(p[0] - np.pi) < 1e-10 and np.isclose(p[1], -1) ex = extremum_series(t, x, "all") assert len(ex) == 2 assert np.allclose(ex[0], (12.566370614359142, 1.0)) From a2960dd9d42daa71370d2b6e09cae4f9a3d760a4 Mon Sep 17 00:00:00 2001 From: Mendez Date: Fri, 12 Dec 2025 13:35:59 +0100 Subject: [PATCH 23/29] Fix Pint version to support python 3.13 --- pyproject.toml | 4 ++-- uv.lock | 27 +++++++++------------------ 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3d5a9d6..2a44c65 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,10 +58,10 @@ classifiers = [ ] dependencies = [ "numpy>=1.26,<2.0", - "pint>=0.24", + "pint>=0.24.4", "jsonpath-ng>=1.7.0", "pythonfmu==0.6.9", - "flexparser<0.4", + "flexparser>=0.4", ] [project.optional-dependencies] diff --git a/uv.lock b/uv.lock index 925f478..9ad716d 100644 --- a/uv.lock +++ b/uv.lock @@ -10,15 +10,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929 }, ] -[[package]] -name = "appdirs" -version = "1.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/d8/05696357e0311f5b5c316d7b95f46c669dd9c15aaeecbb48c7d0aeb88c40/appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", size = 13470 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/00/2344469e2084fb287c2e0b57b72910309874c3245463acd6cf5e3db69324/appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128", size = 9566 }, -] - [[package]] name = "attrs" version = "25.3.0" @@ -168,12 +159,12 @@ dev = [ [package.metadata] requires-dist = [ { name = "docutils", marker = "extra == 'rest'", specifier = ">=0.21" }, - { name = "flexparser", specifier = "<0.4" }, + { name = "flexparser", specifier = ">=0.4" }, { name = "fmpy", marker = "extra == 'tests'", specifier = "==0.3.21" }, { name = "jsonpath-ng", specifier = ">=1.7.0" }, { name = "matplotlib", marker = "extra == 'tests'", specifier = ">=3.9.1" }, { name = "numpy", specifier = ">=1.26,<2.0" }, - { name = "pint", specifier = ">=0.24" }, + { name = "pint", specifier = ">=0.24.4" }, { name = "plotly", marker = "extra == 'tests'", specifier = ">=6.0.1" }, { name = "pythonfmu", specifier = "==0.6.9" }, { name = "scipy", marker = "extra == 'tests'", specifier = ">=1.15.1" }, @@ -352,14 +343,14 @@ wheels = [ [[package]] name = "flexparser" -version = "0.3.1" +version = "0.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dc/e4/a73612499d9c8c450c8f4878e8bb8b3b2dce4bf671b21dd8d5c6549525a7/flexparser-0.3.1.tar.gz", hash = "sha256:36f795d82e50f5c9ae2fde1c33f21f88922fdd67b7629550a3cc4d0b40a66856", size = 31422 } +sdist = { url = "https://files.pythonhosted.org/packages/82/99/b4de7e39e8eaf8207ba1a8fa2241dd98b2ba72ae6e16960d8351736d8702/flexparser-0.4.tar.gz", hash = "sha256:266d98905595be2ccc5da964fe0a2c3526fbbffdc45b65b3146d75db992ef6b2", size = 31799 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/28/5ce78a4838bb9da1bd9f64bc79ba12ddbfcb4824a11ef41da6f05d3240ef/flexparser-0.3.1-py3-none-any.whl", hash = "sha256:2e3e2936bec1f9277f777ef77297522087d96adb09624d4fe4240fd56885c013", size = 27289 }, + { url = "https://files.pythonhosted.org/packages/fe/5e/3be305568fe5f34448807976dc82fc151d76c3e0e03958f34770286278c1/flexparser-0.4-py3-none-any.whl", hash = "sha256:3738b456192dcb3e15620f324c447721023c0293f6af9955b481e91d00179846", size = 27625 }, ] [[package]] @@ -945,17 +936,17 @@ wheels = [ [[package]] name = "pint" -version = "0.24.3" +version = "0.25.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "appdirs" }, { name = "flexcache" }, { name = "flexparser" }, + { name = "platformdirs" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/53/7d/30178ff193a076e35521592260915f74049bfa77dccb43ac8aa5abe1414b/pint-0.24.3.tar.gz", hash = "sha256:d54771093e8b94c4e0a35ac638c2444ddf3ef685652bab7675ffecfa0c5c5cdf", size = 341664 } +sdist = { url = "https://files.pythonhosted.org/packages/5f/74/bc3f671997158aef171194c3c4041e549946f4784b8690baa0626a0a164b/pint-0.25.2.tar.gz", hash = "sha256:85a45d1da8fe9c9f7477fed8aef59ad2b939af3d6611507e1a9cbdacdcd3450a", size = 254467 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/2b/abe15c62ef1aece41d0799f31ba97d298aad9c76bc31dd655c387c29f17a/Pint-0.24.3-py3-none-any.whl", hash = "sha256:d98667e46fd03a1b94694fbfa104ec30858684d8ab26952e2a348b48059089bb", size = 301758 }, + { url = "https://files.pythonhosted.org/packages/ab/88/550d41e81e6d43335603a960cd9c75c1d88f9cf01bc9d4ee8e86290aba7d/pint-0.25.2-py3-none-any.whl", hash = "sha256:ca35ab1d8eeeb6f7d9942b3cb5f34ca42b61cdd5fb3eae79531553dcca04dda7", size = 306762 }, ] [[package]] From b11e143e22f10cea00b0461924906c334d352032 Mon Sep 17 00:00:00 2001 From: Mendez Date: Fri, 12 Dec 2025 13:46:47 +0100 Subject: [PATCH 24/29] Fix tests and mypy errors --- src/component_model/model.py | 13 ++++++++++--- tests/test_pint.py | 4 ++-- tests/test_utils.py | 20 ++++++++++++++++---- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/component_model/model.py b/src/component_model/model.py index fd171c3..d3c98f7 100644 --- a/src/component_model/model.py +++ b/src/component_model/model.py @@ -7,6 +7,7 @@ from abc import abstractmethod from enum import Enum from math import log +from numbers import Real from pathlib import Path from typing import Generator, Sequence, TypeAlias @@ -139,7 +140,7 @@ def __init__( if guid is not None: self.guid = guid # use a common UnitRegistry for all variables: - self.ureg = UnitRegistry(system=unit_system) + self.ureg: UnitRegistry = UnitRegistry(system=unit_system) self.copyright, self.license = self.make_copyright_license(copyright, license) self.guid = guid if guid is not None else uuid.uuid4().hex # print("FLAGS", flags) @@ -538,8 +539,14 @@ def xml_unit_definitions(self): "substance": "mol", "luminosity": "cd", }.items(): - if "[" + key + "]" in dim: - exponents.update({value: str(int(dim["[" + key + "]"]))}) + dim_key = f"[{key}]" + if dim_key not in dim: + continue + dim_value = dim[dim_key] + if not isinstance(dim_value, Real): + logger.debug("Skipping non-real dimensionality entry for %s", dim_key) + continue + exponents.update({value: str(int(float(dim_value)))}) if ( "radian" in str(ubase.units) ): # radians are formally a dimensionless quantity. To include 'rad' as specified in FMI standard this dirty trick is used diff --git a/tests/test_pint.py b/tests/test_pint.py index 40d2d86..8228ef2 100644 --- a/tests/test_pint.py +++ b/tests/test_pint.py @@ -7,11 +7,11 @@ logger = logging.getLogger(__name__) -_reg = UnitRegistry(system="SI", autoconvert_offset_to_baseunit=True) # , auto_reduce_dimensions=True) +_reg: UnitRegistry = UnitRegistry(system="SI", autoconvert_offset_to_baseunit=True) # , auto_reduce_dimensions=True) def test_needed_functions(): - _reg = UnitRegistry(system="SI", autoconvert_offset_to_baseunit=True) # , auto_reduce_dimensions=True) + _reg: UnitRegistry = UnitRegistry(system="SI", autoconvert_offset_to_baseunit=True) # , auto_reduce_dimensions=True) print("AVAILABLE UNITS", dir(_reg.sys.SI)) print( "degrees_Celsius defined?", diff --git a/tests/test_utils.py b/tests/test_utils.py index 68e104e..15688d4 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -28,12 +28,24 @@ def dicts_equal(d1: dict, d2: dict): assert isinstance(d2, dict), f"Dict expected. Found {d2}" for key in d1: assert key in d2, f"Key {key} not found in {d2}" - if key != "copyright": # copyright changes with the year! - assert d1[key] == d2[key], f"Value of key {key} {d1[key]} != {d2[key]}" + if key == "copyright": + continue # copyright changes with the year! + if key == "license": + assert " ".join(str(d1[key]).split()) == " ".join(str(d2[key]).split()), ( + f"Value of key {key} differs after whitespace normalization\n{d1[key]!r}\n!=\n{d2[key]!r}" + ) + continue + assert d1[key] == d2[key], f"Value of key {key} {d1[key]} != {d2[key]}" for key in d2: assert key in d1, f"Key {key} not found in {d1}" - if key != "copyright": # copyright changes with the year! - assert d1[key] == d2[key], f"Value of key {key} {d1[key]} != {d2[key]}" + if key == "copyright": + continue # copyright changes with the year! + if key == "license": + assert " ".join(str(d1[key]).split()) == " ".join(str(d2[key]).split()), ( + f"Value of key {key} differs after whitespace normalization\n{d1[key]!r}\n!=\n{d2[key]!r}" + ) + continue + assert d1[key] == d2[key], f"Value of key {key} {d1[key]} != {d2[key]}" def test_xml_to_python_val(): From 64b79b002e4d392daf72b338e27c4b4a742a198f Mon Sep 17 00:00:00 2001 From: Mendez Date: Fri, 12 Dec 2025 13:47:59 +0100 Subject: [PATCH 25/29] Fix ruff format issue --- tests/test_pint.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_pint.py b/tests/test_pint.py index 8228ef2..4fd9a90 100644 --- a/tests/test_pint.py +++ b/tests/test_pint.py @@ -11,7 +11,9 @@ def test_needed_functions(): - _reg: UnitRegistry = UnitRegistry(system="SI", autoconvert_offset_to_baseunit=True) # , auto_reduce_dimensions=True) + _reg: UnitRegistry = UnitRegistry( + system="SI", autoconvert_offset_to_baseunit=True + ) # , auto_reduce_dimensions=True) print("AVAILABLE UNITS", dir(_reg.sys.SI)) print( "degrees_Celsius defined?", From 4613f0ff265108158acd22b7558c53be8c9bf428 Mon Sep 17 00:00:00 2001 From: Mendez Date: Fri, 12 Dec 2025 15:49:46 +0100 Subject: [PATCH 26/29] Upgrade numpy --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2a44c65..2657b35 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,7 +57,7 @@ classifiers = [ "Topic :: Software Development :: Libraries :: Python Modules", ] dependencies = [ - "numpy>=1.26,<2.0", + "numpy>=2.0", "pint>=0.24.4", "jsonpath-ng>=1.7.0", "pythonfmu==0.6.9", From 3bc75c9e218c77c2a02ab939f400f5693d86a52f Mon Sep 17 00:00:00 2001 From: Mendez Date: Fri, 12 Dec 2025 16:02:16 +0100 Subject: [PATCH 27/29] Remove unnecessary numpy from table FMU --- examples/time_table.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/time_table.py b/examples/time_table.py index cc59a28..0840a2f 100644 --- a/examples/time_table.py +++ b/examples/time_table.py @@ -1,6 +1,5 @@ from typing import Sequence -import numpy as np from scipy.interpolate import make_interp_spline @@ -35,11 +34,11 @@ def __init__( assert self._rows > 0, "Empty lookup table detected, which does not make sense" self._cols = len(data[0]) - 1 assert self._cols > 0, "No data column found in lookup table" - self.times = np.array(list(row[0] for row in data), float) # column 0 as times + self.times = tuple(float(row[0]) for row in data) # column 0 as times assert all(self.times[i - 1] < self.times[i] for i in range(1, len(self.times))), ( "The times in the input data are not properly sorted in ascending order" ) - self.data = np.array(list(row[1:] for row in data), float) + self.data = tuple(float(row[1:]) for row in data) if header is None: self.header = tuple([f"out.{i}" for i in range(self._cols)]) else: From cd2d78838bb1ec0693a7b53588bc6214758d975f Mon Sep 17 00:00:00 2001 From: Mendez Date: Fri, 12 Dec 2025 16:13:57 +0100 Subject: [PATCH 28/29] Rollback changes and try to fix tests --- examples/time_table.py | 5 +++-- examples/time_table_fmu.py | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/time_table.py b/examples/time_table.py index 0840a2f..cc59a28 100644 --- a/examples/time_table.py +++ b/examples/time_table.py @@ -1,5 +1,6 @@ from typing import Sequence +import numpy as np from scipy.interpolate import make_interp_spline @@ -34,11 +35,11 @@ def __init__( assert self._rows > 0, "Empty lookup table detected, which does not make sense" self._cols = len(data[0]) - 1 assert self._cols > 0, "No data column found in lookup table" - self.times = tuple(float(row[0]) for row in data) # column 0 as times + self.times = np.array(list(row[0] for row in data), float) # column 0 as times assert all(self.times[i - 1] < self.times[i] for i in range(1, len(self.times))), ( "The times in the input data are not properly sorted in ascending order" ) - self.data = tuple(float(row[1:]) for row in data) + self.data = np.array(list(row[1:] for row in data), float) if header is None: self.header = tuple([f"out.{i}" for i in range(self._cols)]) else: diff --git a/examples/time_table_fmu.py b/examples/time_table_fmu.py index 23737a2..d47991f 100644 --- a/examples/time_table_fmu.py +++ b/examples/time_table_fmu.py @@ -1,5 +1,6 @@ import logging from typing import Sequence +import numpy as np from component_model.model import Model from component_model.variable import Variable From 6c0302bbd5778f49dd4268cae0280e2ee30514c7 Mon Sep 17 00:00:00 2001 From: Mendez Date: Fri, 12 Dec 2025 16:22:28 +0100 Subject: [PATCH 29/29] Fix mypy issue --- examples/time_table_fmu.py | 3 +- src/component_model/variable.py | 4 +- uv.lock | 99 ++++++++++++++++++++++++++------- 3 files changed, 82 insertions(+), 24 deletions(-) diff --git a/examples/time_table_fmu.py b/examples/time_table_fmu.py index d47991f..cdae87f 100644 --- a/examples/time_table_fmu.py +++ b/examples/time_table_fmu.py @@ -1,6 +1,7 @@ import logging from typing import Sequence -import numpy as np + +import numpy as np # noqa from component_model.model import Model from component_model.variable import Variable diff --git a/src/component_model/variable.py b/src/component_model/variable.py index ef32cf1..f90fe43 100644 --- a/src/component_model/variable.py +++ b/src/component_model/variable.py @@ -435,8 +435,8 @@ def der1(self, current_time: float, step_size: float): newval = val + step_size * np.array(der, float) basevar.setter_internal(newval, -1, True) else: - newval = [val[i] + step_size * der[i] for i in range(len(der))] - basevar.setter_internal(newval, -1, False) + newval_list = [val[i] + step_size * der[i] for i in range(len(der))] + basevar.setter_internal(newval_list, -1, False) # disable super() functions and properties which are not in use here def to_xml(self) -> ET.Element: diff --git a/uv.lock b/uv.lock index 9ad716d..789044d 100644 --- a/uv.lock +++ b/uv.lock @@ -163,7 +163,7 @@ requires-dist = [ { name = "fmpy", marker = "extra == 'tests'", specifier = "==0.3.21" }, { name = "jsonpath-ng", specifier = ">=1.7.0" }, { name = "matplotlib", marker = "extra == 'tests'", specifier = ">=3.9.1" }, - { name = "numpy", specifier = ">=1.26,<2.0" }, + { name = "numpy", specifier = ">=2.0" }, { name = "pint", specifier = ">=0.24.4" }, { name = "plotly", marker = "extra == 'tests'", specifier = ">=6.0.1" }, { name = "pythonfmu", specifier = "==0.6.9" }, @@ -854,26 +854,83 @@ wheels = [ [[package]] name = "numpy" -version = "1.26.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/11/57/baae43d14fe163fa0e4c47f307b6b2511ab8d7d30177c491960504252053/numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71", size = 20630554 }, - { url = "https://files.pythonhosted.org/packages/1a/2e/151484f49fd03944c4a3ad9c418ed193cfd02724e138ac8a9505d056c582/numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef", size = 13997127 }, - { url = "https://files.pythonhosted.org/packages/79/ae/7e5b85136806f9dadf4878bf73cf223fe5c2636818ba3ab1c585d0403164/numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e", size = 14222994 }, - { url = "https://files.pythonhosted.org/packages/3a/d0/edc009c27b406c4f9cbc79274d6e46d634d139075492ad055e3d68445925/numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5", size = 18252005 }, - { url = "https://files.pythonhosted.org/packages/09/bf/2b1aaf8f525f2923ff6cfcf134ae5e750e279ac65ebf386c75a0cf6da06a/numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a", size = 13885297 }, - { url = "https://files.pythonhosted.org/packages/df/a0/4e0f14d847cfc2a633a1c8621d00724f3206cfeddeb66d35698c4e2cf3d2/numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a", size = 18093567 }, - { url = "https://files.pythonhosted.org/packages/d2/b7/a734c733286e10a7f1a8ad1ae8c90f2d33bf604a96548e0a4a3a6739b468/numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20", size = 5968812 }, - { url = "https://files.pythonhosted.org/packages/3f/6b/5610004206cf7f8e7ad91c5a85a8c71b2f2f8051a0c0c4d5916b76d6cbb2/numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2", size = 15811913 }, - { url = "https://files.pythonhosted.org/packages/95/12/8f2020a8e8b8383ac0177dc9570aad031a3beb12e38847f7129bacd96228/numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218", size = 20335901 }, - { url = "https://files.pythonhosted.org/packages/75/5b/ca6c8bd14007e5ca171c7c03102d17b4f4e0ceb53957e8c44343a9546dcc/numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b", size = 13685868 }, - { url = "https://files.pythonhosted.org/packages/79/f8/97f10e6755e2a7d027ca783f63044d5b1bc1ae7acb12afe6a9b4286eac17/numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b", size = 13925109 }, - { url = "https://files.pythonhosted.org/packages/0f/50/de23fde84e45f5c4fda2488c759b69990fd4512387a8632860f3ac9cd225/numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed", size = 17950613 }, - { url = "https://files.pythonhosted.org/packages/4c/0c/9c603826b6465e82591e05ca230dfc13376da512b25ccd0894709b054ed0/numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a", size = 13572172 }, - { url = "https://files.pythonhosted.org/packages/76/8c/2ba3902e1a0fc1c74962ea9bb33a534bb05984ad7ff9515bf8d07527cadd/numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0", size = 17786643 }, - { url = "https://files.pythonhosted.org/packages/28/4a/46d9e65106879492374999e76eb85f87b15328e06bd1550668f79f7b18c6/numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110", size = 5677803 }, - { url = "https://files.pythonhosted.org/packages/16/2e/86f24451c2d530c88daf997cb8d6ac622c1d40d19f5a031ed68a4b73a374/numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818", size = 15517754 }, +version = "2.3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/65/21b3bc86aac7b8f2862db1e808f1ea22b028e30a225a34a5ede9bf8678f2/numpy-2.3.5.tar.gz", hash = "sha256:784db1dcdab56bf0517743e746dfb0f885fc68d948aba86eeec2cba234bdf1c0", size = 20584950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/77/84dd1d2e34d7e2792a236ba180b5e8fcc1e3e414e761ce0253f63d7f572e/numpy-2.3.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:de5672f4a7b200c15a4127042170a694d4df43c992948f5e1af57f0174beed10", size = 17034641 }, + { url = "https://files.pythonhosted.org/packages/2a/ea/25e26fa5837106cde46ae7d0b667e20f69cbbc0efd64cba8221411ab26ae/numpy-2.3.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:acfd89508504a19ed06ef963ad544ec6664518c863436306153e13e94605c218", size = 12528324 }, + { url = "https://files.pythonhosted.org/packages/4d/1a/e85f0eea4cf03d6a0228f5c0256b53f2df4bc794706e7df019fc622e47f1/numpy-2.3.5-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:ffe22d2b05504f786c867c8395de703937f934272eb67586817b46188b4ded6d", size = 5356872 }, + { url = "https://files.pythonhosted.org/packages/5c/bb/35ef04afd567f4c989c2060cde39211e4ac5357155c1833bcd1166055c61/numpy-2.3.5-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:872a5cf366aec6bb1147336480fef14c9164b154aeb6542327de4970282cd2f5", size = 6893148 }, + { url = "https://files.pythonhosted.org/packages/f2/2b/05bbeb06e2dff5eab512dfc678b1cc5ee94d8ac5956a0885c64b6b26252b/numpy-2.3.5-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3095bdb8dd297e5920b010e96134ed91d852d81d490e787beca7e35ae1d89cf7", size = 14557282 }, + { url = "https://files.pythonhosted.org/packages/65/fb/2b23769462b34398d9326081fad5655198fcf18966fcb1f1e49db44fbf31/numpy-2.3.5-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8cba086a43d54ca804ce711b2a940b16e452807acebe7852ff327f1ecd49b0d4", size = 16897903 }, + { url = "https://files.pythonhosted.org/packages/ac/14/085f4cf05fc3f1e8aa95e85404e984ffca9b2275a5dc2b1aae18a67538b8/numpy-2.3.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6cf9b429b21df6b99f4dee7a1218b8b7ffbbe7df8764dc0bd60ce8a0708fed1e", size = 16341672 }, + { url = "https://files.pythonhosted.org/packages/6f/3b/1f73994904142b2aa290449b3bb99772477b5fd94d787093e4f24f5af763/numpy-2.3.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:396084a36abdb603546b119d96528c2f6263921c50df3c8fd7cb28873a237748", size = 18838896 }, + { url = "https://files.pythonhosted.org/packages/cd/b9/cf6649b2124f288309ffc353070792caf42ad69047dcc60da85ee85fea58/numpy-2.3.5-cp311-cp311-win32.whl", hash = "sha256:b0c7088a73aef3d687c4deef8452a3ac7c1be4e29ed8bf3b366c8111128ac60c", size = 6563608 }, + { url = "https://files.pythonhosted.org/packages/aa/44/9fe81ae1dcc29c531843852e2874080dc441338574ccc4306b39e2ff6e59/numpy-2.3.5-cp311-cp311-win_amd64.whl", hash = "sha256:a414504bef8945eae5f2d7cb7be2d4af77c5d1cb5e20b296c2c25b61dff2900c", size = 13078442 }, + { url = "https://files.pythonhosted.org/packages/6d/a7/f99a41553d2da82a20a2f22e93c94f928e4490bb447c9ff3c4ff230581d3/numpy-2.3.5-cp311-cp311-win_arm64.whl", hash = "sha256:0cd00b7b36e35398fa2d16af7b907b65304ef8bb4817a550e06e5012929830fa", size = 10458555 }, + { url = "https://files.pythonhosted.org/packages/44/37/e669fe6cbb2b96c62f6bbedc6a81c0f3b7362f6a59230b23caa673a85721/numpy-2.3.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:74ae7b798248fe62021dbf3c914245ad45d1a6b0cb4a29ecb4b31d0bfbc4cc3e", size = 16733873 }, + { url = "https://files.pythonhosted.org/packages/c5/65/df0db6c097892c9380851ab9e44b52d4f7ba576b833996e0080181c0c439/numpy-2.3.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee3888d9ff7c14604052b2ca5535a30216aa0a58e948cdd3eeb8d3415f638769", size = 12259838 }, + { url = "https://files.pythonhosted.org/packages/5b/e1/1ee06e70eb2136797abe847d386e7c0e830b67ad1d43f364dd04fa50d338/numpy-2.3.5-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:612a95a17655e213502f60cfb9bf9408efdc9eb1d5f50535cc6eb365d11b42b5", size = 5088378 }, + { url = "https://files.pythonhosted.org/packages/6d/9c/1ca85fb86708724275103b81ec4cf1ac1d08f465368acfc8da7ab545bdae/numpy-2.3.5-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3101e5177d114a593d79dd79658650fe28b5a0d8abeb8ce6f437c0e6df5be1a4", size = 6628559 }, + { url = "https://files.pythonhosted.org/packages/74/78/fcd41e5a0ce4f3f7b003da85825acddae6d7ecb60cf25194741b036ca7d6/numpy-2.3.5-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b973c57ff8e184109db042c842423ff4f60446239bd585a5131cc47f06f789d", size = 14250702 }, + { url = "https://files.pythonhosted.org/packages/b6/23/2a1b231b8ff672b4c450dac27164a8b2ca7d9b7144f9c02d2396518352eb/numpy-2.3.5-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d8163f43acde9a73c2a33605353a4f1bc4798745a8b1d73183b28e5b435ae28", size = 16606086 }, + { url = "https://files.pythonhosted.org/packages/a0/c5/5ad26fbfbe2012e190cc7d5003e4d874b88bb18861d0829edc140a713021/numpy-2.3.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:51c1e14eb1e154ebd80e860722f9e6ed6ec89714ad2db2d3aa33c31d7c12179b", size = 16025985 }, + { url = "https://files.pythonhosted.org/packages/d2/fa/dd48e225c46c819288148d9d060b047fd2a6fb1eb37eae25112ee4cb4453/numpy-2.3.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b46b4ec24f7293f23adcd2d146960559aaf8020213de8ad1909dba6c013bf89c", size = 18542976 }, + { url = "https://files.pythonhosted.org/packages/05/79/ccbd23a75862d95af03d28b5c6901a1b7da4803181513d52f3b86ed9446e/numpy-2.3.5-cp312-cp312-win32.whl", hash = "sha256:3997b5b3c9a771e157f9aae01dd579ee35ad7109be18db0e85dbdbe1de06e952", size = 6285274 }, + { url = "https://files.pythonhosted.org/packages/2d/57/8aeaf160312f7f489dea47ab61e430b5cb051f59a98ae68b7133ce8fa06a/numpy-2.3.5-cp312-cp312-win_amd64.whl", hash = "sha256:86945f2ee6d10cdfd67bcb4069c1662dd711f7e2a4343db5cecec06b87cf31aa", size = 12782922 }, + { url = "https://files.pythonhosted.org/packages/78/a6/aae5cc2ca78c45e64b9ef22f089141d661516856cf7c8a54ba434576900d/numpy-2.3.5-cp312-cp312-win_arm64.whl", hash = "sha256:f28620fe26bee16243be2b7b874da327312240a7cdc38b769a697578d2100013", size = 10194667 }, + { url = "https://files.pythonhosted.org/packages/db/69/9cde09f36da4b5a505341180a3f2e6fadc352fd4d2b7096ce9778db83f1a/numpy-2.3.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d0f23b44f57077c1ede8c5f26b30f706498b4862d3ff0a7298b8411dd2f043ff", size = 16728251 }, + { url = "https://files.pythonhosted.org/packages/79/fb/f505c95ceddd7027347b067689db71ca80bd5ecc926f913f1a23e65cf09b/numpy-2.3.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa5bc7c5d59d831d9773d1170acac7893ce3a5e130540605770ade83280e7188", size = 12254652 }, + { url = "https://files.pythonhosted.org/packages/78/da/8c7738060ca9c31b30e9301ee0cf6c5ffdbf889d9593285a1cead337f9a5/numpy-2.3.5-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:ccc933afd4d20aad3c00bcef049cb40049f7f196e0397f1109dba6fed63267b0", size = 5083172 }, + { url = "https://files.pythonhosted.org/packages/a4/b4/ee5bb2537fb9430fd2ef30a616c3672b991a4129bb1c7dcc42aa0abbe5d7/numpy-2.3.5-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:afaffc4393205524af9dfa400fa250143a6c3bc646c08c9f5e25a9f4b4d6a903", size = 6622990 }, + { url = "https://files.pythonhosted.org/packages/95/03/dc0723a013c7d7c19de5ef29e932c3081df1c14ba582b8b86b5de9db7f0f/numpy-2.3.5-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c75442b2209b8470d6d5d8b1c25714270686f14c749028d2199c54e29f20b4d", size = 14248902 }, + { url = "https://files.pythonhosted.org/packages/f5/10/ca162f45a102738958dcec8023062dad0cbc17d1ab99d68c4e4a6c45fb2b/numpy-2.3.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11e06aa0af8c0f05104d56450d6093ee639e15f24ecf62d417329d06e522e017", size = 16597430 }, + { url = "https://files.pythonhosted.org/packages/2a/51/c1e29be863588db58175175f057286900b4b3327a1351e706d5e0f8dd679/numpy-2.3.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ed89927b86296067b4f81f108a2271d8926467a8868e554eaf370fc27fa3ccaf", size = 16024551 }, + { url = "https://files.pythonhosted.org/packages/83/68/8236589d4dbb87253d28259d04d9b814ec0ecce7cb1c7fed29729f4c3a78/numpy-2.3.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51c55fe3451421f3a6ef9a9c1439e82101c57a2c9eab9feb196a62b1a10b58ce", size = 18533275 }, + { url = "https://files.pythonhosted.org/packages/40/56/2932d75b6f13465239e3b7b7e511be27f1b8161ca2510854f0b6e521c395/numpy-2.3.5-cp313-cp313-win32.whl", hash = "sha256:1978155dd49972084bd6ef388d66ab70f0c323ddee6f693d539376498720fb7e", size = 6277637 }, + { url = "https://files.pythonhosted.org/packages/0c/88/e2eaa6cffb115b85ed7c7c87775cb8bcf0816816bc98ca8dbfa2ee33fe6e/numpy-2.3.5-cp313-cp313-win_amd64.whl", hash = "sha256:00dc4e846108a382c5869e77c6ed514394bdeb3403461d25a829711041217d5b", size = 12779090 }, + { url = "https://files.pythonhosted.org/packages/8f/88/3f41e13a44ebd4034ee17baa384acac29ba6a4fcc2aca95f6f08ca0447d1/numpy-2.3.5-cp313-cp313-win_arm64.whl", hash = "sha256:0472f11f6ec23a74a906a00b48a4dcf3849209696dff7c189714511268d103ae", size = 10194710 }, + { url = "https://files.pythonhosted.org/packages/13/cb/71744144e13389d577f867f745b7df2d8489463654a918eea2eeb166dfc9/numpy-2.3.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:414802f3b97f3c1eef41e530aaba3b3c1620649871d8cb38c6eaff034c2e16bd", size = 16827292 }, + { url = "https://files.pythonhosted.org/packages/71/80/ba9dc6f2a4398e7f42b708a7fdc841bb638d353be255655498edbf9a15a8/numpy-2.3.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5ee6609ac3604fa7780e30a03e5e241a7956f8e2fcfe547d51e3afa5247ac47f", size = 12378897 }, + { url = "https://files.pythonhosted.org/packages/2e/6d/db2151b9f64264bcceccd51741aa39b50150de9b602d98ecfe7e0c4bff39/numpy-2.3.5-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:86d835afea1eaa143012a2d7a3f45a3adce2d7adc8b4961f0b362214d800846a", size = 5207391 }, + { url = "https://files.pythonhosted.org/packages/80/ae/429bacace5ccad48a14c4ae5332f6aa8ab9f69524193511d60ccdfdc65fa/numpy-2.3.5-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:30bc11310e8153ca664b14c5f1b73e94bd0503681fcf136a163de856f3a50139", size = 6721275 }, + { url = "https://files.pythonhosted.org/packages/74/5b/1919abf32d8722646a38cd527bc3771eb229a32724ee6ba340ead9b92249/numpy-2.3.5-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1062fde1dcf469571705945b0f221b73928f34a20c904ffb45db101907c3454e", size = 14306855 }, + { url = "https://files.pythonhosted.org/packages/a5/87/6831980559434973bebc30cd9c1f21e541a0f2b0c280d43d3afd909b66d0/numpy-2.3.5-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce581db493ea1a96c0556360ede6607496e8bf9b3a8efa66e06477267bc831e9", size = 16657359 }, + { url = "https://files.pythonhosted.org/packages/dd/91/c797f544491ee99fd00495f12ebb7802c440c1915811d72ac5b4479a3356/numpy-2.3.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:cc8920d2ec5fa99875b670bb86ddeb21e295cb07aa331810d9e486e0b969d946", size = 16093374 }, + { url = "https://files.pythonhosted.org/packages/74/a6/54da03253afcbe7a72785ec4da9c69fb7a17710141ff9ac5fcb2e32dbe64/numpy-2.3.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9ee2197ef8c4f0dfe405d835f3b6a14f5fee7782b5de51ba06fb65fc9b36e9f1", size = 18594587 }, + { url = "https://files.pythonhosted.org/packages/80/e9/aff53abbdd41b0ecca94285f325aff42357c6b5abc482a3fcb4994290b18/numpy-2.3.5-cp313-cp313t-win32.whl", hash = "sha256:70b37199913c1bd300ff6e2693316c6f869c7ee16378faf10e4f5e3275b299c3", size = 6405940 }, + { url = "https://files.pythonhosted.org/packages/d5/81/50613fec9d4de5480de18d4f8ef59ad7e344d497edbef3cfd80f24f98461/numpy-2.3.5-cp313-cp313t-win_amd64.whl", hash = "sha256:b501b5fa195cc9e24fe102f21ec0a44dffc231d2af79950b451e0d99cea02234", size = 12920341 }, + { url = "https://files.pythonhosted.org/packages/bb/ab/08fd63b9a74303947f34f0bd7c5903b9c5532c2d287bead5bdf4c556c486/numpy-2.3.5-cp313-cp313t-win_arm64.whl", hash = "sha256:a80afd79f45f3c4a7d341f13acbe058d1ca8ac017c165d3fa0d3de6bc1a079d7", size = 10262507 }, + { url = "https://files.pythonhosted.org/packages/ba/97/1a914559c19e32d6b2e233cf9a6a114e67c856d35b1d6babca571a3e880f/numpy-2.3.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:bf06bc2af43fa8d32d30fae16ad965663e966b1a3202ed407b84c989c3221e82", size = 16735706 }, + { url = "https://files.pythonhosted.org/packages/57/d4/51233b1c1b13ecd796311216ae417796b88b0616cfd8a33ae4536330748a/numpy-2.3.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:052e8c42e0c49d2575621c158934920524f6c5da05a1d3b9bab5d8e259e045f0", size = 12264507 }, + { url = "https://files.pythonhosted.org/packages/45/98/2fe46c5c2675b8306d0b4a3ec3494273e93e1226a490f766e84298576956/numpy-2.3.5-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:1ed1ec893cff7040a02c8aa1c8611b94d395590d553f6b53629a4461dc7f7b63", size = 5093049 }, + { url = "https://files.pythonhosted.org/packages/ce/0e/0698378989bb0ac5f1660c81c78ab1fe5476c1a521ca9ee9d0710ce54099/numpy-2.3.5-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:2dcd0808a421a482a080f89859a18beb0b3d1e905b81e617a188bd80422d62e9", size = 6626603 }, + { url = "https://files.pythonhosted.org/packages/5e/a6/9ca0eecc489640615642a6cbc0ca9e10df70df38c4d43f5a928ff18d8827/numpy-2.3.5-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:727fd05b57df37dc0bcf1a27767a3d9a78cbbc92822445f32cc3436ba797337b", size = 14262696 }, + { url = "https://files.pythonhosted.org/packages/c8/f6/07ec185b90ec9d7217a00eeeed7383b73d7e709dae2a9a021b051542a708/numpy-2.3.5-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fffe29a1ef00883599d1dc2c51aa2e5d80afe49523c261a74933df395c15c520", size = 16597350 }, + { url = "https://files.pythonhosted.org/packages/75/37/164071d1dde6a1a84c9b8e5b414fa127981bad47adf3a6b7e23917e52190/numpy-2.3.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8f7f0e05112916223d3f438f293abf0727e1181b5983f413dfa2fefc4098245c", size = 16040190 }, + { url = "https://files.pythonhosted.org/packages/08/3c/f18b82a406b04859eb026d204e4e1773eb41c5be58410f41ffa511d114ae/numpy-2.3.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2e2eb32ddb9ccb817d620ac1d8dae7c3f641c1e5f55f531a33e8ab97960a75b8", size = 18536749 }, + { url = "https://files.pythonhosted.org/packages/40/79/f82f572bf44cf0023a2fe8588768e23e1592585020d638999f15158609e1/numpy-2.3.5-cp314-cp314-win32.whl", hash = "sha256:66f85ce62c70b843bab1fb14a05d5737741e74e28c7b8b5a064de10142fad248", size = 6335432 }, + { url = "https://files.pythonhosted.org/packages/a3/2e/235b4d96619931192c91660805e5e49242389742a7a82c27665021db690c/numpy-2.3.5-cp314-cp314-win_amd64.whl", hash = "sha256:e6a0bc88393d65807d751a614207b7129a310ca4fe76a74e5c7da5fa5671417e", size = 12919388 }, + { url = "https://files.pythonhosted.org/packages/07/2b/29fd75ce45d22a39c61aad74f3d718e7ab67ccf839ca8b60866054eb15f8/numpy-2.3.5-cp314-cp314-win_arm64.whl", hash = "sha256:aeffcab3d4b43712bb7a60b65f6044d444e75e563ff6180af8f98dd4b905dfd2", size = 10476651 }, + { url = "https://files.pythonhosted.org/packages/17/e1/f6a721234ebd4d87084cfa68d081bcba2f5cfe1974f7de4e0e8b9b2a2ba1/numpy-2.3.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:17531366a2e3a9e30762c000f2c43a9aaa05728712e25c11ce1dbe700c53ad41", size = 16834503 }, + { url = "https://files.pythonhosted.org/packages/5c/1c/baf7ffdc3af9c356e1c135e57ab7cf8d247931b9554f55c467efe2c69eff/numpy-2.3.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d21644de1b609825ede2f48be98dfde4656aefc713654eeee280e37cadc4e0ad", size = 12381612 }, + { url = "https://files.pythonhosted.org/packages/74/91/f7f0295151407ddc9ba34e699013c32c3c91944f9b35fcf9281163dc1468/numpy-2.3.5-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:c804e3a5aba5460c73955c955bdbd5c08c354954e9270a2c1565f62e866bdc39", size = 5210042 }, + { url = "https://files.pythonhosted.org/packages/2e/3b/78aebf345104ec50dd50a4d06ddeb46a9ff5261c33bcc58b1c4f12f85ec2/numpy-2.3.5-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:cc0a57f895b96ec78969c34f682c602bf8da1a0270b09bc65673df2e7638ec20", size = 6724502 }, + { url = "https://files.pythonhosted.org/packages/02/c6/7c34b528740512e57ef1b7c8337ab0b4f0bddf34c723b8996c675bc2bc91/numpy-2.3.5-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:900218e456384ea676e24ea6a0417f030a3b07306d29d7ad843957b40a9d8d52", size = 14308962 }, + { url = "https://files.pythonhosted.org/packages/80/35/09d433c5262bc32d725bafc619e095b6a6651caf94027a03da624146f655/numpy-2.3.5-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:09a1bea522b25109bf8e6f3027bd810f7c1085c64a0c7ce050c1676ad0ba010b", size = 16655054 }, + { url = "https://files.pythonhosted.org/packages/7a/ab/6a7b259703c09a88804fa2430b43d6457b692378f6b74b356155283566ac/numpy-2.3.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04822c00b5fd0323c8166d66c701dc31b7fbd252c100acd708c48f763968d6a3", size = 16091613 }, + { url = "https://files.pythonhosted.org/packages/c2/88/330da2071e8771e60d1038166ff9d73f29da37b01ec3eb43cb1427464e10/numpy-2.3.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d6889ec4ec662a1a37eb4b4fb26b6100841804dac55bd9df579e326cdc146227", size = 18591147 }, + { url = "https://files.pythonhosted.org/packages/51/41/851c4b4082402d9ea860c3626db5d5df47164a712cb23b54be028b184c1c/numpy-2.3.5-cp314-cp314t-win32.whl", hash = "sha256:93eebbcf1aafdf7e2ddd44c2923e2672e1010bddc014138b229e49725b4d6be5", size = 6479806 }, + { url = "https://files.pythonhosted.org/packages/90/30/d48bde1dfd93332fa557cff1972fbc039e055a52021fbef4c2c4b1eefd17/numpy-2.3.5-cp314-cp314t-win_amd64.whl", hash = "sha256:c8a9958e88b65c3b27e22ca2a076311636850b612d6bbfb76e8d156aacde2aaf", size = 13105760 }, + { url = "https://files.pythonhosted.org/packages/2d/fd/4b5eb0b3e888d86aee4d198c23acec7d214baaf17ea93c1adec94c9518b9/numpy-2.3.5-cp314-cp314t-win_arm64.whl", hash = "sha256:6203fdf9f3dc5bdaed7319ad8698e685c7a3be10819f41d32a0723e611733b42", size = 10545459 }, + { url = "https://files.pythonhosted.org/packages/c6/65/f9dea8e109371ade9c782b4e4756a82edf9d3366bca495d84d79859a0b79/numpy-2.3.5-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f0963b55cdd70fad460fa4c1341f12f976bb26cb66021a5580329bd498988310", size = 16910689 }, + { url = "https://files.pythonhosted.org/packages/00/4f/edb00032a8fb92ec0a679d3830368355da91a69cab6f3e9c21b64d0bb986/numpy-2.3.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f4255143f5160d0de972d28c8f9665d882b5f61309d8362fdd3e103cf7bf010c", size = 12457053 }, + { url = "https://files.pythonhosted.org/packages/16/a4/e8a53b5abd500a63836a29ebe145fc1ab1f2eefe1cfe59276020373ae0aa/numpy-2.3.5-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:a4b9159734b326535f4dd01d947f919c6eefd2d9827466a696c44ced82dfbc18", size = 5285635 }, + { url = "https://files.pythonhosted.org/packages/a3/2f/37eeb9014d9c8b3e9c55bc599c68263ca44fdbc12a93e45a21d1d56df737/numpy-2.3.5-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:2feae0d2c91d46e59fcd62784a3a83b3fb677fead592ce51b5a6fbb4f95965ff", size = 6801770 }, + { url = "https://files.pythonhosted.org/packages/7d/e4/68d2f474df2cb671b2b6c2986a02e520671295647dad82484cde80ca427b/numpy-2.3.5-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffac52f28a7849ad7576293c0cb7b9f08304e8f7d738a8cb8a90ec4c55a998eb", size = 14391768 }, + { url = "https://files.pythonhosted.org/packages/b8/50/94ccd8a2b141cb50651fddd4f6a48874acb3c91c8f0842b08a6afc4b0b21/numpy-2.3.5-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63c0e9e7eea69588479ebf4a8a270d5ac22763cc5854e9a7eae952a3908103f7", size = 16729263 }, + { url = "https://files.pythonhosted.org/packages/2d/ee/346fa473e666fe14c52fcdd19ec2424157290a032d4c41f98127bfb31ac7/numpy-2.3.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f16417ec91f12f814b10bafe79ef77e70113a2f5f7018640e7425ff979253425", size = 12967213 }, ] [[package]]

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