Skip to content

Commit

Permalink
Merge branch 'release/v0.2.23'
Browse files Browse the repository at this point in the history
  • Loading branch information
t-sommer committed Sep 2, 2020
2 parents a79cf68 + 1204758 commit 0326808
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 101 deletions.
6 changes: 3 additions & 3 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:

- bash: |
source activate myEnvironment
conda install --yes --quiet --name myEnvironment -c conda-forge python=$PYTHON_VERSION cmake dask lark-parser lxml matplotlib numpy pyqt pyqtgraph pytest-cov requests $PYTHON_LIBRARIES
conda install --yes --quiet --name myEnvironment -c conda-forge python=$PYTHON_VERSION cmake dask lark-parser lxml matplotlib numpy pyqt pyqtgraph pytest-cov requests scipy $PYTHON_LIBRARIES
displayName: Install Anaconda packages
- bash: |
Expand Down Expand Up @@ -85,7 +85,7 @@ jobs:

- bash: |
source activate myEnvironment
conda install --yes --quiet --name myEnvironment -c conda-forge python=$PYTHON_VERSION dask lark-parser lxml matplotlib numpy pyqt pyqtgraph pytest-cov requests $PYTHON_LIBRARIES
conda install --yes --quiet --name myEnvironment -c conda-forge python=$PYTHON_VERSION dask lark-parser lxml matplotlib numpy pyqt pyqtgraph pytest-cov requests scipy $PYTHON_LIBRARIES
displayName: Install Anaconda packages
- bash: |
Expand Down Expand Up @@ -143,7 +143,7 @@ jobs:

- script: |
call activate myEnvironment
conda install --yes --quiet --name myEnvironment -c conda-forge python=%PYTHON_VERSION% cmake dask lark-parser lxml matplotlib numpy pyqt pyqtgraph pytest-cov pywin32 requests
conda install --yes --quiet --name myEnvironment -c conda-forge python=%PYTHON_VERSION% cmake dask lark-parser lxml matplotlib numpy pyqt pyqtgraph pytest-cov pywin32 requests scipy
displayName: Install Anaconda packages
- script: |
Expand Down
17 changes: 17 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
## v0.2.23 (2020-09-02)

### Enhancements

- Add getAdjointDerivative() and fix getDirectionalDerivative()
- Validate results for FMI 3 Reference FMUs
- Add getClock() and setClock() to _FMU3
- Add FMU2Model.getNominalsOfContinuousStates()

### Bug fixes

