Skip to content

Commit

Permalink
Added an exception if the Simulator fails (#420)
Browse files Browse the repository at this point in the history
* Added an exception if the Simulator fails

For #419

* Separated tests to make debugging easier

* Changed to Buildings v7.0.0 for example (as model is removed in latest version
  • Loading branch information
mwetter authored Dec 8, 2021
1 parent 1783a1e commit 4291001
Show file tree
Hide file tree
Showing 12 changed files with 135 additions and 52 deletions.
17 changes: 15 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ before_install:
- sudo cp buildingspy/tests/MyModelicaLibrary/Resources/Scripts/travis/bin/jm_ipython.sh /usr/local/bin/
- echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
- docker pull "$DOCKER_USERNAME"/travis_ubuntu-1804_dymola:2022-x86_64
- docker pull "$DOCKER_USERNAME"/travis-ubuntu-1804-optimica:r19089
- docker pull "$DOCKER_USERNAME"/travis-ubuntu-1804-optimica:r26446
- python -m pip install --upgrade pip

install:
Expand Down Expand Up @@ -68,7 +68,20 @@ script:
- omc --version
- make pep8 PEP8_CORRECT_CODE=true
- make doctest
- make unittest
- make unittest_development_error_dictionary
- make unittest_development_merger
- make unittest_development_refactor
- make unittest_development_regressiontest_jmodelica
- make unittest_development_regressiontest_openmodelica
- make unittest_development_regressiontest_optimica
- make unittest_development_regressiontest
- make unittest_development_Validator
- make unittest_examples_dymola
- make unittest_io_outputfile
- make unittest_io_postprocess
- make unittest_simulate_Dymola
- make unittest_simulate_Optimica
- make unittest_simulate_Simulator
- make doc

after_failure:
Expand Down
42 changes: 42 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,48 @@ unittest:
# python buildingspy/tests/test_development_regressiontest_optimica.py Test_regressiontest_optimica_Tester.test_regressiontest_diagnostics
python3 -m unittest discover buildingspy/tests

unittest_development_error_dictionary:
python3 buildingspy/tests/test_development_error_dictionary.py

unittest_development_merger:
python3 buildingspy/tests/test_development_merger.py

unittest_development_refactor:
python3 buildingspy/tests/test_development_refactor.py

unittest_development_regressiontest_jmodelica:
python3 buildingspy/tests/test_development_regressiontest_jmodelica.py

unittest_development_regressiontest_openmodelica:
python3 buildingspy/tests/test_development_regressiontest_openmodelica.py

unittest_development_regressiontest_optimica:
python3 buildingspy/tests/test_development_regressiontest_optimica.py

unittest_development_regressiontest:
python3 buildingspy/tests/test_development_regressiontest.py

unittest_development_Validator:
python3 buildingspy/tests/test_development_Validator.py

unittest_examples_dymola:
python3 buildingspy/tests/test_examples_dymola.py

unittest_io_outputfile:
python3 buildingspy/tests/test_io_outputfile.py

unittest_io_postprocess:
python3 buildingspy/tests/test_io_postprocess.py

unittest_simulate_Dymola:
python3 buildingspy/tests/test_simulate_Dymola.py

unittest_simulate_Optimica:
python3 buildingspy/tests/test_simulate_Optimica.py

unittest_simulate_Simulator:
python3 buildingspy/tests/test_simulate_Simulator.py

doctest:
python3 -m doctest \
buildingspy/fmi/*.py \
Expand Down
1 change: 1 addition & 0 deletions buildingspy/examples/dymola/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
:mod:`buildingspy.simulate` that can be used to automate running simulations.
For example, to translate and simulate the model
``Buildings.Controls.Continuous.Examples.PIDHysteresis.mo``
from the Modelica Buildings Library, release 7.0.0,
with controller parameters ``con.eOn = 1`` and ``con.eOn = 5``, use
the following commands:
Expand Down
2 changes: 1 addition & 1 deletion buildingspy/examples/dymola/runSimulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def main():

# Build list of cases to run
li = []
# First model
# First model, from Modelica Buildings Library, v7.0.0
model = 'Buildings.Controls.Continuous.Examples.PIDHysteresis'
s = Simulator(model, 'case1')
s.addParameters({'con.eOn': 0.1})
Expand Down
2 changes: 2 additions & 0 deletions buildingspy/io/outputfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ def get_errors_and_warnings(log_file, simulator):
listWarn.append(temp)
elif lin.find(ERR) >= 0:
listErr.append(lines[index + 1].strip())
elif simulator == "dymola" and lin == " = false\n":
listErr.append("Log file contained the line ' = false'")

ret["warnings"] = listWarn
ret["errors"] = listErr
Expand Down
11 changes: 6 additions & 5 deletions buildingspy/simulate/Dymola.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,8 @@ def simulate(self):
5. Closes the Modelica simulation environment.
This method requires that the directory that contains the executable ``dymola``
is on the system ``PATH`` variable. If it is not found, the function returns with
an error message.
is on the system ``PATH`` variable.
If it is not found, the function raises an exception.
"""
import os
Expand Down Expand Up @@ -304,8 +304,8 @@ def translate(self):
5. Closes the Modelica simulation environment.
This method requires that the directory that contains the executable ``dymola``
is on the system ``PATH`` variable. If it is not found, the function returns with
an error message.
is on the system ``PATH`` variable.
If it is not found, the function raises an exception.
"""
import os
Expand Down Expand Up @@ -403,10 +403,11 @@ def _check_simulation_errors(self, worDir):
else:
for li in ret["errors"]:
self._reporter.writeError(li)
raise IOError
raise Exception(f"Simulation terminated with error. Check {path_to_logfile}.")
else:
em = f"Log file {path_to_logfile} does not exist."
self._reporter.writeError(em)
raise IOError(em)

# Classes that are inherited. These are listed here
# so that they appear in the documentation.
Expand Down
18 changes: 9 additions & 9 deletions buildingspy/simulate/Optimica.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def simulate(self):
Usage: Type
>>> from buildingspy.simulate.Optimica import Simulator
>>> s=Simulator("MyModelicaLibrary.Examples.Constants", packagePath="buildingspy/tests")
>>> s=Simulator("MyModelicaLibrary.Examples.Constants", packagePath="buildingspy/tests/MyModelicaLibrary")
>>> s.simulate() # doctest: +SKIP
This method
Expand All @@ -131,8 +131,8 @@ def simulate(self):
5. Closes the Modelica simulation environment.
This method requires that the directory that contains the executable ``jm_ipython.sh``
is on the system ``PATH`` variable. If it is not found, the function returns with
an error message.
is on the system ``PATH`` variable.
If it is not found, the function raises an exception.
"""
return self._translate_and_simulate(simulate=True)
Expand All @@ -142,7 +142,7 @@ def translate(self):
Usage: Type
>>> from buildingspy.simulate.Optimica import Simulator
>>> s=Simulator("MyModelicaLibrary.Examples.Constants", packagePath="buildingspy/tests")
>>> s=Simulator("MyModelicaLibrary.Examples.Constants", packagePath="buildingspy/tests/MyModelicaLibrary")
>>> s.translate() # doctest: +SKIP
This method
Expand All @@ -153,8 +153,8 @@ def translate(self):
5. Closes the Modelica simulation environment.
This method requires that the directory that contains the executable ``jm_ipython.sh``
is on the system ``PATH`` variable. If it is not found, the function returns with
an error message.
is on the system ``PATH`` variable.
If it is not found, the function raises an exception.
"""
return self._translate_and_simulate(simulate=False)
Expand All @@ -172,8 +172,8 @@ def _translate_and_simulate(self, simulate):
5. Closes the Modelica simulation environment.
This method requires that the directory that contains the executable ``jm_ipython.sh``
is on the system ``PATH`` variable. If it is not found, the function returns with
an error message.
is on the system ``PATH`` variable.
If it is not found, the function raises an exception.
"""
import os
Expand Down Expand Up @@ -246,7 +246,7 @@ def _translate_and_simulate(self, simulate):
except Exception as e: # Catch all possible exceptions
em = f"Simulation failed in '{worDir}'\n Exception: {e}.\n You need to delete the directory manually.\n"
self._reporter.writeError(em)
raise
raise e

def setResultFilter(self, filter):
""" Specifies a list of variables that should be stored in the result file.
Expand Down
24 changes: 11 additions & 13 deletions buildingspy/simulate/base_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,22 +79,18 @@ def __init__(
def setPackagePath(self, packagePath):
""" Set the path specified by ``packagePath``.
:param packagePath: The path where the Modelica package to be loaded is located.
:param packagePath: The path where the Modelica ``package.mo`` file is located.
It first checks whether the path exists and whether it is a directory.
If both conditions are satisfied, the path is set.
It checks whether the file ``package.mo`` exists in the directory ``packagePath``,
and then sets the package path.
Otherwise, a ``ValueError`` is raised.
"""
import os

# Check whether the package Path parameter is correct
if not os.path.exists(packagePath):
msg = "Argument packagePath=%s does not exist." % packagePath
raise ValueError(msg)

if not os.path.isdir(packagePath):
msg = "Argument packagePath=%s must be a directory " % packagePath
msg += "containing a Modelica package."
fil = os.path.join(packagePath, "package.mo")
if not os.path.isfile(fil):
msg = f"Argument packagePath={packagePath} must be a directory containing 'package.mo'. Did not find '{fil}'"
raise ValueError(msg)

# All the checks have been successfully passed
Expand Down Expand Up @@ -373,12 +369,14 @@ def _runSimulation(self, cmd, timeout, directory, env=None):
em += " Make sure it is on the PATH variable of your operating system."
raise RuntimeError(em)

# Add _packagePath to MODELICAPATH. This is for example needed for
# _packagePath is the path that contains the package.mo file.
# Export its parent directory to the MODELICAPATH.
# This is for example needed for
# export USE_DOCKER=true
# python buildingspy/tests/test_simulate_Optimica.py
# Test_simulate_Simulator.test_setResultFilter
osEnv = os.environ.copy() if env is None else env
osEnv = self.prependToModelicaPath(osEnv, self._packagePath)
osEnv = self.prependToModelicaPath(osEnv, os.path.dirname(self._packagePath))

# Run command
try:
Expand Down Expand Up @@ -521,7 +519,7 @@ def _printProgressBar(self, fractionComplete):

@staticmethod
def prependToModelicaPath(env, path):
''' Prepends `path` to the dictonary `env` and returns it.
''' Prepends `path` to the dictionary `env` and returns it.
:param env: A dictionary that may contain the key `MODELICAPATH`.
:param path: The directory to add to the front of `MODELICAPATH`.
Expand Down
28 changes: 15 additions & 13 deletions buildingspy/tests/test_examples_dymola.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,28 @@ def setUp(self):
""" Ensure that environment variables that are needed to run
the tests are set
"""
from git import Repo
import tempfile
import os
import shutil
import requests
import zipfile
from io import BytesIO

self._temDir = tempfile.mkdtemp(prefix='tmp-BuildingsPy-Modelica-Lib-')
self._buiDir = os.path.join(os.getcwd(), "Buildings")

clo_dir = os.path.join(os.getcwd(), "tmp")
zip_file_url = "https://github.com/lbl-srg/modelica-buildings/archive/refs/tags/v7.0.0.zip"

if os.path.exists(clo_dir):
shutil.rmtree(clo_dir)

Repo.clone_from("https://github.com/lbl-srg/modelica-buildings.git",
clo_dir, depth=1)

if os.path.exists(os.path.join(os.getcwd(), "Buildings")):
shutil.rmtree(os.path.join(os.getcwd(), "Buildings"))
shutil.move(os.path.join(os.getcwd(), "tmp", "Buildings"),
self._buiDir)
shutil.rmtree(clo_dir)
r = requests.get(zip_file_url)
# Split URL to get the file name
zip_file = zip_file_url.split('/')[-1]
# Writing the file to the local file system
with open(zip_file, 'wb') as output_file:
output_file.write(r.content)
z = zipfile.ZipFile(BytesIO(r.content))
z.extractall()
shutil.move(os.path.join("modelica-buildings-7.0.0", "Buildings"), "Buildings")
shutil.rmtree("modelica-buildings-7.0.0")

def tearDown(self):
""" Method called after all the tests.
Expand All @@ -52,6 +53,7 @@ def tearDown(self):
import shutil
import os
shutil.rmtree(self._buiDir)
os.remove("v7.0.0.zip")

def test_runSimulation(self):
"""
Expand Down
16 changes: 14 additions & 2 deletions buildingspy/tests/test_simulate_Dymola.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def _simulate(cas):
"""
from buildingspy.simulate.Dymola import Simulator

packagePath = os.path.abspath(os.path.join("buildingspy", "tests"))
packagePath = os.path.abspath(os.path.join("buildingspy", "tests", "MyModelicaLibrary"))
s = Simulator(cas['model'], outputDirectory=f"out-{cas['tol']}", packagePath=packagePath)
s.setTolerance(cas['tol'])
s.simulate()
Expand All @@ -31,7 +31,7 @@ def setUp(self):
that contains a Modelica package.
"""
self._packagePath = os.path.abspath(os.path.join(
"buildingspy", "tests"))
"buildingspy", "tests", "MyModelicaLibrary"))

def test_Constructor(self):
"""
Expand Down Expand Up @@ -198,6 +198,18 @@ def test_setBooleanParameterValues(self):
# Delete output files
s.deleteOutputFiles()

def test_raisesAssertionIfWrongDataType(self):
"""
Tests the :mod:`buildingspy.simulate.Dymola.simulate`
function to make sure it raises an assertion if a model fails to translate.
"""
model = "MyModelicaLibrary.Examples.BooleanParameters"

s = Simulator(model, packagePath=self._packagePath)
s.addParameters({'p1': 123}) # p1 is a boolean parameter. This will fail the model.
with self.assertRaises(Exception):
s.simulate()

def test_timeout(self, timeout=3):
model = 'MyModelicaLibrary.MyModelTimeOut'
s = Simulator(
Expand Down
Loading

0 comments on commit 4291001

Please sign in to comment.