Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/pyfmi/fmi3.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ cdef class FMUModelCS3(FMUModelBase3):
cpdef _set_time(self, FMIL3.fmi3_float64_t t)

cpdef FMIL3.fmi3_status_t do_step(self, FMIL3.fmi3_float64_t current_t, FMIL3.fmi3_float64_t step_size, new_step=*)
cdef FMIL3.fmi3_status_t _get_output_derivatives(self, np.ndarray value_refs, np.ndarray values, np.ndarray orders)

cdef class _WorkerClass3:
cdef int _dim
Expand Down
64 changes: 64 additions & 0 deletions src/pyfmi/fmi3.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -3968,6 +3968,70 @@ cdef class FMUModelCS3(FMUModelBase3):

return status

def get_output_derivatives(self, variables, FMIL3.fmi3_int32_t order):
"""
Returns the output derivatives for the specified variables. The
order specifies the nth-derivative.

Parameters::

variables --
The variables for which the output derivatives
should be returned.

order --
The derivative order.

Returns::

The derivatives of the specified order.
"""
cdef FMIL3.fmi3_status_t status
cdef unsigned int max_output_derivative
cdef FMIL.size_t nref
cdef np.ndarray[FMIL3.fmi3_float64_t, ndim=1, mode='c'] values
cdef np.ndarray[FMIL3.fmi3_value_reference_t, ndim=1, mode='c'] value_refs
cdef np.ndarray[FMIL3.fmi3_int32_t, ndim=1, mode='c'] orders

max_output_derivative = FMIL3.fmi3_import_get_capability(self._fmu, FMIL3.fmi3_cs_maxOutputDerivativeOrder)

if order < 1 or order > max_output_derivative:
raise FMUException("The order must be greater than zero and below the maximum output derivative support of the FMU (%d)."%max_output_derivative)

if not isinstance(variables, (str, list)) or not all(isinstance(v, str) for v in variables):
raise FMUException("The variables must either be a string or a list of strings.")

if isinstance(variables, str):
variables = [variables]
nref = len(variables)
value_refs = np.array([self.get_variable_valueref(v) for v in variables], dtype=np.uint32, ndmin=1).ravel()
orders = np.array([order]*nref, dtype=np.int32)
values = np.array([0.0]*nref, dtype=float, ndmin=1)

status = self._get_output_derivatives(value_refs, values, orders)

if status != 0:
raise FMUException('Failed to get the output derivatives.')

return values

cdef FMIL3.fmi3_status_t _get_output_derivatives(self, np.ndarray[FMIL3.fmi3_value_reference_t, ndim=1, mode="c"] value_refs,
np.ndarray[FMIL3.fmi3_float64_t, ndim=1, mode="c"] values,
np.ndarray[FMIL3.fmi3_int32_t, ndim=1, mode="c"] orders):
cdef FMIL3.fmi3_status_t status

if not (np.size(values) >= np.size(value_refs) and np.size(orders) >= np.size(value_refs)):
raise FMUException('Failed to get the output derivatives. Fatal dimension mismatch')

self._log_handler.capi_start_callback(self._max_log_size_msg_sent, self._current_log_size)
status = FMIL3.fmi3_import_get_output_derivatives(self._fmu,
<FMIL3.fmi3_value_reference_t*> value_refs.data, np.size(value_refs),
<FMIL3.fmi3_int32_t*> orders.data,
<FMIL3.fmi3_float64_t*> values.data, np.size(values))
self._log_handler.capi_end_callback(self._max_log_size_msg_sent, self._current_log_size)

return status

def simulate(self,
start_time="Default",
final_time="Default",
Expand Down
4 changes: 4 additions & 0 deletions src/pyfmi/fmil3_import.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,10 @@ cdef extern from 'fmilib.h':
fmi3_value_reference_t*, size_t,
fmi3_float64_t*, size_t,
fmi3_float64_t*, size_t)
fmi3_status_t fmi3_import_get_output_derivatives(fmi3_import_t*,
fmi3_value_reference_t*, size_t,
fmi3_int32_t*,
fmi3_float64_t*, size_t)

# Misc
fmi3_status_t fmi3_import_update_discrete_states(
Expand Down
33 changes: 33 additions & 0 deletions tests/test_fmi3.py
Original file line number Diff line number Diff line change
Expand Up @@ -1631,6 +1631,39 @@ def test_do_step_terminated_resets(self, fmi3_cs_stair):
assert not fmi3_cs_stair.do_step_terminated


@pytest.mark.parametrize("order", [1, 2])
def test_get_output_derivatives_order_too_high(self, order, fmi3_cs_feedthrough):
"""get_output_derivatives should raise if the order exceeds the FMU's
maxOutputDerivativeOrder. None of the reference FMUs declare support for
output derivatives, so any positive order is out of range."""
fmi3_cs_feedthrough.initialize()
assert fmi3_cs_feedthrough.get_capability_flags()["maxOutputDerivativeOrder"] == 0

msg = "The order must be greater than zero and below the maximum output " \
"derivative support of the FMU (0)."
with pytest.raises(FMUException, match = re.escape(msg)):
fmi3_cs_feedthrough.get_output_derivatives("Float64_continuous_output", order)

@pytest.mark.parametrize("order", [0, -1])
def test_get_output_derivatives_order_too_low(self, order, fmi3_cs_feedthrough):
"""get_output_derivatives should raise for a non-positive order."""
fmi3_cs_feedthrough.initialize()

msg = "The order must be greater than zero and below the maximum output " \
"derivative support of the FMU (0)."
with pytest.raises(FMUException, match = re.escape(msg)):
fmi3_cs_feedthrough.get_output_derivatives("Float64_continuous_output", order)

def test_get_output_derivatives_list_input(self, fmi3_cs_feedthrough):
"""A list of variables is accepted; the order is still validated per the
FMU's maxOutputDerivativeOrder (0 for the reference FMUs)."""
fmi3_cs_feedthrough.initialize()

msg = "The order must be greater than zero and below the maximum output " \
"derivative support of the FMU (0)."
with pytest.raises(FMUException, match = re.escape(msg)):
fmi3_cs_feedthrough.get_output_derivatives(["Float64_continuous_output"], 1)

class TestFMI3SE:
# TODO: Unsupported for now
pass
Loading