- Fix logging for FMI 3 (#159)
- Read start value of String variables in FMI 3
- Add missing fields to EventInfoReturnValue message (#160)
- Move enterEventMode() and newDiscreteStates() to _FMU3
- Fix variabilities for variable type "Clock"

## v0.2.22 (2020-08-01)

- `FIXED' Forward fmi2NewDiscreteStates() in remoting client (#154)
Expand Down
2 changes: 1 addition & 1 deletion fmpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from ctypes import *
import _ctypes

__version__ = '0.2.22'
__version__ = '0.2.23'


# determine the platform
Expand Down
17 changes: 13 additions & 4 deletions fmpy/fmi1.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,15 +172,24 @@ def _log_fmi_args(self, fname, argnames, argtypes, args, restype, res):
a += hex(0 if v is None else v)
elif t == POINTER(c_uint):
# value references
a += '[' + ', '.join(map(str, v)) + ']'
if v is None:
a += 'NULL'
else:
a += '[' + ', '.join(map(str, v)) + ']'
elif t == POINTER(c_double):
if hasattr(v, '__len__'):
# c_double_Array_N
a += '[' + ', '.join(map(str, v)) + ']'
else:
# double pointers are always flowed by the size of the array
arr = np.ctypeslib.as_array(v, (args[i+1],))
a += '[' + ', '.join(map(str, arr)) + ']'
if len(args) > i + 1:
# double pointers are always flowed by the size of the array
arr = np.ctypeslib.as_array(v, (args[i + 1],))
a += '[' + ', '.join(map(str, arr)) + ']'
else:
# except for fmi3DoStep
v_ = cast(v, POINTER(c_double))
a += str(str(v_.contents.value))

elif hasattr(v, '_obj'):
# byref object
if hasattr(v._obj, 'value'):
Expand Down
38 changes: 24 additions & 14 deletions fmpy/fmi2.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,12 @@ def __init__(self, **kwargs):

super(FMU2Model, self).__init__(**kwargs)

# Enter and exit the different modes

self._fmi2Function('fmi2EnterEventMode',
['component'],
[fmi2Component])

self._fmi2Function('fmi2NewDiscreteStates',
['component', 'eventInfo'],
[fmi2Component, POINTER(fmi2EventInfo)])
Expand All @@ -421,18 +427,22 @@ def __init__(self, **kwargs):
['component'],
[fmi2Component])

self._fmi2Function('fmi2EnterEventMode',
['component'],
[fmi2Component])
self._fmi2Function('fmi2CompletedIntegratorStep',
['component', 'noSetFMUStatePriorToCurrentPoint', 'enterEventMode', 'terminateSimulation'],
[fmi2Component, fmi2Boolean, POINTER(fmi2Boolean), POINTER(fmi2Boolean)])

self._fmi2Function('fmi2GetContinuousStates',
['component', 'x', 'nx'],
[fmi2Component, POINTER(fmi2Real), c_size_t])
# Providing independent variables and re-initialization of caching

self._fmi2Function('fmi2SetTime',
['component', 'time'],
[fmi2Component, fmi2Real])

self._fmi2Function('fmi2SetContinuousStates',
['component', 'x', 'nx'],
[fmi2Component, POINTER(fmi2Real), c_size_t])

# Evaluation of the model equations

self._fmi2Function('fmi2GetDerivatives',
['component', 'derivatives', 'nx'],
[fmi2Component, POINTER(fmi2Real), c_size_t])
Expand All @@ -441,13 +451,13 @@ def __init__(self, **kwargs):
['component', 'eventIndicators', 'ni'],
[fmi2Component, POINTER(fmi2Real), c_size_t])

self._fmi2Function('fmi2SetTime',
['component', 'time'],
[fmi2Component, fmi2Real])
self._fmi2Function('fmi2GetContinuousStates',
['component', 'x', 'nx'],
[fmi2Component, POINTER(fmi2Real), c_size_t])

self._fmi2Function('fmi2CompletedIntegratorStep',
['component', 'noSetFMUStatePriorToCurrentPoint', 'enterEventMode', 'terminateSimulation'],
[fmi2Component, fmi2Boolean, POINTER(fmi2Boolean), POINTER(fmi2Boolean)])
self._fmi2Function('fmi2GetNominalsOfContinuousStates',
['component', 'x_nominal', 'nx'],
[fmi2Component, POINTER(fmi2Real), c_size_t])

# Enter and exit the different modes

Expand Down Expand Up @@ -495,8 +505,8 @@ def getEventIndicators(self, z, nz):
def getContinuousStates(self, x, nx):
return self.fmi2GetContinuousStates(self.component, x, nx)

def getNominalsOfContinuousStatesTYPE(self):
pass
def getNominalsOfContinuousStates(self, x_nominal, nx):
return self.fmi2GetNominalsOfContinuousStates(self.component, x_nominal, nx)


class FMU2Slave(_FMU2):
Expand Down
162 changes: 97 additions & 65 deletions fmpy/fmi3.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,18 @@ def __init__(self, **kwargs):
(c_size_t, 'nSensitivity')
])

self._fmi3Function('fmi3GetAdjointDerivative', [
(fmi3Instance, 'instance'),
(POINTER(fmi3ValueReference), 'unknowns'),
(c_size_t, 'nUnknowns'),
(POINTER(fmi3ValueReference), 'knowns'),
(c_size_t, 'nKnowns'),
(POINTER(fmi3Float64), 'seed'),
(c_size_t, 'nSeed'),
(POINTER(fmi3Float64), 'sensitivity'),
(c_size_t, 'nSensitivity')
])

# Entering and exiting the Configuration or Reconfiguration Mode
self._fmi3Function('fmi3EnterConfigurationMode', [(fmi3Instance, 'instance')])

Expand Down Expand Up @@ -376,6 +388,42 @@ def enterInitializationMode(self, tolerance=None, startTime=0.0, stopTime=None):
def exitInitializationMode(self):
return self.fmi3ExitInitializationMode(self.component)

def enterEventMode(self, stepEvent=False, rootsFound=[], timeEvent=False):

rootsFound = (fmi3Int32 * len(rootsFound))(*rootsFound)

return self.fmi3EnterEventMode(
self.component,
fmi3True if stepEvent else fmi3False,
rootsFound,
len(rootsFound),
fmi3True if timeEvent else fmi3False,
)

def newDiscreteStates(self):

newDiscreteStatesNeeded = fmi3Boolean()
terminateSimulation = fmi3Boolean()
nominalsOfContinuousStatesChanged = fmi3Boolean()
valuesOfContinuousStatesChanged = fmi3Boolean()
nextEventTimeDefined = fmi3Boolean()
nextEventTime = fmi3Float64()

self.fmi3NewDiscreteStates(self.component,
byref(newDiscreteStatesNeeded),
byref(terminateSimulation),
byref(nominalsOfContinuousStatesChanged),
byref(valuesOfContinuousStatesChanged),
byref(nextEventTimeDefined),
byref(nextEventTime))

return (newDiscreteStatesNeeded.value != fmi3False,
terminateSimulation.value != fmi3False,
nominalsOfContinuousStatesChanged.value != fmi3False,
valuesOfContinuousStatesChanged.value != fmi3False,
nextEventTimeDefined.value != fmi3False,
nextEventTime.value)

def terminate(self):
return self.fmi3Terminate(self.component)

Expand Down Expand Up @@ -485,6 +533,14 @@ def getBinary(self, vr):
self.fmi3GetBinary(self.component, vr, len(vr), size, value, len(value))
return list(value)

def getClock(self, vr, nValues=None):
if nValues is None:
nValues = len(vr)
vr = (fmi3ValueReference * len(vr))(*vr)
value = (fmi3Clock * nValues)()
self.fmi3GetClock(self.component, vr, len(vr), value, nValues)
return list(value)

def setFloat32(self, vr, values):
vr = (fmi3ValueReference * len(vr))(*vr)
values = (fmi3Float32 * len(values))(*values)
Expand Down Expand Up @@ -552,6 +608,11 @@ def setBinary(self, vr, values):
size = (c_size_t * len(vr))(*[len(v) for v in values])
self.fmi3SetBinary(self.component, vr, len(vr), size, values_, len(values))

def setClock(self, vr, values, subactive=None):
vr = (fmi3ValueReference * len(vr))(*vr)
values = (fmi3Clock * len(values))(*values)
self.fmi3SetClock(self.component, vr, len(vr), values, subactive, len(values))

# Getting and setting the internal FMU state

def getFMUState(self):
Expand Down Expand Up @@ -594,26 +655,47 @@ def deSerializeFMUState(self, serializedState, state):

# Getting partial derivatives

def getDirectionalDerivative(self, vUnknown_ref, vKnown_ref, dvKnown):
""" Get partial derivatives
def getDirectionalDerivative(self, unknowns, knowns, seed, sensitivity):
""" Get the directional derivatives
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)
unknowns list of value references of the unknowns
knowns list of value references of the knowns
seed list of delta values (one per known)
Returns:
a list of the partial derivatives (one per unknown)
sensitivity list of the partial derivatives (one per unknown)
"""

vUnknown_ref = (fmi3ValueReference * len(vUnknown_ref))(*vUnknown_ref)
vKnown_ref = (fmi3ValueReference * len(vKnown_ref))(*vKnown_ref)
dvKnown = (fmi3Float64 * len(dvKnown))(*dvKnown)
dvUnknown = (fmi3Float64 * len(vUnknown_ref))()
unknowns = (fmi3ValueReference * len(unknowns))(*unknowns)
knowns = (fmi3ValueReference * len(knowns))(*knowns)
seed = (fmi3Float64 * len(seed))(*seed)
sensitivity = (fmi3Float64 * len(sensitivity))()

self.fmi3GetDirectionalDerivative(self.component, vUnknown_ref, len(vUnknown_ref), vKnown_ref, len(vKnown_ref), dvKnown, dvUnknown)
self.fmi3GetDirectionalDerivative(self.component, unknowns, len(unknowns), knowns, len(knowns), seed, sensitivity)

return list(dvUnknown)
return list(sensitivity)

def getAdjointDerivative(self, unknowns, knowns, seed, sensitivity):
""" Get adjoint derivatives
Parameters:
unknowns list of value references of the unknowns
knowns list of value references of the knowns
seed list of delta values (one per known)
Returns:
sensitivity list of the partial derivatives (one per unknown)
"""

unknowns = (fmi3ValueReference * len(unknowns))(*unknowns)
knowns = (fmi3ValueReference * len(knowns))(*knowns)
seed = (fmi3Float64 * len(seed))(*seed)
sensitivity = (fmi3Float64 * len(sensitivity))()

self.fmi3GetAdjointDerivative(self.component, unknowns, len(unknowns), knowns, len(knowns), seed, sensitivity)

return list(sensitivity)


class FMU3Model(_FMU3):
Expand Down Expand Up @@ -708,42 +790,6 @@ def instantiate(self, visible=False, loggingOn=False):

# Enter and exit the different modes

def enterEventMode(self, stepEvent=False, rootsFound=[], timeEvent=False):

rootsFound = (fmi3Int32 * len(rootsFound))(*rootsFound)

return self.fmi3EnterEventMode(
self.component,
fmi3True if stepEvent else fmi3False,
rootsFound,
len(rootsFound),
fmi3True if timeEvent else fmi3False,
)

def newDiscreteStates(self):

newDiscreteStatesNeeded = fmi3Boolean()
terminateSimulation = fmi3Boolean()
nominalsOfContinuousStatesChanged = fmi3Boolean()
valuesOfContinuousStatesChanged = fmi3Boolean()
nextEventTimeDefined = fmi3Boolean()
nextEventTime = fmi3Float64()

self.fmi3NewDiscreteStates(self.component,
byref(newDiscreteStatesNeeded),
byref(terminateSimulation),
byref(nominalsOfContinuousStatesChanged),
byref(valuesOfContinuousStatesChanged),
byref(nextEventTimeDefined),
byref(nextEventTime))

return (newDiscreteStatesNeeded.value != fmi3False,
terminateSimulation.value != fmi3False,
nominalsOfContinuousStatesChanged.value != fmi3False,
valuesOfContinuousStatesChanged.value != fmi3False,
nextEventTimeDefined.value != fmi3False,
nextEventTime.value)

def enterContinuousTimeMode(self):
return self.fmi3EnterContinuousTimeMode(self.component)

Expand Down Expand Up @@ -852,6 +898,9 @@ def instantiate(self, visible=False, loggingOn=False, eventModeRequired=False):
if not self.component:
raise Exception("Failed to instantiate FMU")

def enterStepMode(self):
return self.fmi3EnterStepMode(self.component)

# Simulating the slave

def setInputDerivatives(self, vr, order, value):
Expand All @@ -873,20 +922,3 @@ def doStep(self, currentCommunicationPoint, communicationStepSize, noSetFMUState
lastSuccessfulTime = fmi3Float64()
status = self.fmi3DoStep(self.component, currentCommunicationPoint, communicationStepSize, noSetFMUStatePriorToCurrentPoint, byref(terminate), byref(earlyReturn), byref(lastSuccessfulTime))
return status, terminate.value != fmi3False, earlyReturn.value != fmi3False, lastSuccessfulTime.value

def cancelStep(self):
self.fmi3CancelStep(self.component)

# Inquire slave status

def getDoStepPendingStatus(self):
status = fmi3Status(fmi3OK)
message = fmi3String()
self.fmi3GetDoStepPendingStatus(self.component, byref(status), byref(message))
return status, message

def getDoStepDiscardedStatus(self):
terminate = fmi3Boolean(fmi3False)
lastSuccessfulTime = fmi3Float64(0)
self.fmi3GetDoStepDiscardedStatus(self.component, byref(terminate), byref(lastSuccessfulTime))
return terminate, lastSuccessfulTime
Loading

0 comments on commit 0326808

Please sign in to comment.