Skip to content

Commit

Permalink
Merge branch 'release/v0.2.17'
Browse files Browse the repository at this point in the history
  • Loading branch information
t-sommer committed Feb 3, 2020
2 parents 1dd3f44 + 52d6ef0 commit d6f042a
Show file tree
Hide file tree
Showing 25 changed files with 85 additions and 96 deletions.
5 changes: 0 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@
# compiled files
*.py[cod]

# shared libraries
*.dll
*.dylib
*.so

# build artifacts
build/
cvode-*/
Expand Down
12 changes: 12 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
## v0.2.17 (2020-02-04)

- `NEW` "Clear Plots" action has been added to the context menu
- `IMPROVED` single quotes are now removed from name segments in the tree view
- `IMPROVED` a RuntimeError is raised when an errors occurs in CVode
- `IMPROVED` exceptions are raised for undefined declaredType, illegal combinations of causality and variability, and missing shared libraries
- `IMPROVED` communicationPoint is now calculated as n_steps * step_size to avoid accumulation of numerical errors

## v0.2.16 (2019-12-26)

- `FIXED` pre-compiled SUNDIALS libraries re-added

## v0.2.15 (2019-12-18)

- `FIXED` validation of structured variable names with apostrophes
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.15'
__version__ = '0.2.17'


