Skip to content

Commit

Permalink
Merge branch 'release/v0.2.9'
Browse files Browse the repository at this point in the history
  • Loading branch information
t-sommer committed Feb 7, 2019
2 parents 352d46b + e9aca2a commit c6d7e91
Show file tree
Hide file tree
Showing 16 changed files with 281 additions and 169 deletions.
49 changes: 49 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
version: 2.1

jobs:
build:
parameters:
installer:
description: "URL of the Anaconda installer script"
type: string
machine: true
steps:
- checkout
- run:
name: Install Anaconda
command: |
wget << parameters.installer >> -O anaconda.sh
bash anaconda.sh -b -p ~/anaconda
- run:
name: Add Anaconda to PATH
command: echo 'export PATH=$HOME/anaconda/bin:$PATH' >> $BASH_ENV
- run:
name: Install dependencies
command: |
conda info -a
conda install -c anaconda pathlib
- run:
name: Build wheel
command: |
which python
python setup.py bdist_wheel --universal
- run:
name: Install wheel
command: |
for f in dist/FMPy-*.whl; do
pip install $f --no-deps -vv
done
- run:
name: Run tests
command: |
which fmpy
cd tests
python -m unittest discover
workflows:
build:
jobs:
- build:
installer: https://repo.continuum.io/archive/Anaconda2-2018.12-Linux-x86_64.sh
- build:
installer: https://repo.continuum.io/archive/Anaconda3-2018.12-Linux-x86_64.sh
26 changes: 6 additions & 20 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,27 +1,13 @@
language: python
language: generic
os: osx

matrix:
include:
- os: linux
env:
- PYTHON_VERSION=2.7
- MINICONDA=Miniconda2-latest-Linux-x86_64.sh

- os: linux
env:
- PYTHON_VERSION=3.6
- MINICONDA=Miniconda3-latest-Linux-x86_64.sh

- os: osx
language: generic
env:
- env:
- PYTHON_VERSION=2.7
- MINICONDA=Miniconda2-latest-MacOSX-x86_64.sh

- os: osx
language: generic
env:
- PYTHON_VERSION=3.6
- env:
- PYTHON_VERSION=3.7
- MINICONDA=Miniconda3-latest-MacOSX-x86_64.sh

