diff --git a/teaser/data/input/inputdata/weatherdata/DEU_BW_Mannheim_107290_TRY2010_12_Jahr_BBSR.mos b/teaser/data/input/inputdata/weatherdata/DEU_BW_Mannheim_107290_TRY2010_12_Jahr_BBSR.mos index 75be83867..ecd954879 100644 --- a/teaser/data/input/inputdata/weatherdata/DEU_BW_Mannheim_107290_TRY2010_12_Jahr_BBSR.mos +++ b/teaser/data/input/inputdata/weatherdata/DEU_BW_Mannheim_107290_TRY2010_12_Jahr_BBSR.mos @@ -5,7 +5,7 @@ double tab1(8760,30) #TYPICAL/EXTREME PERIODS,6,Summer - Week Nearest Max Temperature For Period,Extreme,7/13,7/19,Summer - Week Nearest Average Temperature For Period,Typical,8/ 3,8/ 9,Winter - Week Nearest Min Temperature For Period,Extreme,12/ 8,12/14,Winter - Week Nearest Average Temperature For Period,Typical,12/15,12/21,Autumn - Week Nearest Average Temperature For Period,Typical,10/20,10/26,Spring - Week Nearest Average Temperature For Period,Typical,4/19,4/25 #GROUND TEMPERATURES,3,.5,,,,3.79,3.08,4.35,6.34,11.55,15.57,18.29,19.11,17.71,14.58,10.41,6.54,2,,,,6.44,5.16,5.42,6.48,9.96,13.11,15.64,16.99,16.70,14.93,12.06,9.01,4,,,,8.62,7.31,7.02,7.42,9.39,11.52,13.46,14.82,15.14,14.37,12.68,10.62 #HOLIDAYS/DAYLIGHT SAVINGS,No,0,0,0 -#COMMENTS 1,"Custom/User Format -- WMO#107290; Bundesinstitut für Bau-, Stadt- und Raumforschung im Bundesamt für Bauwesen und Raumordnung. 1) DWD are the original author of the DTRY dataset on which these EPWs are based. 2) The EPWs were converted by Climate.OneBuilding.Org. Neither the DWD nor the BBSR were responsible for the conversion, and they disclaim all liability associated with the use of the converted DTRY EPW data set." +#COMMENTS 1,"Custom/User Format -- WMO#107290; Bundesinstitut fuer Bau-, Stadt- und Raumforschung im Bundesamt fuer Bauwesen und Raumordnung. 1) DWD are the original author of the DTRY dataset on which these EPWs are based. 2) The EPWs were converted by Climate.OneBuilding.Org. Neither the DWD nor the BBSR were responsible for the conversion, and they disclaim all liability associated with the use of the converted DTRY EPW data set." #COMMENTS 2,"Downloaded from Climate.OneBuilding.Org -- Ground temps represent undisturbed earth temperatures - calculated from this weather data." #DATA PERIODS,1,1,Data,Monday, 1/ 1,12/31 #C1 Time in seconds. Beginning of a year is 0s. diff --git a/teaser/data/output/aixlib_output.py b/teaser/data/output/aixlib_output.py index b511d530b..a05f65b4d 100644 --- a/teaser/data/output/aixlib_output.py +++ b/teaser/data/output/aixlib_output.py @@ -82,6 +82,10 @@ def export_multizone(buildings, prj, path=None): filename=utilities.get_full_path( "data/output/modelicatemplate/AixLib/AixLib_Multizone"), lookup=lookup) + test_script_template = Template( + filename=utilities.get_full_path( + "data/output/modelicatemplate/modelica_test_script"), + lookup=lookup) uses = [ 'Modelica(version="' + prj.modelica_info.version + '")', @@ -146,6 +150,17 @@ def export_multizone(buildings, prj, path=None): modelica_info=bldg.parent.modelica_info)) out_file.close() + dir_resources = os.path.join(path, "Resources") + if not os.path.exists(dir_resources): + os.mkdir(dir_resources) + dir_scripts = os.path.join(dir_resources, "Scripts") + if not os.path.exists(dir_scripts): + os.mkdir(dir_scripts) + dir_dymola = os.path.join(dir_scripts, "Dymola") + if not os.path.exists(dir_dymola): + os.mkdir(dir_dymola) + _help_test_script(bldg, dir_dymola, test_script_template) + zone_path = os.path.join(bldg_path, bldg.name + "_DataBase") for zone in bldg.thermal_zones: @@ -173,10 +188,75 @@ def export_multizone(buildings, prj, path=None): addition=bldg.name + "_", extra=None) + _copy_script_unit_tests(os.path.join(dir_scripts, "runUnitTests.py")) + _copy_reference_results(dir_resources, prj) + print("Exports can be found here:") print(path) +def _copy_reference_results(dir_resources, prj): + """Copy reference results to modelica output. + + Parameters + ---------- + dir_resources : str + Resources directory of the modelica output + prj : teaser.project.Project + Project to be exported + """ + + if prj.dir_reference_results is not None: + dir_ref_out = os.path.join(dir_resources, "ReferenceResults") + if not os.path.exists(dir_ref_out): + os.mkdir(dir_ref_out) + dir_ref_out_dymola = os.path.join(dir_ref_out, "Dymola") + if not os.path.exists(dir_ref_out_dymola): + os.mkdir(dir_ref_out_dymola) + for filename in os.listdir(prj.dir_reference_results): + if filename.endswith(".txt"): + shutil.copy2( + os.path.join(prj.dir_reference_results, filename), + os.path.join(dir_ref_out_dymola, filename) + ) + + +def _help_test_script(bldg, dir_dymola, test_script_template): + """Create a test script for regression testing with BuildingsPy + + Parameters + ---------- + bldg : teaser.logic.buildingobjects.building.Building + Building for which test script is created + dir_dymola : str + Output directory for Dymola scripts + test_script_template : mako.template.Template + Template for the test script + + Returns + ------- + dir_scripts : str + Path to the scripts directory + """ + + dir_building = os.path.join(dir_dymola, bldg.name) + if not os.path.exists(dir_building): + os.mkdir(dir_building) + out_file = open(utilities.get_full_path + (os.path.join(dir_building, bldg.name + ".mos")), 'w') + names_variables = [] + for i, zone in enumerate(bldg.thermal_zones): + names_variables.append(f"multizone.PHeater[{i+1}]") + names_variables.append(f"multizone.PCooler[{i+1}]") + names_variables.append(f"multizone.TAir[{i+1}]") + out_file.write(test_script_template.render_unicode( + project=bldg.parent, + bldg=bldg, + stop_time=3600 * 24 * 365, + names_variables=names_variables, + )) + out_file.close() + def _help_package(path, name, uses=None, within=None): """creates a package.mo file @@ -248,3 +328,16 @@ def _copy_weather_data(source_path, destination_path): """ shutil.copy2(source_path, destination_path) + + +def _copy_script_unit_tests(destination_path): + """Copies the script to run the unit tests. + + Parameters + ---------- + destination_path : str + path of where the weather file should be placed + """ + + source_path = utilities.get_full_path("data/output/runUnitTests.py") + shutil.copy2(source_path, destination_path) diff --git a/teaser/data/output/modelicatemplate/AixLib/AixLib_Multizone b/teaser/data/output/modelicatemplate/AixLib/AixLib_Multizone index 8ab137a4d..92809a6bd 100644 --- a/teaser/data/output/modelicatemplate/AixLib/AixLib_Multizone +++ b/teaser/data/output/modelicatemplate/AixLib/AixLib_Multizone @@ -140,9 +140,13 @@ equation StartTime=${str(zone.parent.parent.modelica_info.start_time)}, StopTime=${str(zone.parent.parent.modelica_info.stop_time)}, Interval=${modelica_info.interval_output}, + Tolerance=0.0001, __Dymola_Algorithm="${modelica_info.current_solver}"), __Dymola_experimentSetupOutput(equidistant=${get_true_false(modelica_info.equidistant_output)}, events=${get_true_false(modelica_info.results_at_events)}), + __Dymola_Commands(file= + "Resources/Scripts/Dymola/${bldg.name}/${bldg.name}.mos" + "Simulate and Plot"), Icon(coordinateSystem(preserveAspectRatio=false, extent={{-100,-100},{100,100}}), graphics={ Line(points={{80,-82}}, color={28,108,200}), diff --git a/teaser/data/output/modelicatemplate/modelica_test_script b/teaser/data/output/modelicatemplate/modelica_test_script new file mode 100644 index 000000000..2c309e153 --- /dev/null +++ b/teaser/data/output/modelicatemplate/modelica_test_script @@ -0,0 +1,5 @@ +simulateModel("${project.name}.${bldg.name}.${bldg.name}", method="Dassl", tolerance=0.0001, stopTime=${stop_time}, resultFile="${bldg.name}"); +% for name_variable in names_variables: +createPlot(id=${loop.index + 1}, y={"${name_variable}"}); +% endfor + diff --git a/teaser/data/output/runUnitTests.py b/teaser/data/output/runUnitTests.py new file mode 100755 index 000000000..2124dd863 --- /dev/null +++ b/teaser/data/output/runUnitTests.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +####################################################### +# Script that runs all unit tests or, optionally, +# only checks the html syntax or the validity of +# the simulation parameters of the models +# +# To run the unit tests, this script +# - creates temporary directories for each processor, +# - copies the library directory into these +# temporary directories, +# - creates run scripts that run all unit tests, +# - runs these unit tests, +# - collects the dymola log files from each process, +# - writes the combined log file 'unitTests.log' +# in the current directory, +# - checks whether all unit tests run successfully, +# and produced the same results as the reference +# results, and +# - exits with the message +# 'Unit tests completed successfully.' or with +# an error message. +# +# If no errors occurred during the unit tests, then +# this script returns 0. Otherwise, it returns a +# non-zero exit value. +# +# MWetter@lbl.gov 2011-02-23 +# TSNouidui@lbl.gov 2017-04-11 +####################################################### +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + + +def _validate_experiment_setup(path): + import buildingspy.development.validator as v + + val = v.Validator() + retVal = val.validateExperimentSetup(path) + + +def _validate_html(path): + import buildingspy.development.validator as v + + val = v.Validator() + errMsg = val.validateHTMLInPackage(path) + n_msg = len(errMsg) + for i in range(n_msg): + if i == 0: + print( + "The following malformed html syntax has been found:\n{}".format(errMsg[i])) + else: + print(errMsg[i]) + + if n_msg == 0: + return 0 + else: + return 1 + + +def _setEnvironmentVariables(var, value): + ''' Add to the environment variable `var` the value `value` + ''' + import os + import platform + if var in os.environ: + if platform.system() == "Windows": + os.environ[var] = value + ";" + os.environ[var] + else: + os.environ[var] = value + ":" + os.environ[var] + else: + os.environ[var] = value + + +def _runUnitTests(batch, tool, package, path, n_pro, show_gui, skip_verification): + import buildingspy.development.regressiontest as u + + ut = u.Tester(tool=tool, skip_verification=skip_verification) + ut.batchMode(batch) + ut.setLibraryRoot(path) + if package is not None: + ut.setSinglePackage(package) + ut.setNumberOfThreads(n_pro) + ut.pedanticModelica(False) + ut.showGUI(show_gui) + # Below are some option that may occassionally be used. + # These are currently not exposed as command line arguments. +# ut.setNumberOfThreads(1) +# ut.deleteTemporaryDirectories(False) +# ut.useExistingResults(['/tmp/tmp-Buildings-0-fagmeZ']) + +# ut.writeOpenModelicaResultDictionary() + # Run the regression tests + + retVal = ut.run() + + # Display HTML report if not run in batch mode. + # (For buildingspy.__version__ >= 2) + if not batch: + try: + if not skip_verification: + ut.report() + except AttributeError: + pass + + return retVal + + +def _runOpenModelicaUnitTests(): + import buildingspy.development.regressiontest as u + ut = u.Tester() + ut.batchMode(batch) + ut.test_OpenModelica(cmpl=True, simulate=True, + packages=['Examples'], number=-1) + + +if __name__ == '__main__': + import multiprocessing + import platform + import argparse + import os + import sys + + # Configure the argument parser + parser = argparse.ArgumentParser( + description='Run the unit tests or the html validation only.') + unit_test_group = parser.add_argument_group("arguments to run unit tests") + + unit_test_group.add_argument("-b", "--batch", + action="store_true", + help="Run in batch mode without user interaction") + unit_test_group.add_argument('-t', "--tool", + metavar="dymola", + default="dymola", + help="Tool for the regression tests. Set to dymola or jmodelica") + unit_test_group.add_argument('-s', "--single-package", + metavar="Modelica.Package", + help="Test only the Modelica package Modelica.Package") + unit_test_group.add_argument("-p", "--path", + default=".", + help="Path where top-level package.mo of the library is located") + unit_test_group.add_argument("-n", "--number-of-processors", + type=int, + default=multiprocessing.cpu_count(), + help='Maximum number of processors to be used') + unit_test_group.add_argument("--show-gui", + help='Show the GUI of the simulator', + action="store_true") + unit_test_group.add_argument("--skip-verification", + help='If specified, do not verify simulation results against reference points', + action="store_true") + + html_group = parser.add_argument_group( + "arguments to check html syntax only") + html_group.add_argument("--validate-html-only", + action="store_true") + + experiment_setup_group = parser.add_argument_group( + "arguments to check validity of .mos and .mo experiment setup only") + experiment_setup_group.add_argument("--validate-experiment-setup", + action="store_true") + + # Set environment variables + if platform.system() == "Windows": + _setEnvironmentVariables("PATH", + os.path.join(os.path.abspath('.'), + "Resources", "Library", "win32")) + else: + # For https://github.com/lbl-srg/modelica-buildings/issues/559, we add + # 32 and 64 bit resources to run the Utilities.IO.Python27 regression tests. + _setEnvironmentVariables("LD_LIBRARY_PATH", + os.path.join(os.path.abspath('.'), + "Resources", "Library", "linux32") + ":" + + os.path.join(os.path.abspath('.'), + "Resources", "Library", "linux64")) + + # The path to buildingspy must be added to sys.path to work on Linux. + # If only added to os.environ, the Python interpreter won't find buildingspy + sys.path.append(os.path.join( + os.path.abspath('.'), "..", "..", "BuildingsPy")) + + # Parse the arguments + args = parser.parse_args() + + if args.validate_html_only: + # Validate the html syntax only, and then exit + ret_val = _validate_html(args.path) + exit(ret_val) + + if args.validate_experiment_setup: + # Match the mos file parameters with the mo files only, and then exit + ret_val = _validate_experiment_setup(args.path) + exit(ret_val) + + if args.single_package: + single_package = args.single_package + else: + single_package = None + + retVal = _runUnitTests(batch=args.batch, + tool=args.tool, + package=single_package, + path=args.path, + n_pro=args.number_of_processors, + show_gui=args.show_gui, + skip_verification=args.skip_verification + ) + exit(retVal) + +# _runOpenModelicaUnitTests() diff --git a/teaser/project.py b/teaser/project.py index eeadd7cd6..3074302de 100644 --- a/teaser/project.py +++ b/teaser/project.py @@ -86,6 +86,10 @@ class Project(object): IBPSA) used_library_calc : str used library (AixLib and IBPSA are supported) + dir_reference_results : str + Path to reference results in BuildingsPy format. If not None, the results + will be copied into the model output directories so that the exported + models can be regression tested against these results with BuildingsPy. """ def __init__(self, load_data=False): @@ -117,6 +121,8 @@ def __init__(self, load_data=False): else: self.data = None + self.dir_reference_results = None + @staticmethod def instantiate_data_class(): """Initialization of DataClass