Skip to content

Commit

Permalink
Merge branch 'release/v0.2.24'
Browse files Browse the repository at this point in the history
  • Loading branch information
t-sommer committed Oct 14, 2020
2 parents 0326808 + 3a3f2c0 commit e1927a1
Show file tree
Hide file tree
Showing 12 changed files with 277 additions and 64 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

# build artifacts
build/
cvode-*/
sundials-*/
dist/
MANIFEST
rpclib-*/
Expand Down
44 changes: 27 additions & 17 deletions build_cvode.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
sundials_binary_dir = os.path.join('fmpy', 'sundials', platform_tuple)

# clean up
for build_dir in ['cswrapper/build', 'cvode-5.3.0', sundials_binary_dir, 'fmpy/logging/build']:
for build_dir in ['cswrapper/build', 'sundials-5.3.0', sundials_binary_dir, 'fmpy/logging/build']:
if os.path.isdir(build_dir):
shutil.rmtree(build_dir)

url = 'https://computing.llnl.gov/projects/sundials/download/cvode-5.3.0.tar.gz'
checksum = 'd7ff8e77bb2a59cf8143de30f05a2651c2b4d29b586f8003f9187bf9e5a7da6e'
url = 'https://computing.llnl.gov/projects/sundials/download/sundials-5.3.0.tar.gz'
checksum = '88dff7e11a366853d8afd5de05bf197a8129a804d9d4461fb64297f1ef89bca7'

filename = os.path.basename(url)

Expand All @@ -37,42 +37,52 @@
with tarfile.open(filename, "r:gz") as tar:
tar.extractall()

os.mkdir('cvode-5.3.0/static')
os.mkdir('sundials-5.3.0/static')

# build CVode as static library
check_call([
'cmake',
'-DEXAMPLES_ENABLE_C=OFF',
'-DBUILD_ARKODE=OFF',
'-DBUILD_CVODES=OFF',
'-DBUILD_IDA=OFF',
'-DBUILD_IDAS=OFF',
'-DBUILD_KINSOL=OFF',
'-DBUILD_SHARED_LIBS=OFF',
'-DCMAKE_INSTALL_PREFIX=cvode-5.3.0/static/install',
'-DCMAKE_INSTALL_PREFIX=sundials-5.3.0/static/install',
'-DCMAKE_USER_MAKE_RULES_OVERRIDE=../OverrideMSVCFlags.cmake',
'-DEXAMPLES_ENABLE_C=OFF',
'-G', generator,
'-S', 'cvode-5.3.0',
'-B', 'cvode-5.3.0/static'
'-S', 'sundials-5.3.0',
'-B', 'sundials-5.3.0/static'
])

check_call(['cmake', '--build', 'cvode-5.3.0/static', '--target', 'install', '--config', 'Release'])
check_call(['cmake', '--build', 'sundials-5.3.0/static', '--target', 'install', '--config', 'Release'])

# build CVode as dynamic library
check_call([
'cmake',
'-DEXAMPLES_ENABLE_C=OFF',
'-DBUILD_ARKODE=OFF',
'-DBUILD_CVODES=OFF',
'-DBUILD_IDA=OFF',
'-DBUILD_IDAS=OFF',
'-DBUILD_KINSOL=OFF',
'-DBUILD_STATIC_LIBS=OFF',
'-DCMAKE_INSTALL_PREFIX=cvode-5.3.0/dynamic/install',
'-DEXAMPLES_ENABLE_C=OFF',
'-DCMAKE_INSTALL_PREFIX=sundials-5.3.0/dynamic/install',
'-DCMAKE_USER_MAKE_RULES_OVERRIDE=../OverrideMSVCFlags.cmake',
'-G', generator,
'-S', 'cvode-5.3.0',
'-B', 'cvode-5.3.0/dynamic'
'-S', 'sundials-5.3.0',
'-B', 'sundials-5.3.0/dynamic'
])

check_call(['cmake', '--build', 'cvode-5.3.0/dynamic', '--target', 'install', '--config', 'Release'])
check_call(['cmake', '--build', 'sundials-5.3.0/dynamic', '--target', 'install', '--config', 'Release'])

os.mkdir(sundials_binary_dir)

os.path.join('cvode-5.3.0', 'dynamic', 'install', 'sundials_cvode' + sharedLibraryExtension)
os.path.join('sundials-5.3.0', 'dynamic', 'install', 'sundials_cvode' + sharedLibraryExtension)

for name in ['sundials_cvode', 'sundials_nvecserial', 'sundials_sunlinsoldense', 'sundials_sunmatrixdense']:
src = os.path.join('cvode-5.3.0', 'dynamic', 'install', 'lib', sl_prefix + name + sl_suffix)
src = os.path.join('sundials-5.3.0', 'dynamic', 'install', 'lib', sl_prefix + name + sl_suffix)
dst = os.path.join(sundials_binary_dir, name + sl_suffix)
shutil.copy(src, dst)