install:
Expand All @@ -46,7 +32,7 @@ install:
# install the wheel
- |
for f in dist/FMPy-*.whl; do
pip install $f --no-deps
pip install $f --no-deps -vv
done
script:
Expand Down
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
[![Travis CI](https://travis-ci.org/CATIA-Systems/FMPy.svg?branch=master)](https://travis-ci.org/CATIA-Systems/FMPy)
[![AppVeyor](https://ci.appveyor.com/api/projects/status/github/CATIA-Systems/FMPy?branch=master&svg=true)](https://ci.appveyor.com/project/TorstenSommer/fmpy)
[![PyPI version](https://badge.fury.io/py/fmpy.svg)](https://badge.fury.io/py/fmpy)
[![Anaconda-Server Badge](https://anaconda.org/conda-forge/fmpy/badges/version.svg)](https://anaconda.org/conda-forge/fmpy)
[![Documentation Status](https://readthedocs.org/projects/fmpy/badge/?version=latest)](http://fmpy.readthedocs.io/en/latest/?badge=latest)
[![Linux](https://img.shields.io/circleci/project/github/CATIA-Systems/FMPy/master.svg?label=Linux&logo=circleci)](https://circleci.com/gh/CATIA-Systems/FMPy)
[![OSX](https://img.shields.io/travis/CATIA-Systems/FMPy/master.svg?label=macOS&logo=travis)](https://travis-ci.org/CATIA-Systems/FMPy)
[![Windows](https://img.shields.io/appveyor/ci/TorstenSommer/FMPy/master.svg?label=Windows&logo=appveyor)](https://ci.appveyor.com/project/TorstenSommer/FMPy/branch/master)
[![PyPI](https://img.shields.io/pypi/dm/FMPy.svg?label=PyPI%20downloads)](https://pypi.org/project/FMPy/)
[![Conda](https://img.shields.io/conda/dn/conda-forge/FMPy.svg?label=Conda%20downloads)](https://anaconda.org/conda-forge/fmpy)
[![Read the Docs](https://readthedocs.org/projects/fmpy/badge/?version=latest)](https://fmpy.readthedocs.io/)

# FMPy

Expand All @@ -12,7 +13,7 @@ FMPy is a free Python library to simulate [Functional Mock-up Units (FMUs)](http
- supports Co-Simulation and Model Exchange
- runs on Windows, Linux and macOS
- has a graphical user interface
- compiles C code FMUs and generates CMake projects for debugging `NEW`
- compiles C code FMUs and generates [CMake](https://cmake.org/) projects for debugging

## Installation

Expand All @@ -33,10 +34,9 @@ You can start the FMPy GUI with `python -m fmpy.gui`
## Simulate an FMU in Python

To follow this example download `Rectifier.fmu` for your platform by clicking on the respective link:
[Linux](https://github.com/modelica/fmi-cross-check/blob/master/fmus/2.0/cs/linux64/MapleSim/2017/Rectifier/Rectifier.fmu),
[macOS](https://github.com/modelica/fmi-cross-check/blob/master/fmus/2.0/cs/darwin64/MapleSim/2017/Rectifier/Rectifier.fmu),
[Windows (32-bit)](https://github.com/modelica/fmi-cross-check/blob/master/fmus/2.0/cs/win32/MapleSim/2017/Rectifier/Rectifier.fmu),
[Windows (64-bit)](https://github.com/modelica/fmi-cross-check/blob/master/fmus/2.0/cs/win64/MapleSim/2017/Rectifier/Rectifier.fmu).
[Linux](https://github.com/modelica/fmi-cross-check/raw/master/fmus/2.0/cs/linux64/MapleSim/2018/Rectifier/Rectifier.fmu),
[macOS](https://github.com/modelica/fmi-cross-check/raw/master/fmus/2.0/cs/darwin64/MapleSim/2018/Rectifier/Rectifier.fmu),
[Windows](https://github.com/modelica/fmi-cross-check/raw/master/fmus/2.0/cs/win64/MapleSim/2018/Rectifier/Rectifier.fmu).
Change to the folder where you've saved the FMU and open a Python prompt.

```
Expand Down
11 changes: 11 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
## v0.2.9 (2019-02-07)

Improved logging, discrete inputs and tunable parameters

- `FIXED` handling of discrete inputs
- `FIXED` set continuous states after solver step
- `NEW` set tunable parameters via input
- `IMPROVED` disable inactive root warnings for CVode
- `IMPROVED` error and log messages
- `IMPROVED` plotting of discrete signals

## v0.2.8 (2018-12-24)

- `FIXED` Handle optional elements in ScalarVariable tag
Expand Down
19 changes: 10 additions & 9 deletions docs/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ You can start the FMPy GUI with `python -m fmpy.gui`
## Python

To follow this example download `Rectifier.fmu` for your platform by clicking on the respective link:
[Linux](https://github.com/modelica/fmi-cross-check/blob/master/fmus/2.0/cs/linux64/MapleSim/2017/Rectifier/Rectifier.fmu),
[macOS](hhttps://github.com/modelica/fmi-cross-check/blob/master/fmus/2.0/cs/darwin64/MapleSim/2017/Rectifier/Rectifier.fmu),
[Windows (32-bit)](https://github.com/modelica/fmi-cross-check/blob/master/fmus/2.0/cs/win32/MapleSim/2017/Rectifier/Rectifier.fmu),
[Windows (64-bit)](https://github.com/modelica/fmi-cross-check/blob/master/fmus/2.0/cs/win64/MapleSim/2017/Rectifier/Rectifier.fmu).
[Linux](https://github.com/modelica/fmi-cross-check/raw/master/fmus/2.0/cs/linux64/MapleSim/2016.2/Rectifier/Rectifier.fmu),
[macOS](https://github.com/modelica/fmi-cross-check/raw/master/fmus/2.0/cs/darwin64/MapleSim/2016.2/Rectifier/Rectifier.fmu),
[Windows (32-bit)](https://github.com/modelica/fmi-cross-check/raw/master/fmus/2.0/cs/win32/MapleSim/2016.2/Rectifier/Rectifier.fmu),
[Windows (64-bit)](https://github.com/modelica/fmi-cross-check/raw/master/fmus/2.0/cs/win64/MapleSim/2016.2/Rectifier/Rectifier.fmu).
Change to the folder where you've saved the FMU and open a Python prompt.

```
Expand All @@ -21,14 +21,15 @@ Change to the folder where you've saved the FMU and open a Python prompt.
Model Info
FMI Version 2.0
FMI Type Co-Simulation
Model Name Rectifier
Description Model Rectifier
Platforms win64
Continuous States 4
Event Indicators 6
Variables 63
Generation Tool MapleSim (1267140/1267140/1267140)
Generation Date 2017-10-04T12:07:10Z
Generation Tool MapleSim (1196527/1196706/1196706)
Generation Date 2017-01-19T18:42:46Z
Default Experiment
Expand All @@ -37,11 +38,11 @@ Default Experiment
Variables (input, output)
Name Causality Start Value Unit Description
outputs output 282.842712474619 V Rectifier1.Capacitor1.v
Name Causality Start Value Unit Description
outputs output V Rectifier1.Capacitor1.v
>>> result = simulate_fmu(fmu) # simulate the FMU
>>> from fmpy.util import plot_result # import the plot function
>>> plot_result(result) # plot two variables
>>> plot_result(result) # plot the result
```

![Rectifier Result](Rectifier_result.png)
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.8'
__version__ = '0.2.9'


# determine the platform
Expand Down
9 changes: 6 additions & 3 deletions fmpy/cross_check/cross_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,17 @@ def cross_check(fmus_dir, report, result_dir, simulate, tool_name, tool_version,

fmu_filename = None

if 'notCompliantWithLatestRules' in files:
continue # skip

for file in files:
if file.endswith('.fmu'):
fmu_name, _ = os.path.splitext(file) # FMU name without file extension
fmu_filename = os.path.join(root, file) # absolute path filename
break

if fmu_filename is None:
continue
continue # skip

# dictionary that contains the information about the FMU
options = {'fmu_filename': fmu_filename}
Expand All @@ -101,7 +104,7 @@ def cross_check(fmus_dir, report, result_dir, simulate, tool_name, tool_version,
options.update(fmu_path_info(root))

# skip FMUs in _FMIModelicaTest and other directories
if options['fmi_version'] not in ['FMI_1.0', 'FMI_2.0']:
if options['fmi_version'] not in ['1.0', '2.0']:
continue

if skip(options):
Expand Down Expand Up @@ -226,7 +229,7 @@ def check_csv_file(filename, variables):
start_time = time.time()

options['fmu_filename'] = fmu_filename
options['step_size'] = step_size
options['step_size'] = None if step_size == 0 else step_size
options['stop_time'] = stop_time

if in_path is not None:
Expand Down
20 changes: 13 additions & 7 deletions fmpy/fmi1.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def printLogMessage(component, instanceName, status, category, message):
""" Print the FMU's log messages to the command line (works for both FMI 1.0 and 2.0) """

label = ['OK', 'WARNING', 'DISCARD', 'ERROR', 'FATAL', 'PENDING'][status]
print("[%s] %s" % (label, message))
print("[%s] %s" % (label, message.decode("utf-8")))


def allocateMemory(nobj, size):
Expand Down Expand Up @@ -182,6 +182,9 @@ def _log_fmi_args(self, fname, argnames, argtypes, args, restype, res):
else:
# struct
a += str(v._obj)
elif hasattr(v, 'decode'):
# UTF-8 byte string
a += '"' + v.decode('utf-8') + '"'
else:
a += str(v)

Expand All @@ -208,7 +211,7 @@ def _log_fmi_args(self, fname, argnames, argtypes, args, restype, res):
else:
f += str(res)
elif restype == c_void_p:
f += ' -> ' + hex(res)
f += ' -> ' + hex(0 if res is None else res)

self.fmiCallLogger(f)

Expand Down Expand Up @@ -258,7 +261,7 @@ def __init__(self, **kwargs):
['component', 'vr', 'nvr', 'value'],
[fmi1Component, POINTER(fmi1ValueReference), c_size_t, POINTER(fmi1String)])

def _fmi1Function(self, name, argnames, argtypes, restype=fmi1Status):
def _fmi1Function(self, fname, argnames, argtypes, restype=fmi1Status):
""" Add an FMI 1.0 function to this instance and add a wrapper that allows
logging and checks the return code if the return type if fmi1Status
Expand All @@ -270,7 +273,7 @@ def _fmi1Function(self, name, argnames, argtypes, restype=fmi1Status):
"""

# get the exported function form the shared library
f = getattr(self.dll, self.modelIdentifier + '_fmi' + name)
f = getattr(self.dll, self.modelIdentifier + '_fmi' + fname)
f.argtypes = argtypes
f.restype = restype

Expand All @@ -282,17 +285,17 @@ def w(*args):

if self.fmiCallLogger is not None:
# log the call
self._log_fmi_args('fmi' + name, argnames, argtypes, args, restype, res)
self._log_fmi_args('fmi' + fname, argnames, argtypes, args, restype, res)

if restype == fmi1Status:
# check the status code
if res > fmi1Warning:
raise Exception("FMI call failed with status %d." % res)
raise Exception("fmi%s failed with status %d." % (fname, res))

return res

# add the function to the instance
setattr(self, 'fmi1' + name, w)
setattr(self, 'fmi1' + fname, w)

# Inquire version numbers of header files

Expand Down Expand Up @@ -596,6 +599,9 @@ def instantiate(self, functions=None, loggingOn=False):
self.callbacks,
fmi1True if loggingOn else fmi1False)

if self.component is None:
raise Exception("Failed to instantiate model")

def freeInstance(self):
self.fmi1FreeModelInstance(self.component)
self.freeLibrary()
Expand Down
5 changes: 4 additions & 1 deletion fmpy/fmi2.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ def w(*args):
if restype == fmi2Status: # status code
# check the status code
if res > fmi2Warning:
raise Exception("FMI call failed with status %d." % res)
raise Exception("%s failed with status %d." % (fname, res))

return res

Expand Down Expand Up @@ -251,6 +251,9 @@ def instantiate(self, visible=False, callbacks=None, loggingOn=False):
fmi2True if visible else fmi2False,
fmi2True if loggingOn else fmi2False)

if self.component is None:
raise Exception("Failed to instantiate model")

def freeInstance(self):
self.fmi2FreeInstance(self.component)
self.freeLibrary()
Expand Down
2 changes: 1 addition & 1 deletion fmpy/model_description.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ def read_model_description(filename, validate=True):

# default values for 'initial' derived from variability and causality
initial_defaults = {
'constant': {'output': 'exact', 'local': 'exact'},
'constant': {'output': 'exact', 'local': 'exact', 'parameter': 'exact'},
'fixed': {'parameter': 'exact', 'calculatedParameter': 'calculated', 'local': 'calculated'},
'tunable': {'parameter': 'exact', 'calculatedParameter': 'calculated', 'local': 'calculated'},
'discrete': {'input': None, 'output': 'calculated', 'local': 'calculated'},
Expand Down
Loading

0 comments on commit c6d7e91

Please sign in to comment.