From 0b62d0104adbbd861559bf2c85a313e9229380ba Mon Sep 17 00:00:00 2001 From: Torsten Sommer Date: Tue, 27 Feb 2018 16:10:13 +0100 Subject: [PATCH] Add getDirectionalDerivative() --- docs/changelog.md | 6 +++ fmpy/fmi2.py | 27 ++++++++++ tests/test_get_directional_derivative.py | 63 ++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 tests/test_get_directional_derivative.py diff --git a/docs/changelog.md b/docs/changelog.md index f3ad7cdd..b3bb8deb 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,3 +1,8 @@ +## v0.2.4 (unreleased) + +- `NEW` getDirectionalDerivative() + + ## v0.2.3 (2018-04-11) - `NEW` Allow simulation of extracted FMUs and pre-loaded model descriptions @@ -16,6 +21,7 @@ - `FIXED` Handling of time events - `FIXED` Conversion of Boolean start values + ## v0.2.2 (2018-03-13) - `NEW` FMI 2.0 state serialization functions added diff --git a/fmpy/fmi2.py b/fmpy/fmi2.py index 1bdaeb56..332781f3 100644 --- a/fmpy/fmi2.py +++ b/fmpy/fmi2.py @@ -174,6 +174,12 @@ def __init__(self, **kwargs): [fmi2Component, POINTER(fmi2Byte), c_size_t, POINTER(fmi2FMUstate)], fmi2Status) + # Getting partial derivatives + self._fmi2Function('fmi2GetDirectionalDerivative', + ['component', 'vUnknown_ref', 'nUnknown', 'vKnown_ref', 'nKnown', 'dvKnown', 'dvUnknown'], + [fmi2Component, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2ValueReference), c_size_t, POINTER(fmi2Real), POINTER(fmi2Real)], + fmi2Status) + def _fmi2Function(self, fname, argnames, argtypes, restype): if not hasattr(self.dll, fname): @@ -335,6 +341,27 @@ def deSerializeFMUstate(self, serializedState, state): buffer = create_string_buffer(serializedState) self.fmi2DeSerializeFMUstate(self.component, buffer, len(buffer), byref(state)) + def getDirectionalDerivative(self, vUnknown_ref, vKnown_ref, dvKnown): + """ Get the partial derivative + + Parameters: + vUnknown_ref a list of value references of the unknowns + vKnown_ref a list of value references of the knowns + dvKnown a list of delta values (one per known) + + Returns: + a list of the partial derivatives (one per unknown) + """ + + vUnknown_ref = (fmi2ValueReference * len(vUnknown_ref))(*vUnknown_ref) + vKnown_ref = (fmi2ValueReference * len(vKnown_ref))(*vKnown_ref) + dvKnown = (fmi2Real * len(dvKnown))(*dvKnown) + dvUnknown = (fmi2Real * len(vUnknown_ref))() + + self.fmi2GetDirectionalDerivative(self.component, vUnknown_ref, len(vUnknown_ref), vKnown_ref, len(vKnown_ref), dvKnown, dvUnknown) + + return list(dvUnknown) + class FMU2Model(_FMU2): """ Base class for FMI 2.0 model exchange FMUs """ diff --git a/tests/test_get_directional_derivative.py b/tests/test_get_directional_derivative.py new file mode 100644 index 00000000..7f99f65f --- /dev/null +++ b/tests/test_get_directional_derivative.py @@ -0,0 +1,63 @@ +import unittest +from shutil import rmtree +from unittest import skipIf + +from fmpy import platform, read_model_description, extract +from fmpy.util import download_test_file +from fmpy.fmi2 import FMU2Slave + + +class GetDirectionalDerivativeTest(unittest.TestCase): + + @skipIf(platform not in ['win32', 'win64'], "Current platform not supported by this FMU") + def test_get_directional_derivative(self): + + fmu_filename = 'Rectifier.fmu' + + download_test_file('2.0', 'CoSimulation', 'Dymola', '2017', 'Rectifier', fmu_filename) + + model_description = read_model_description(filename=fmu_filename) + + unzipdir = extract(fmu_filename) + + fmu = FMU2Slave(guid=model_description.guid, + unzipDirectory=unzipdir, + modelIdentifier=model_description.coSimulation.modelIdentifier) + + fmu.instantiate() + fmu.setupExperiment() + fmu.enterInitializationMode() + + # get the partial derivative for an initial unknown + unknown = model_description.initialUnknowns[1] + + self.assertEqual('iAC[1]', unknown.variable.name) + + vrs_unknown = [unknown.variable.valueReference] + vrs_known = [v.valueReference for v in unknown.dependencies] + dv_known = [1.0] * len(unknown.dependencies) + + partial_der = fmu.getDirectionalDerivative(vUnknown_ref=vrs_unknown, vKnown_ref=vrs_known, dvKnown=dv_known) + + self.assertEqual([-2.0], partial_der) + + fmu.exitInitializationMode() + + # get the partial derivative for three output variables + unknowns = model_description.outputs[4:7] + + self.assertEqual(['uAC[1]', 'uAC[2]', 'uAC[3]'], [u.variable.name for u in unknowns]) + + vrs_unknown = [u.variable.valueReference for u in unknowns] + vrs_known = [v.valueReference for v in unknowns[0].dependencies] + dv_known = [1.0] * len(vrs_known) + + partial_der = fmu.getDirectionalDerivative(vUnknown_ref=vrs_unknown, vKnown_ref=vrs_known, dvKnown=dv_known) + + self.assertAlmostEqual(-1500, partial_der[0]) + self.assertAlmostEqual(0, partial_der[1]) + self.assertAlmostEqual(1500, partial_der[2]) + + fmu.terminate() + fmu.freeInstance() + rmtree(unzipdir)