diff --git a/src/common/core.py b/src/common/core.py index 409d941d..97c2c044 100644 --- a/src/common/core.py +++ b/src/common/core.py @@ -470,7 +470,7 @@ class Trajectory: def __init__(self, abscissa, ordinate, tol=1e-8): """ - Default constructor for creating a tracjectory object. + Default constructor for creating a trajectory object. Parameters:: @@ -479,7 +479,7 @@ def __init__(self, abscissa, ordinate, tol=1e-8): (independent) values. ordinate -- - Two dimensional n x m numpy matrix containing the ordiate + Two dimensional n x m numpy matrix containing the ordinate values. The matrix has the same number of rows as the abscissa has elements. The number of columns is equal to the number of output variables. @@ -507,7 +507,7 @@ def __init__(self, abscissa, ordinate, tol=1e-8): def eval(self,x): """ - Evaluate the trajectory at a specifed abscissa. + Evaluate the trajectory at a specified abscissa. Parameters:: @@ -667,12 +667,22 @@ def __init__(self, func): func -- A function which calculates the ordinate values. + Assumed to return either simple numbers or iterables. """ self.traj = func + def _eval_traj_with_float_abscissa(self, x): + """ Evaluate user trajectory function with a single float abscissa and + convert output to the format & shape specified by Trajectory.eval(...). """ + res = self.traj(x) + if hasattr(res, "__iter__"): # iterable + return np.reshape(np.array(res), (1, -1)) + else: # assume simple number + return np.array([[res]]) + def eval(self, x): """ - Evaluate the trajectory at a specifed abscissa. + Evaluate the trajectory at a specified abscissa. Parameters:: @@ -685,12 +695,7 @@ def eval(self, x): Two dimensional n x m matrix containing the ordinate values corresponding to the argument x. """ - try: - y = np.array(np.matrix(self.traj(float(x)))) - except TypeError: - y = np.array(np.matrix(self.traj(x)).transpose()) - #In order to guarantee that the - #return values are on the correct - #form. May need to be evaluated - #for speed improvements. - return y + if hasattr(x, "__iter__"): + return np.vstack([self._eval_traj_with_float_abscissa(i) for i in x]) + else: + return self._eval_traj_with_float_abscissa(x) diff --git a/src/pyfmi/examples/fmu_with_input_function.py b/src/pyfmi/examples/fmu_with_input_function.py index 2e4c42f5..064426cb 100644 --- a/src/pyfmi/examples/fmu_with_input_function.py +++ b/src/pyfmi/examples/fmu_with_input_function.py @@ -62,4 +62,3 @@ def run_demo(with_plots=True): if __name__=="__main__": run_demo() - diff --git a/src/pyfmi/fmi.pyx b/src/pyfmi/fmi.pyx index 13263331..48e45a3b 100644 --- a/src/pyfmi/fmi.pyx +++ b/src/pyfmi/fmi.pyx @@ -5767,7 +5767,7 @@ cdef class FMUModelBase2(ModelBase): relative_quantity = 1 if relative_quantity_bool == FMI2_TRUE else 0 vr = FMIL.fmi2_import_get_variable_vr(variable) - value = self.get_real(vr) + value = self.get_real(vr)[0] display_value = FMIL.fmi2_import_convert_to_display_unit(value, display_unit, relative_quantity) diff --git a/tests/test_common.py b/tests/test_common.py new file mode 100644 index 00000000..2547fd40 --- /dev/null +++ b/tests/test_common.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2024 Modelon AB +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Testing common functionality + +import numpy as np +from pyfmi.common.core import TrajectoryLinearInterpolation, TrajectoryUserFunction + + +class TestTrajectoryLinearInterpolation: + def test_shape(self): + """Test returned shape of TrajectoryLinearInterpolation.""" + t = np.linspace(0, 1, 11) + x = np.random.rand(11, 3) + traj = TrajectoryLinearInterpolation(t, x) + assert traj.eval(0.5).shape == (1, 3) + + +class TestTrajectoryUserFunction: + def test_shape_1_dim(self): + """Testing shape of TrajectoryUserFunction; 1 dim output""" + traj = TrajectoryUserFunction(lambda x: 5) + + assert traj.eval(1).shape == (1, 1) + assert traj.eval(np.array([1])).shape == (1, 1) + + assert traj.eval([1, 2]).shape == (2, 1) + assert traj.eval(np.array([1, 2])).shape == (2, 1) + + def test_shape_multi_dim(self): + """Testing shape of TrajectoryUserFunction; multi dim output; array""" + traj = TrajectoryUserFunction(lambda x: np.array([1, 2, 3])) + + assert traj.eval(1).shape == (1, 3) + assert traj.eval(np.array([1])).shape == (1, 3) + + assert traj.eval([1, 2]).shape == (2, 3) + assert traj.eval(np.array([1, 2])).shape == (2, 3) + + def test_shape_multi_dim_list(self): + """Testing shape of TrajectoryUserFunction; multi dim output, list""" + traj = TrajectoryUserFunction(lambda x: list(range(3))) + + assert traj.eval(1).shape == (1, 3) + assert traj.eval(np.array([1])).shape == (1, 3) + + assert traj.eval([1, 2]).shape == (2, 3) + assert traj.eval(np.array([1, 2])).shape == (2, 3)