# determine the platform
Expand Down
9 changes: 8 additions & 1 deletion fmpy/fmi1.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,18 @@ def __init__(self, guid, modelIdentifier, unzipDirectory, instanceName=None, lib
else:
library_dir = os.path.dirname(libraryPath)

# check if shared library exists
if not os.path.isfile(libraryPath):
raise Exception("Cannot find shared library %s." % libraryPath)

# change to the library directory as some DLLs expect this to resolve dependencies
os.chdir(library_dir)

# load the shared library
self.dll = cdll.LoadLibrary(libraryPath)
try:
self.dll = cdll.LoadLibrary(libraryPath)
except Exception as e:
raise Exception("Failed to load shared library %s. %s" % (libraryPath, e))

# change back to the working directory
os.chdir(work_dir)
Expand Down
7 changes: 7 additions & 0 deletions fmpy/gui/MainWindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ def __init__(self, parent=None):
action = self.columnsMenu.addAction(column)
action.setCheckable(True)
action.toggled.connect(lambda show, col=column: self.showColumn(col, show))
self.contextMenu.addSeparator()
self.actionClearPlots = self.contextMenu.addAction("Clear Plots", self.clearPlots)

# file menu
self.ui.actionExit.triggered.connect(QApplication.closeAllWindows)
Expand Down Expand Up @@ -934,6 +936,11 @@ def getSelectedVariables(self):

return variables

def clearPlots(self):
""" Clear all plots """
self.selectedVariables.clear()
self.updatePlotLayout()

def createGraphics(self):
""" Create the graphical representation of the FMU's inputs and outputs """

Expand Down
3 changes: 2 additions & 1 deletion fmpy/gui/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,8 @@ def setModelDescription(self, md):
else:
prefix = suffix = ''

segments = name.split('.')
# split and remove single quotes from segments
segments = [s[1:-1] if s.startswith("'") and s.endswith("'") else s for s in name.split('.')]

parentItem = self.rootItem

Expand Down
13 changes: 11 additions & 2 deletions fmpy/model_description.py
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,12 @@ def read_model_description(filename, validate=True, validate_variable_names=Fals
sv.max = value.get('max')

# resolve the declared type
sv.declaredType = type_definitions[value.get('declaredType')]
declared_type = value.get('declaredType')
if declared_type in type_definitions:
sv.declaredType = type_definitions[value.get('declaredType')]
else:
raise Exception('Variable "%s" (line %s) has declaredType="%s" which has not been defined.'
% (sv.name, sv.sourceline, declared_type))

if fmiVersion == '1.0':
if sv.causality == 'internal':
Expand All @@ -606,7 +611,11 @@ def read_model_description(filename, validate=True, validate_variable_names=Fals
sv.variability = 'continuous' if sv.type in {'Float32', 'Float64', 'Real'} else 'discrete'

if sv.initial is None:
sv.initial = initial_defaults[sv.variability][sv.causality]
try:
sv.initial = initial_defaults[sv.variability][sv.causality]
except KeyError:
raise Exception('Variable "%s" (line %s) has an illegal combination of causality="%s"'
' and variability="%s".' % (sv.name, sv.sourceline, sv.causality, sv.variability))

dimensions = variable.findall('Dimension')

Expand Down
5 changes: 4 additions & 1 deletion fmpy/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,8 @@ def simulateCS(model_description, fmu_kwargs, start_time, stop_time, relative_to

recorder = Recorder(fmu=fmu, modelDescription=model_description, variableNames=output, interval=output_interval)

n_steps = 0

# simulation loop
while time < stop_time:

Expand All @@ -843,7 +845,8 @@ def simulateCS(model_description, fmu_kwargs, start_time, stop_time, relative_to
if step_finished is not None and not step_finished(time, recorder):
break

time += output_interval
n_steps += 1
time = n_steps * output_interval

recorder.sample(time, force=True)

Expand Down
8 changes: 7 additions & 1 deletion fmpy/sundials/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ def __init__(self,
self.get_dx = get_dx
self.get_z = get_z
self.set_time = set_time
self.error_info = None

self.discrete = nx == 0

Expand Down Expand Up @@ -88,6 +89,7 @@ def __init__(self,
# add function pointers as members to save them from GC
self.f_ = CVRhsFn(self.f)
self.g_ = CVRootFn(self.g)
self.ehfun_ = CVErrHandlerFn(self.ehfun)

assert CVodeInit(self.cvode_mem, self.f_, startTime, self.x) == CV_SUCCESS

Expand All @@ -107,9 +109,11 @@ def __init__(self,

assert CVodeSetNoInactiveRootWarn(self.cvode_mem) == CV_SUCCESS

assert CVodeSetErrHandlerFn(self.cvode_mem, self.ehfun_, None) == CV_SUCCESS

def ehfun(self, error_code, module, function, msg, user_data):
""" Error handler function """
print("[%s] %s" % (module.decode("utf-8"), msg.decode("utf-8")))
self.error_info = (error_code, module.decode("utf-8"), function.decode("utf-8"), msg.decode("utf-8"))

def f(self, t, y, ydot, user_data):
""" Right-hand-side function """
Expand Down Expand Up @@ -156,6 +160,8 @@ def step(self, t, tNext):
if flag == CV_ROOT_RETURN:
p_roots_found = np.ctypeslib.as_ctypes(roots_found)
assert CVodeGetRootInfo(self.cvode_mem, p_roots_found) == CV_SUCCESS
elif flag < 0:
raise RuntimeError("CVode error (code %s) in module %s, function %s: %s" % self.error_info)

return flag > 0, roots_found, tret.value

Expand Down
4 changes: 4 additions & 0 deletions fmpy/sundials/cvode.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
# typedef void (*CVErrHandlerFn)(int error_code,
# const char *module, const char *function,
# char *msg, void *user_data);
CVErrHandlerFn = CFUNCTYPE(None, c_int, c_char_p, c_char_p, c_char_p, c_void_p)
#
# /* -------------------
# * Exported Functions
Expand Down Expand Up @@ -139,6 +140,9 @@
# /* Optional input functions */
# SUNDIALS_EXPORT int CVodeSetErrHandlerFn(void *cvode_mem, CVErrHandlerFn ehfun,
# void *eh_data);
CVodeSetErrHandlerFn = getattr(sundials_cvode, 'CVodeSetErrHandlerFn')
CVodeSetErrHandlerFn.argtypes = [c_void_p, CVErrHandlerFn, c_void_p]
CVodeSetErrHandlerFn.restype = c_int
# SUNDIALS_EXPORT int CVodeSetErrFile(void *cvode_mem, FILE *errfp);
# SUNDIALS_EXPORT int CVodeSetUserData(void *cvode_mem, void *user_data);
# SUNDIALS_EXPORT int CVodeSetMaxOrd(void *cvode_mem, int maxord);
Expand Down
10 changes: 5 additions & 5 deletions fmpy/sundials/libraries.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from ctypes import cdll
import os
from fmpy import sharedLibraryExtension
from fmpy import sharedLibraryExtension, platform_tuple

library_dir, _ = os.path.split(__file__)

# load SUNDIALS shared libraries
sundials_nvecserial = cdll.LoadLibrary(os.path.join(library_dir, 'sundials_nvecserial' + sharedLibraryExtension))
sundials_sunmatrixdense = cdll.LoadLibrary(os.path.join(library_dir, 'sundials_sunmatrixdense' + sharedLibraryExtension))
sundials_sunlinsoldense = cdll.LoadLibrary(os.path.join(library_dir, 'sundials_sunlinsoldense' + sharedLibraryExtension))
sundials_cvode = cdll.LoadLibrary(os.path.join(library_dir, 'sundials_cvode' + sharedLibraryExtension))
sundials_nvecserial = cdll.LoadLibrary(os.path.join(library_dir, platform_tuple, 'sundials_nvecserial' + sharedLibraryExtension))
sundials_sunmatrixdense = cdll.LoadLibrary(os.path.join(library_dir, platform_tuple, 'sundials_sunmatrixdense' + sharedLibraryExtension))
sundials_sunlinsoldense = cdll.LoadLibrary(os.path.join(library_dir, platform_tuple, 'sundials_sunlinsoldense' + sharedLibraryExtension))
sundials_cvode = cdll.LoadLibrary(os.path.join(library_dir, platform_tuple, 'sundials_cvode' + sharedLibraryExtension))
Binary file added fmpy/sundials/x86_64-darwin/sundials_cvode.dylib
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added fmpy/sundials/x86_64-linux/sundials_cvode.so
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added fmpy/sundials/x86_64-windows/sundials_cvode.dll
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
81 changes: 4 additions & 77 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,69 +10,6 @@
except Exception as e:
print("Failed to compile Qt UI and resources. %s" % e)

# build CVode shared libraries
try:
import tarfile
import shutil
from subprocess import check_call
from fmpy import sharedLibraryExtension
from fmpy.util import download_file, visual_c_versions

url = 'https://computing.llnl.gov/projects/sundials/download/cvode-5.0.0.tar.gz'
checksum = '909ae7b696ec5e10a1b13c38708adf27e9a6f9e216a64dc67924263c86add7af'

download_file(url, checksum)

filename = os.path.basename(url)

print("Extracting %s" % filename)
with tarfile.open(filename, 'r:gz') as tar:
tar.extractall()

print("Building CVode")
cmake_args = [
'cmake',
'-B', 'cvode-5.0.0/build',
'-D', 'EXAMPLES_ENABLE_C=OFF',
'-D', 'BUILD_STATIC_LIBS=OFF',
'-D', 'EXAMPLES_INSTALL=OFF',
'-D', 'CMAKE_INSTALL_PREFIX=cvode-5.0.0/dist',
]

vc_versions = visual_c_versions()

if os.name == 'nt':

# set a 64-bit generator
if 160 in vc_versions:
cmake_args += ['-G', 'Visual Studio 16 2019', '-A', 'x64']
elif 150 in vc_versions:
cmake_args += ['-G', 'Visual Studio 15 2017 Win64']
elif 140 in vc_versions:
cmake_args += ['-G', 'Visual Studio 14 2015 Win64']
elif 120 in vc_versions:
cmake_args += ['-G', 'Visual Studio 12 2013 Win64']
elif 110 in vc_versions:
cmake_args += ['-G', 'Visual Studio 11 2012 Win64']

# link statically against the VC runtime
cmake_args += ['-D', 'CMAKE_USER_MAKE_RULES_OVERRIDE:STRING=../OverrideMSVCFlags.cmake']

cmake_args += ['cvode-5.0.0']

check_call(args=cmake_args)
check_call(args=['cmake', '--build', 'cvode-5.0.0/build', '--target', 'install', '--config', 'Release'])

library_prefix = '' if os.name == 'nt' else 'lib'

for shared_library in ['sundials_cvode', 'sundials_nvecserial', 'sundials_sunlinsoldense',
'sundials_sunmatrixdense']:
shutil.copyfile(
os.path.join('cvode-5.0.0', 'dist', 'lib', library_prefix + shared_library + sharedLibraryExtension),
os.path.join('fmpy', 'sundials', shared_library + sharedLibraryExtension))
except Exception as e:
print("Failed to compile CVode shared libraries. %s" % e)

long_description = """
FMPy
====
Expand All @@ -94,9 +31,9 @@
'schema/fmi1/*.xsd',
'schema/fmi2/*.xsd',
'schema/fmi3/*.xsd',
'sundials/sundials_*.dylib',
'sundials/sundials_*.so',
'sundials/sundials_*.dll'],
'sundials/x86_64-darwin/sundials_*.dylib',
'sundials/x86_64-linux/sundials_*.so',
'sundials/x86_64-windows/sundials_*.dll'],
'fmpy.gui': ['icons/app_icon.ico'],
'fmpy.ssp': ['schema/*.xsd'],
}
Expand All @@ -112,7 +49,7 @@
extras_require['complete'] = sorted(set(sum(extras_require.values(), [])))

setup(name='FMPy',
version='0.2.15',
version='0.2.17',
description="Simulate Functional Mock-up Units (FMUs) in Python",
long_description=long_description,
author="Torsten Sommer",
Expand All @@ -124,13 +61,3 @@
install_requires=install_requires,
extras_require=extras_require,
entry_points={'console_scripts': ['fmpy=fmpy.command_line:main']})

# see https://www.python.org/dev/peps/pep-0425/#python-tag
platform_tag = distutils.util.get_platform().replace('-', '_').replace('.', '_')

# add the platform tag to the wheel
for dirpath, _, filenames in os.walk('dist'):
for filename in filenames:
if filename.endswith('-any.whl'):
shutil.move(os.path.join(dirpath, filename),
os.path.join(dirpath, filename).replace('-any.whl', '-' + platform_tag + '.whl'))
22 changes: 20 additions & 2 deletions tests/test_output_grid.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import unittest
import numpy as np
import sys
import os
from fmpy import simulate_fmu
from fmpy.util import download_test_file
from fmpy.util import download_test_file, download_file


class OutputGridTest(unittest.TestCase):
Expand All @@ -11,7 +12,24 @@ class OutputGridTest(unittest.TestCase):
def setUpClass(cls):
print("Python:", sys.version)

def test_step_size(self):
def test_step_size_cs(self):

url = 'https://github.com/modelica/fmi-cross-check/raw/master/fmus/2.0/cs/win64/Test-FMUs/0.0.2/Dahlquist/Dahlquist.fmu'
sha256 = '6df6ab64705615dfa1217123a103c23384a081763a6f71726ba7943503da8fc0'

download_file(url, checksum=sha256)

h = 0.02

result = simulate_fmu(os.path.basename(url), output_interval=h, stop_time=10)

time = result['time']

grid = np.array(range(501)) * h

self.assertTrue(np.all(time == grid))

def test_step_size_me(self):

# download the FMU and input file
for filename in ['CoupledClutches.fmu', 'CoupledClutches_in.csv']:
Expand Down

0 comments on commit d6f042a

Please sign in to comment.