Expand All @@ -81,7 +91,7 @@

check_call([
'cmake',
'-DCVODE_INSTALL_DIR=../cvode-5.3.0/static/install',
'-DCVODE_INSTALL_DIR=../sundials-5.3.0/static/install',
'-G', generator,
'-S', 'cswrapper',
'-B', 'cswrapper/build'
Expand Down
7 changes: 7 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## v0.2.24 (2020-10-14)

### Enhancements

- Allow start values with units and display units
- Add FMI 3.0 Scheduled Execution API

## v0.2.23 (2020-09-02)

### Enhancements
Expand Down
14 changes: 14 additions & 0 deletions docs/faq.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
## How do I update an existing installation of FMPy?

If you've installed FMPy with `conda`, open a conda prompt and run

```
conda update -c conda-forge fmpy
```

If you've installed FMPy with `pip`, run

```
pip install fmpy --upgrade
```

## How do I install a development build?

- go to the [CI server](https://dev.azure.com/CATIA-Systems/FMPy/)
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.23'
__version__ = '0.2.24'


# determine the platform
Expand Down
64 changes: 64 additions & 0 deletions fmpy/fmi3.py
Original file line number Diff line number Diff line change
Expand Up @@ -922,3 +922,67 @@ 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


class FMU3ScheduledExecution(_FMU3):
""" An FMI 3.0 Scheduled Execution FMU """

def __init__(self, instanceName=None, **kwargs):
kwargs['instanceName'] = instanceName

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

self._fmi3Function('fmi3InstantiateScheduledExecution', [
(fmi3String, 'instanceName'),
(fmi3String, 'instantiationToken'),
(fmi3String, 'resourceLocation'),
(fmi3Boolean, 'visible'),
(fmi3Boolean, 'loggingOn'),
(POINTER(fmi3ValueReference), 'requiredIntermediateVariables'),
(c_size_t, 'nRequiredIntermediateVariables'),
(fmi3InstanceEnvironment, 'instanceEnvironment'),
(fmi3CallbackLogMessageTYPE, 'logMessage'),
(fmi3CallbackIntermediateUpdateTYPE, 'intermediateUpdate'),
(fmi3CallbackLockPreemptionTYPE, 'lockPreemption'),
(fmi3CallbackUnlockPreemptionTYPE, 'unlockPreemption'),
], fmi3Instance)

self._fmi3Function('fmi3ActivateModelPartition', [
(fmi3Instance, 'instance'),
(fmi3ValueReference, 'clockReference'),
(c_size_t, 'clockElementIndex'),
(fmi3Float64, 'activationTime'),
])

def instantiate(self, visible=False, loggingOn=False, eventModeRequired=False):

resourceLocation = pathlib.Path(self.unzipDirectory, 'resources').as_uri()

def noop():
pass

# save callbacks from GC
self.printLogMessage = fmi3CallbackLogMessageTYPE(printLogMessage)
self.intermediateUpdate = fmi3CallbackIntermediateUpdateTYPE(intermediateUpdate)
self.lockPreemption = fmi3CallbackLockPreemptionTYPE(noop)
self.unlockPreemption = fmi3CallbackUnlockPreemptionTYPE(noop)

self.component = self.fmi3InstantiateScheduledExecution(
self.instanceName.encode('utf-8'),
self.guid.encode('utf-8'),
resourceLocation.encode('utf-8'),
fmi3True if visible else fmi3False,
fmi3True if loggingOn else fmi3False,
None, 0,
fmi3InstanceEnvironment(),
self.printLogMessage,
self.intermediateUpdate,
self.lockPreemption,
self.unlockPreemption
)

if not self.component:
raise Exception("Failed to instantiate FMU")

def activateModelPartition(self, clockReference, clockElementIndex, activationTime):
self.fmi3ActivateModelPartition(self.component, clockReference, clockElementIndex, activationTime)
73 changes: 37 additions & 36 deletions fmpy/model_description.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def __init__(self):

self.coSimulation = None
self.modelExchange = None
self.scheduledExecution = None

self.buildConfigurations = []

Expand All @@ -39,33 +40,44 @@ def __init__(self):
self.stepSize = None


class CoSimulation(object):
class InterfaceType(object):

def __init__(self):
self.modelIdentifier = None
self.needsExecutionTool = False
self.canHandleVariableCommunicationStepSize = False
self.canInterpolateInputs = False
self.maxOutputDerivativeOrder = 0
self.canRunAsynchronuously = False
self.canBeInstantiatedOnlyOncePerProcess = False
self.canNotUseMemoryManagementFunctions = False
self.canGetAndSetFMUstate = False
self.canSerializeFMUstate = False
self.providesDirectionalDerivative = False
self.providesAdjointDerivatives = False
self.providesPerElementDependencies = False

# FMI 2.0
self.canNotUseMemoryManagementFunctions = False
self.providesDirectionalDerivative = False

class ModelExchange(object):

class ModelExchange(InterfaceType):

def __init__(self):
self.modelIdentifier = None
self.needsExecutionTool = False
super(ModelExchange, self).__init__()
self.completedIntegratorStepNotNeeded = False
self.canBeInstantiatedOnlyOncePerProcess = False
self.canNotUseMemoryManagementFunctions = False
self.canGetAndSetFMUstate = False
self.canSerializeFMUstate = False
self.providesDirectionalDerivative = False


class CoSimulation(InterfaceType):

def __init__(self):
super(CoSimulation, self).__init__()
self.canHandleVariableCommunicationStepSize = False
self.canInterpolateInputs = False
self.maxOutputDerivativeOrder = 0
self.canRunAsynchronuously = False


class ScheduledExecution(CoSimulation):

def __init__(self):
super(ScheduledExecution, self).__init__()


class BuildConfiguration(object):
Expand Down Expand Up @@ -262,9 +274,12 @@ def __repr__(self):
return '%s' % self.variable


def _copy_attributes(element, object, attributes):
def _copy_attributes(element, object, attributes=None):
""" Copy attributes from an XML element to a Python object """

if attributes is None:
attributes = object.__dict__.keys()

for attribute in attributes:

if attribute not in element.attrib:
Expand Down Expand Up @@ -485,30 +500,16 @@ def read_model_description(filename, validate=True, validate_variable_names=Fals

for me in root.findall('ModelExchange'):
modelDescription.modelExchange = ModelExchange()
_copy_attributes(me, modelDescription.modelExchange,
['modelIdentifier',
'needsExecutionTool',
'completedIntegratorStepNotNeeded',
'canBeInstantiatedOnlyOncePerProcess',
'canNotUseMemoryManagementFunctions',
'canGetAndSetFMUstate',
'canSerializeFMUstate',
'providesDirectionalDerivative'])
_copy_attributes(me, modelDescription.modelExchange)

for cs in root.findall('CoSimulation'):
modelDescription.coSimulation = CoSimulation()
_copy_attributes(cs, modelDescription.coSimulation,
['modelIdentifier',
'needsExecutionTool',
'canHandleVariableCommunicationStepSize',
'canInterpolateInputs',
'maxOutputDerivativeOrder',
'canRunAsynchronuously',
'canBeInstantiatedOnlyOncePerProcess',
'canNotUseMemoryManagementFunctions',
'canGetAndSetFMUstate',
'canSerializeFMUstate',
'providesDirectionalDerivative'])
_copy_attributes(cs, modelDescription.coSimulation)

for se in root.findall('ScheduledExecution'):
modelDescription.scheduledExecution = ScheduledExecution()
_copy_attributes(se, modelDescription.scheduledExecution)

# build configurations
if fmiVersion == '2.0':

Expand Down
27 changes: 27 additions & 0 deletions fmpy/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,8 @@ def apply_start_values(fmu, model_description, start_values, apply_default_start

start_values = start_values.copy()

unit_definitions = dict((u.name, dict((d.name, d) for d in u.displayUnits)) for u in model_description.unitDefinitions)

for variable in model_description.modelVariables:

if variable.name in start_values:
Expand All @@ -343,6 +345,31 @@ def apply_start_values(fmu, model_description, start_values, apply_default_start
else:
continue

if type(value) is tuple:
if len(value) != 2:
raise Exception('The start value for variable %s must be a scalar value or a tuple (<value>, {<unit>|<display_unit>}) but was "%s".' % (variable.name, value))
value, unit = value
else:
unit = None

if unit is None or unit == variable.unit:
pass
elif variable.declaredType is not None and unit == variable.declaredType.unit:
pass
else:
if variable.unit is not None:
base_unit = variable.unit
elif variable.declaredType is not None:
base_unit = variable.declaredType.unit
else:
raise Exception('Variable %s has no unit but the unit "%s" was specified for its start value.' % (variable.name, unit))

if unit not in unit_definitions[base_unit]:
raise Exception('The unit "%s" of the start value for variable %s is not defined.' % (unit, variable.name))

display_unit = unit_definitions[base_unit][unit]
value = (value - display_unit.offset) / display_unit.factor

vr = variable.valueReference

# get the setter function
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
extras_require['complete'] = sorted(set(sum(extras_require.values(), [])))

setup(name='FMPy',
version='0.2.23',
version='0.2.24',
description="Simulate Functional Mock-up Units (FMUs) in Python",
long_description=long_description,
author="Torsten Sommer",
Expand Down
2 changes: 2 additions & 0 deletions tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# test resources
Reference-FMUs-repo/
Loading

0 comments on commit e1927a1

Please sign in to comment.