From db725f6dd3f7bf8f2ad20f6a173c8768b18a53c9 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Mon, 30 Dec 2019 12:00:23 -0700 Subject: [PATCH 01/62] add github templates --- .github/ISSUE_TEMPLATE.md | 15 +++++++++++++++ .github/PULL_REQUEST_TEMPLATE.md | 5 +++++ 2 files changed, 20 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..6f7827a4d --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,15 @@ + + +### Expected Behavior + +### Actual Behavior + +### Steps to Reproduce + +### Version Information + + + +Modelica Buildings Library: `7.0.0 or GithubBranch/SHA (e.g., issue1442_loadCoupling/SHA_XYZ)` +TEASER: `URBANopt/0.6.9` +Python: `3.6.5` diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..e47cd976a --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,5 @@ +#### Any background context you want to provide? +#### What does this PR accomplish? +#### How should this be manually tested? +#### What are the relevant tickets? +#### Screenshots (if appropriate) From d125894c0aa8f2c9ffb64d8215490de7f789cb99 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Mon, 17 Feb 2020 14:12:42 -0700 Subject: [PATCH 02/62] update to latest teaser --- CHANGELOG.rst | 6 ++-- .../model_connectors/teaser.py | 2 +- requirements.txt | 4 +-- tests/test_translator.py | 29 +++++++++---------- 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b07e93320..f697415d6 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,11 +1,11 @@ Change Log ========== -Version 0.1 ------------ +Version 0.1 (Unreleased) +------------------------ This is the initial release of the package and includes the following functionality: -* Create a ROM/RC model using Modelica 3.2.x, Modelica Buildings Library 7.0 and TEASER 0.6.8 +* Create a ROM/RC model using Modelica 3.2.x, Modelica Buildings Library 7.0 and TEASER 0.7.2 (prerelease) * Create a Spawn-based models which loads an IDF file diff --git a/geojson_modelica_translator/model_connectors/teaser.py b/geojson_modelica_translator/model_connectors/teaser.py index 01d226de0..580cf5e5e 100644 --- a/geojson_modelica_translator/model_connectors/teaser.py +++ b/geojson_modelica_translator/model_connectors/teaser.py @@ -150,7 +150,7 @@ def post_process(self, project_name, root_building_dir, building_names, keep_ori package = PackageParser(os.path.join(root_building_dir, f'B{b}')) # move the internal gains files to a new resources folder - mat_files = glob.glob(os.path.join(root_building_dir, f'B{b}/*.mat')) + mat_files = glob.glob(os.path.join(root_building_dir, f'B{b}/*.txt')) for f in mat_files: new_file_name = os.path.basename(f).replace(f'B{b}', '') os.rename(f, f'{b_modelica_path.resources_dir}/{new_file_name}') diff --git a/requirements.txt b/requirements.txt index c9d492e4e..c3e93860b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,14 +7,14 @@ jsonschema==3.0.1 requests==2.22.0 # dependent projects -## Require the MBL. Note this is a large dependency, but needed to assemble the models for running BuildingsPy==1.7.0 +# There may be a need to require the MBL. Note this is a large dependency, but needed to assemble the models for running #-e git+https://github.com/lbl-srg/modelica-buildings.git@issue1442_loadCoupling#egg=buildings # Use TEASER from URBANopt for now to support MBL 7.0 #teaser==0.6.8 #-e file:../TEASER-urbanopt#egg=teaser --e git+https://github.com/urbanopt/TEASER.git@mbl-7.0#egg=teaser +-e git+https://github.com/RWTH-EBC/TEASER.git@0.7.2#egg=teaser # Test and documentation nose==1.3.7 diff --git a/tests/test_translator.py b/tests/test_translator.py index 592f2d636..b9f88cd06 100644 --- a/tests/test_translator.py +++ b/tests/test_translator.py @@ -72,17 +72,16 @@ def test_to_modelica_defaults(self): path_checks = [f'{os.path.sep.join(r)}.mo' for r in itertools.product(building_paths, model_names)] for p in path_checks: - # print(p) - self.assertTrue(os.path.exists(p)) + self.assertTrue(os.path.exists(p), f'Path not found {p}') - # look for resource files + # go through the generated buildings and ensure that the resources are created resource_names = ['InternalGains_Floor', 'InternalGains_ICT', 'InternalGains_Meeting', 'InternalGains_Office', 'InternalGains_Restroom', 'InternalGains_Storage'] - resource_paths = [os.path.join(gj.loads_path.files_dir, 'Resources', 'Data', b.dirname) for b in gj.buildings] - path_checks = [f'{os.path.sep.join(r)}.mat' for r in itertools.product(resource_paths, resource_names)] - - for p in path_checks: - self.assertTrue(os.path.exists(p)) + for b in gj.buildings: + for resource_name in resource_names: + # TEASER 0.7.2 used .txt for schedule files + path = os.path.join(gj.loads_path.files_dir, 'Resources', 'Data', b.dirname, f'{resource_name}.txt') + self.assertTrue(os.path.exists(path), f'Path not found: {path}') def test_to_modelica_rc_order_4(self): self.results_path = os.path.abspath('tests/output/rc_order_4') @@ -115,17 +114,15 @@ def test_to_modelica_rc_order_4(self): path_checks = [f'{os.path.sep.join(r)}.mo' for r in itertools.product(building_paths, model_names)] for p in path_checks: - # print(p) - self.assertTrue(os.path.exists(p)) + self.assertTrue(os.path.exists(p), f'Path not found: {p}') - # look for resource files resource_names = ['InternalGains_Floor', 'InternalGains_ICT', 'InternalGains_Meeting', 'InternalGains_Office', 'InternalGains_Restroom', 'InternalGains_Storage'] - resource_paths = [os.path.join(gj.loads_path.files_dir, 'Resources', 'Data', b.dirname) for b in gj.buildings] - path_checks = [f'{os.path.sep.join(r)}.mat' for r in itertools.product(resource_paths, resource_names)] - - for p in path_checks: - self.assertTrue(os.path.exists(p)) + for b in gj.buildings: + for resource_name in resource_names: + # TEASER 0.7.2 used .txt for schedule files + path = os.path.join(gj.loads_path.files_dir, 'Resources', 'Data', b.dirname, f'{resource_name}.txt') + self.assertTrue(os.path.exists(path), f'Path not found: {path}') if __name__ == '__main__': From 90805ccd450ff7026424254a351c6481132c9439 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Tue, 18 Feb 2020 10:56:30 -0700 Subject: [PATCH 03/62] add remove_connect method and fix teaser result --- .../model_connectors/teaser.py | 5 +++- .../modelica/input_parser.py | 12 ++++++++ tests/modelica/test_input_parser.py | 29 +++++++++++++++++-- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/geojson_modelica_translator/model_connectors/teaser.py b/geojson_modelica_translator/model_connectors/teaser.py index 580cf5e5e..a0a615938 100644 --- a/geojson_modelica_translator/model_connectors/teaser.py +++ b/geojson_modelica_translator/model_connectors/teaser.py @@ -199,8 +199,11 @@ def post_process(self, project_name, root_building_dir, building_names, keep_ori ] mofile.add_model_object('Buildings.Controls.OBC.CDL.Interfaces.RealOutput', instance, data) - # update the weaBus connectors + # All existing weaDat.weaBus connections need to be updated to simply weaBus mofile.replace_connect_string('weaDat.weaBus', None, 'weaBus', None, True) + # Now remove the redundant weaBus -> weaBus connection + mofile.remove_connect_string('weaDat.weaBus', 'weaBus') + # add new port connections if self.system_parameters.get_param('buildings.default.load_model_parameters.rc.order', default=2) == 1: # noqa diff --git a/geojson_modelica_translator/modelica/input_parser.py b/geojson_modelica_translator/modelica/input_parser.py index 5de861a22..965d8cb1a 100644 --- a/geojson_modelica_translator/modelica/input_parser.py +++ b/geojson_modelica_translator/modelica/input_parser.py @@ -346,6 +346,18 @@ def replace_connect_string(self, a, b, new_a, new_b, replace_all=False): else: index, c = self.find_connect(a, b) + def remove_connect_string(self, a, b): + """ + Remove a connection string that matches the a, b. + + :param a: string, existing port a + :param b: string, existing port b + """ + # find the connection that matches a, b + index, c = self.find_connect(a, b) + if index: + del self.connections[index] + def serialize(self): """ Serialize the modelica object to a string with line feeds diff --git a/tests/modelica/test_input_parser.py b/tests/modelica/test_input_parser.py index 77c4c95ca..5988db5ce 100644 --- a/tests/modelica/test_input_parser.py +++ b/tests/modelica/test_input_parser.py @@ -93,7 +93,8 @@ def test_rename_filename(self): f1.replace_model_string( 'Modelica.Blocks.Sources.CombiTimeTable', 'internalGains', - 'modelica://Project/B5a6b99ec37f4de7f94020090/B5a6b99ec37f4de7f94020090_Models/InternalGains_B5a6b99ec37f4de7f94020090Floor.mat', # noqa + 'modelica://Project/B5a6b99ec37f4de7f94020090/B5a6b99ec37f4de7f94020090_Models/InternalGains_B5a6b99ec37f4de7f94020090Floor.mat', + # noqa 'modelica://a/new/path.mat' ) f1.save_as(new_filename) @@ -109,7 +110,8 @@ def test_add_model_obj(self): new_filename = os.path.abspath('tests/modelica/output/test_1_output_5.mo') f1 = InputParser(filename) data = [ - 'annotation (Placement(transformation(extent={{-10,90},{10,110}}), iconTransformation(extent={{-10,90},{10,110}})));' # noqa + 'annotation (Placement(transformation(extent={{-10,90},{10,110}}), iconTransformation(extent={{-10,90},{10,110}})));' + # noqa ] f1.add_model_object('Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a', 'port_a', data) f1.save_as(new_filename) @@ -143,6 +145,29 @@ def test_rename_connection(self): f1.replace_connect_string('weaDat.weaBus', None, 'weaBus', None, True) f1.save_as(new_filename) + f2 = InputParser(new_filename) + self.assertFalse(filecmp.cmp(filename, new_filename)) + index, c = f2.find_connect('weaBus', 'weaBus') + # there should exist the new connection + self.assertGreaterEqual(index, 0) + + # the old one should not exist + index, c = f2.find_connect('weaDat.weaBus', 'weaBus') + self.assertIsNone(index) + + def test_remove_connection(self): + filename = os.path.abspath('tests/modelica/data/test_1.mo') + new_filename = os.path.abspath('tests/modelica/output/test_1_output_8.mo') + f1 = InputParser(filename) + f1.remove_connect_string('weaDat.weaBus', 'weaBus') + f1.save_as(new_filename) + + f2 = InputParser(new_filename) + self.assertFalse(filecmp.cmp(filename, new_filename)) + index, c = f2.find_connect('weaDat.weaBus', 'weaBus') + # the connection should no longer exist + self.assertIsNone(index) + def update_connection(self): pass From 90f79c822fb81e50cb478fd1b98cfd5a1e6ecfc9 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Tue, 18 Feb 2020 10:59:36 -0700 Subject: [PATCH 04/62] remove the correct weaBus --- geojson_modelica_translator/model_connectors/teaser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geojson_modelica_translator/model_connectors/teaser.py b/geojson_modelica_translator/model_connectors/teaser.py index a0a615938..b70512bd9 100644 --- a/geojson_modelica_translator/model_connectors/teaser.py +++ b/geojson_modelica_translator/model_connectors/teaser.py @@ -202,7 +202,7 @@ def post_process(self, project_name, root_building_dir, building_names, keep_ori # All existing weaDat.weaBus connections need to be updated to simply weaBus mofile.replace_connect_string('weaDat.weaBus', None, 'weaBus', None, True) # Now remove the redundant weaBus -> weaBus connection - mofile.remove_connect_string('weaDat.weaBus', 'weaBus') + mofile.remove_connect_string('weaBus', 'weaBus') # add new port connections From b381d30c910f97d77950321ebe330e4dd60f5481 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Tue, 18 Feb 2020 11:07:45 -0700 Subject: [PATCH 05/62] flake8 --- geojson_modelica_translator/model_connectors/teaser.py | 1 - tests/modelica/test_input_parser.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/geojson_modelica_translator/model_connectors/teaser.py b/geojson_modelica_translator/model_connectors/teaser.py index b70512bd9..2efed0c74 100644 --- a/geojson_modelica_translator/model_connectors/teaser.py +++ b/geojson_modelica_translator/model_connectors/teaser.py @@ -204,7 +204,6 @@ def post_process(self, project_name, root_building_dir, building_names, keep_ori # Now remove the redundant weaBus -> weaBus connection mofile.remove_connect_string('weaBus', 'weaBus') - # add new port connections if self.system_parameters.get_param('buildings.default.load_model_parameters.rc.order', default=2) == 1: # noqa data = 'annotation (Line(points={{0,100},{96,100},{96,20},{92,20}}, color={191,0,0}))' diff --git a/tests/modelica/test_input_parser.py b/tests/modelica/test_input_parser.py index 5988db5ce..0e0a0e1ea 100644 --- a/tests/modelica/test_input_parser.py +++ b/tests/modelica/test_input_parser.py @@ -93,7 +93,7 @@ def test_rename_filename(self): f1.replace_model_string( 'Modelica.Blocks.Sources.CombiTimeTable', 'internalGains', - 'modelica://Project/B5a6b99ec37f4de7f94020090/B5a6b99ec37f4de7f94020090_Models/InternalGains_B5a6b99ec37f4de7f94020090Floor.mat', + 'modelica://Project/B5a6b99ec37f4de7f94020090/B5a6b99ec37f4de7f94020090_Models/InternalGains_B5a6b99ec37f4de7f94020090Floor.mat', # noqa # noqa 'modelica://a/new/path.mat' ) @@ -110,7 +110,7 @@ def test_add_model_obj(self): new_filename = os.path.abspath('tests/modelica/output/test_1_output_5.mo') f1 = InputParser(filename) data = [ - 'annotation (Placement(transformation(extent={{-10,90},{10,110}}), iconTransformation(extent={{-10,90},{10,110}})));' + 'annotation (Placement(transformation(extent={{-10,90},{10,110}}), iconTransformation(extent={{-10,90},{10,110}})));' # noqa # noqa ] f1.add_model_object('Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a', 'port_a', data) From 03ec4c09b4ef30d3dd9a7c5e9e3feab2caf4e8f2 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Tue, 18 Feb 2020 14:32:58 -0700 Subject: [PATCH 06/62] update dependencies --- requirements.txt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/requirements.txt b/requirements.txt index c3e93860b..85986784c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,12 +2,12 @@ # Remember to also add them in setup.py # core libraries -geojson==2.4.1 +geojson==2.5.0 jsonschema==3.0.1 requests==2.22.0 # dependent projects -BuildingsPy==1.7.0 +BuildingsPy==2.0.0 # There may be a need to require the MBL. Note this is a large dependency, but needed to assemble the models for running #-e git+https://github.com/lbl-srg/modelica-buildings.git@issue1442_loadCoupling#egg=buildings @@ -18,14 +18,14 @@ BuildingsPy==1.7.0 # Test and documentation nose==1.3.7 -sphinx==2.1.2 +sphinx==2.4.2 sphinx_rtd_theme==0.4.3 -pytest==5.0.1 -pytest-cov==2.7.1 -python-coveralls==2.9.2 -autopep8==1.4.4 -flake8==3.7.8 -tox==3.13.2 +pytest==5.3.5 +pytest-cov==2.8.1 +python-coveralls==2.9.3 +autopep8==1.5 +flake8==3.7.9 +tox==3.14.5 # debugging and testing, not used at the moment in the main portion of the code (i.e. not needed in setup.cfg) scipy From fbfacb9fc0c22ca859e5eade95478b4f6e038e57 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Thu, 20 Feb 2020 17:02:18 -0700 Subject: [PATCH 07/62] use teaser from pypi --- requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 85986784c..3100e8dbe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,10 +11,10 @@ BuildingsPy==2.0.0 # There may be a need to require the MBL. Note this is a large dependency, but needed to assemble the models for running #-e git+https://github.com/lbl-srg/modelica-buildings.git@issue1442_loadCoupling#egg=buildings -# Use TEASER from URBANopt for now to support MBL 7.0 -#teaser==0.6.8 +# Use the core teaser library. +teaser==0.7.2 #-e file:../TEASER-urbanopt#egg=teaser --e git+https://github.com/RWTH-EBC/TEASER.git@0.7.2#egg=teaser +#-e git+https://github.com/RWTH-EBC/TEASER.git@0.7.2#egg=teaser # Test and documentation nose==1.3.7 From 15302438ac92a18ee9c922b0d69719a6a33fb69c Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Thu, 20 Feb 2020 17:12:30 -0700 Subject: [PATCH 08/62] do not search src in tests --- requirements.txt | 2 +- setup.cfg | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 3100e8dbe..2dcadfded 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ BuildingsPy==2.0.0 # There may be a need to require the MBL. Note this is a large dependency, but needed to assemble the models for running #-e git+https://github.com/lbl-srg/modelica-buildings.git@issue1442_loadCoupling#egg=buildings -# Use the core teaser library. +# Use the core teaser library. Note: if using the github checkout, then it will be saved in the ./src directory if python is system python. teaser==0.7.2 #-e file:../TEASER-urbanopt#egg=teaser #-e git+https://github.com/RWTH-EBC/TEASER.git@0.7.2#egg=teaser diff --git a/setup.cfg b/setup.cfg index 6ee70d475..45e8f5d9f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,6 +5,12 @@ addopts = --cov geojson_modelica_translator --cov-report term-missing --verbose -s +norecursedirs = + dist + build + .tox + src +testpaths = tests [flake8] # Some sane defaults for the code style checker flake8 @@ -27,3 +33,4 @@ detailed-errors=1 nologcapture=1 nocapture=1 logging-level=INFO + From a8e3b382cfc0d1caca86626b7fd713472bdf3cd7 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Wed, 4 Mar 2020 09:57:42 -0700 Subject: [PATCH 09/62] update copyrights --- LICENSE | 2 +- geojson_modelica_translator/cli.py | 2 +- geojson_modelica_translator/geojson/schemas.py | 2 +- geojson_modelica_translator/geojson/urbanopt_geojson.py | 2 +- geojson_modelica_translator/geojson_modelica_translator.py | 3 ++- geojson_modelica_translator/model_connectors/base.py | 2 +- geojson_modelica_translator/model_connectors/spawn.py | 6 +++--- geojson_modelica_translator/model_connectors/teaser.py | 2 +- geojson_modelica_translator/modelica/input_parser.py | 2 +- .../system_parameters/system_parameters.py | 2 +- geojson_modelica_translator/utils.py | 2 +- management/update_licenses.py | 4 ++-- management/update_schemas.py | 2 +- setup.py | 2 +- tests/context.py | 2 +- tests/geojson/test_geojson.py | 2 +- tests/geojson/test_schemas.py | 2 +- tests/model_connectors/test_spawn.py | 2 +- tests/modelica/test_input_parser.py | 2 +- tests/system_parameters/test_system_parameters.py | 2 +- tests/test_translator.py | 2 +- tests/test_utils.py | 2 +- 22 files changed, 26 insertions(+), 25 deletions(-) diff --git a/LICENSE b/LICENSE index 22dc67fc2..1f9f84834 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -URBANopt, Copyright (c) 2019, Alliance for Sustainable Energy, LLC, and other contributors. +URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted diff --git a/geojson_modelica_translator/cli.py b/geojson_modelica_translator/cli.py index 12b8139d3..d56724ea8 100644 --- a/geojson_modelica_translator/cli.py +++ b/geojson_modelica_translator/cli.py @@ -1,6 +1,6 @@ """ **************************************************************************************************** -:copyright (c) 2019 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. diff --git a/geojson_modelica_translator/geojson/schemas.py b/geojson_modelica_translator/geojson/schemas.py index 9f832c0d1..764fcc482 100644 --- a/geojson_modelica_translator/geojson/schemas.py +++ b/geojson_modelica_translator/geojson/schemas.py @@ -1,6 +1,6 @@ """ **************************************************************************************************** -:copyright (c) 2019 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. diff --git a/geojson_modelica_translator/geojson/urbanopt_geojson.py b/geojson_modelica_translator/geojson/urbanopt_geojson.py index 0fa312d41..13ae70cc4 100644 --- a/geojson_modelica_translator/geojson/urbanopt_geojson.py +++ b/geojson_modelica_translator/geojson/urbanopt_geojson.py @@ -1,6 +1,6 @@ """ **************************************************************************************************** -:copyright (c) 2019 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. diff --git a/geojson_modelica_translator/geojson_modelica_translator.py b/geojson_modelica_translator/geojson_modelica_translator.py index dc86f882e..e9b948058 100644 --- a/geojson_modelica_translator/geojson_modelica_translator.py +++ b/geojson_modelica_translator/geojson_modelica_translator.py @@ -1,6 +1,6 @@ """ **************************************************************************************************** -:copyright (c) 2019 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. @@ -113,6 +113,7 @@ def to_modelica(self, project_name, save_dir, model_connector_str='TeaserConnect # TODO: Handle other connectors -- create map based on model_connector_str import geojson_modelica_translator.model_connectors.teaser + import geojson_modelica_translator.model_connectors.spawn mc_klass = getattr(geojson_modelica_translator.model_connectors.teaser, model_connector_str) model_connector = mc_klass(self.system_parameters) diff --git a/geojson_modelica_translator/model_connectors/base.py b/geojson_modelica_translator/model_connectors/base.py index 3405b09d6..f9bd1980d 100644 --- a/geojson_modelica_translator/model_connectors/base.py +++ b/geojson_modelica_translator/model_connectors/base.py @@ -1,6 +1,6 @@ """ **************************************************************************************************** -:copyright (c) 2019 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. diff --git a/geojson_modelica_translator/model_connectors/spawn.py b/geojson_modelica_translator/model_connectors/spawn.py index a017146c3..204791c23 100644 --- a/geojson_modelica_translator/model_connectors/spawn.py +++ b/geojson_modelica_translator/model_connectors/spawn.py @@ -1,6 +1,6 @@ """ **************************************************************************************************** -:copyright (c) 2019 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. @@ -159,7 +159,7 @@ def to_modelica(self, project_name, root_building_dir): os.path.join(b_modelica_path.resources_dir, template_data['epw']['filename']) ) else: - raise Exception(f"Missing epw file for Spawn: {template_data['epw']['epw_filename']}") + raise Exception(f"Missing EPW file for Spawn: {template_data['epw']['epw_filename']}") if os.path.exists(template_data['mos_weather']['mos_weather_filename']): shutil.copy( @@ -168,7 +168,7 @@ def to_modelica(self, project_name, root_building_dir): ) else: raise Exception( - f"Missing mos weather file for Spawn: {template_data['mos_weather']['mos_weather_filename']}") + f"Missing MOS weather file for Spawn: {template_data['mos_weather']['mos_weather_filename']}") file_data = spawn_fmu_template.render( project_name=project_name, diff --git a/geojson_modelica_translator/model_connectors/teaser.py b/geojson_modelica_translator/model_connectors/teaser.py index 2efed0c74..1f4ada2ef 100644 --- a/geojson_modelica_translator/model_connectors/teaser.py +++ b/geojson_modelica_translator/model_connectors/teaser.py @@ -1,6 +1,6 @@ """ **************************************************************************************************** -:copyright (c) 2019 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. diff --git a/geojson_modelica_translator/modelica/input_parser.py b/geojson_modelica_translator/modelica/input_parser.py index 965d8cb1a..cae132bd8 100644 --- a/geojson_modelica_translator/modelica/input_parser.py +++ b/geojson_modelica_translator/modelica/input_parser.py @@ -1,6 +1,6 @@ """ **************************************************************************************************** -:copyright (c) 2019 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. diff --git a/geojson_modelica_translator/system_parameters/system_parameters.py b/geojson_modelica_translator/system_parameters/system_parameters.py index 5ff8e5d28..882a03990 100644 --- a/geojson_modelica_translator/system_parameters/system_parameters.py +++ b/geojson_modelica_translator/system_parameters/system_parameters.py @@ -1,6 +1,6 @@ """ **************************************************************************************************** -:copyright (c) 2019 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. diff --git a/geojson_modelica_translator/utils.py b/geojson_modelica_translator/utils.py index 428c50b4f..a6f9e3ea3 100644 --- a/geojson_modelica_translator/utils.py +++ b/geojson_modelica_translator/utils.py @@ -1,6 +1,6 @@ """ **************************************************************************************************** -:copyright (c) 2019 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. diff --git a/management/update_licenses.py b/management/update_licenses.py index da3c84368..2eb4441a1 100644 --- a/management/update_licenses.py +++ b/management/update_licenses.py @@ -1,6 +1,6 @@ """ **************************************************************************************************** -:copyright (c) 2019 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. @@ -37,7 +37,7 @@ PYTHON_REGEX = re.compile(r'^""".\*{100}.*:copyright.*\*{100}."""$', re.MULTILINE | re.DOTALL) PYTHON_LICENSE = '''""" **************************************************************************************************** -:copyright (c) 2019 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. diff --git a/management/update_schemas.py b/management/update_schemas.py index b79b5eaa4..372f4c64a 100644 --- a/management/update_schemas.py +++ b/management/update_schemas.py @@ -1,6 +1,6 @@ """ **************************************************************************************************** -:copyright (c) 2019 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. diff --git a/setup.py b/setup.py index 0b68b61bd..2c5f04cbb 100755 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ """ **************************************************************************************************** -:copyright (c) 2019 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. diff --git a/tests/context.py b/tests/context.py index c74fe5fe6..8e7ad4658 100644 --- a/tests/context.py +++ b/tests/context.py @@ -1,6 +1,6 @@ """ **************************************************************************************************** -:copyright (c) 2019 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. diff --git a/tests/geojson/test_geojson.py b/tests/geojson/test_geojson.py index d8c9f8972..0ae724c05 100644 --- a/tests/geojson/test_geojson.py +++ b/tests/geojson/test_geojson.py @@ -1,6 +1,6 @@ """ **************************************************************************************************** -:copyright (c) 2019 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. diff --git a/tests/geojson/test_schemas.py b/tests/geojson/test_schemas.py index b8bbbe5a8..6ca4ade0c 100644 --- a/tests/geojson/test_schemas.py +++ b/tests/geojson/test_schemas.py @@ -1,6 +1,6 @@ """ **************************************************************************************************** -:copyright (c) 2019 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. diff --git a/tests/model_connectors/test_spawn.py b/tests/model_connectors/test_spawn.py index 4f38a95ba..6cedd0d61 100644 --- a/tests/model_connectors/test_spawn.py +++ b/tests/model_connectors/test_spawn.py @@ -1,6 +1,6 @@ """ **************************************************************************************************** -:copyright (c) 2019 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. diff --git a/tests/modelica/test_input_parser.py b/tests/modelica/test_input_parser.py index 0e0a0e1ea..d30c64520 100644 --- a/tests/modelica/test_input_parser.py +++ b/tests/modelica/test_input_parser.py @@ -1,6 +1,6 @@ """ **************************************************************************************************** -:copyright (c) 2019 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. diff --git a/tests/system_parameters/test_system_parameters.py b/tests/system_parameters/test_system_parameters.py index a44724c1e..4f8180b96 100644 --- a/tests/system_parameters/test_system_parameters.py +++ b/tests/system_parameters/test_system_parameters.py @@ -1,6 +1,6 @@ """ **************************************************************************************************** -:copyright (c) 2019 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. diff --git a/tests/test_translator.py b/tests/test_translator.py index b9f88cd06..8db539492 100644 --- a/tests/test_translator.py +++ b/tests/test_translator.py @@ -1,6 +1,6 @@ """ **************************************************************************************************** -:copyright (c) 2019 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. diff --git a/tests/test_utils.py b/tests/test_utils.py index 2aca12cb0..56ae1567a 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,6 +1,6 @@ """ **************************************************************************************************** -:copyright (c) 2019 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. From 3174d32f6e17f01b1a0798b4acec75e7feab8781 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Wed, 4 Mar 2020 10:17:09 -0700 Subject: [PATCH 10/62] update template --- CHANGELOG.rst | 3 +-- .../model_connectors/templates/spawn_fmu.mot | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f697415d6..07bfe05bc 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,5 @@ Version 0.1 (Unreleased) This is the initial release of the package and includes the following functionality: -* Create a ROM/RC model using Modelica 3.2.x, Modelica Buildings Library 7.0 and TEASER 0.7.2 (prerelease) +* Create a ROM/RC model using Modelica 3.2.x, Modelica Buildings Library 7.0 and TEASER 0.7.2 * Create a Spawn-based models which loads an IDF file - diff --git a/geojson_modelica_translator/model_connectors/templates/spawn_fmu.mot b/geojson_modelica_translator/model_connectors/templates/spawn_fmu.mot index 37c870083..2e523274a 100644 --- a/geojson_modelica_translator/model_connectors/templates/spawn_fmu.mot +++ b/geojson_modelica_translator/model_connectors/templates/spawn_fmu.mot @@ -20,7 +20,7 @@ model building Modelica.Blocks.Sources.Constant qLatGai_flow(k=0) "Latent heat gain" annotation (Placement(transformation(extent={{-74,-50},{-54,-30}})));{% endraw %} {% for zone in data['thermal_zones'] %} - Buildings.Experimental.EnergyPlus.ThermalZone {{zone['modelica_object_name']}}( + Buildings.ThermalZones.EnergyPlus.ThermalZone {{zone['modelica_object_name']}}( redeclare package Medium = Medium, idfName=idfName, weaName=weaName, @@ -59,7 +59,7 @@ First implementation. "), - __Dymola_Commands(file="modelica://Buildings/Resources/Scripts/Dymola/Experimental/EnergyPlus/Validation/RefBldgSmallOffice.mos" + __Dymola_Commands(file="modelica://Buildings/Resources/Scripts/Dymola/ThermalZones/EnergyPlus/Validation/RefBldgSmallOffice.mos" "Simulate and plot"), experiment( StopTime=604800, From d7bea91690101960a2e72f135b0e4acf7e946c9c Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Wed, 4 Mar 2020 12:37:38 -0700 Subject: [PATCH 11/62] add running modelica class --- .gitignore | 1 + README.rst | 5 +- .../modelica/lib/__init__.py | 0 .../modelica/lib/runner/__init__.py | 0 .../modelica/lib/runner/jm_ipython.sh | 105 +++++++++++++++ .../modelica/lib/runner/jmodelica.py | 125 ++++++++++++++++++ .../modelica/lib/runner/license.txt | 43 ++++++ .../modelica/modelica_runner.py | 114 ++++++++++++++++ tests/modelica/data/BouncingBall.mo | 23 ++++ tests/modelica/test_modelica_runner.py | 81 ++++++++++++ 10 files changed, 495 insertions(+), 2 deletions(-) create mode 100644 geojson_modelica_translator/modelica/lib/__init__.py create mode 100644 geojson_modelica_translator/modelica/lib/runner/__init__.py create mode 100755 geojson_modelica_translator/modelica/lib/runner/jm_ipython.sh create mode 100644 geojson_modelica_translator/modelica/lib/runner/jmodelica.py create mode 100644 geojson_modelica_translator/modelica/lib/runner/license.txt create mode 100644 geojson_modelica_translator/modelica/modelica_runner.py create mode 100644 tests/modelica/data/BouncingBall.mo create mode 100644 tests/modelica/test_modelica_runner.py diff --git a/.gitignore b/.gitignore index e6f301d64..03956b627 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ downloads/ eggs/ .eggs/ lib/ +!geojson_modelica_translator/modelica/lib lib64/ parts/ sdist/ diff --git a/README.rst b/README.rst index d93f51829..4eb106f38 100644 --- a/README.rst +++ b/README.rst @@ -53,7 +53,7 @@ The Simulation Mapper Class can operate at mulitple levels: 2. The Load Model Connection -- input: geojson+, output: multiple files related to building load models (spawn, rom, csv) 3. The Translation to Modelica -- input: custom format, output: .mo (example inputs: geojson+, system design parameters). The translators are implicit to the load model connectors as each load model requires different paramters to calculate the loads. -In some cases, the Level 3 case (transalation to Modelica) is a blackbox method (e.g. TEASER) which prevents a simulation mapper class from existing at that level. +In some cases, the Level 3 case (translation to Modelica) is a blackbox method (e.g. TEASER) which prevents a simulation mapper class from existing at that level. Adjacency Matrix ++++++++++++++++ @@ -75,7 +75,8 @@ will automatically run the models without having to follow the steps below. * Mac: `brew install git-lfs; git lfs install` * Ubuntu: `sudo apt install git-lfs; git lfs install` * Add the Buildings Library path to your MODELICAPATH environment variable (e.g., export MODELICAPATH=${MODELICAPATH}:/home//github/modelica-buildings). -* Example simulation: +* Example simulation: + * `cp * `jm_ipython.sh jmodelica.py spawn_two_building.Loads.B5a6b99ec37f4de7f94020090.building` * `jm_ipython.sh jmodelica.py spawn_two_building/Loads/B5a6b99ec37f4de7f94020090/building.mo` * Visualize the results by inspecting the resulting mat file using BuildingsPy. diff --git a/geojson_modelica_translator/modelica/lib/__init__.py b/geojson_modelica_translator/modelica/lib/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/geojson_modelica_translator/modelica/lib/runner/__init__.py b/geojson_modelica_translator/modelica/lib/runner/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/geojson_modelica_translator/modelica/lib/runner/jm_ipython.sh b/geojson_modelica_translator/modelica/lib/runner/jm_ipython.sh new file mode 100755 index 000000000..00aa2808c --- /dev/null +++ b/geojson_modelica_translator/modelica/lib/runner/jm_ipython.sh @@ -0,0 +1,105 @@ +#!/bin/bash +################################################# +# Shell script that simulates JModelica using +# a docker image of JModelica. +# +# The main purpose of this script is to export +# MODELICAPATH and PYTHONPATH with their values +# updated for the docker, and to mount the +# required directories. +################################################# +set -e +IMG_NAME=ubuntu-1804_jmodelica_trunk +DOCKER_USERNAME=michaelwetter + +# Function declarations +function create_mount_command() +{ + local pat="$1" + # Each entry in pat will be a mounted read-only volume + local mnt_cmd="" + for ele in ${pat//:/ }; do + mnt_cmd="${mnt_cmd} -v ${ele}:/mnt${ele}:ro" + done + + # On Darwin, the exported temporary folder needs to be /private/var/folders, not /var/folders + # see https://askubuntu.com/questions/600018/how-to-display-the-paths-in-path-separately + if [ `uname` == "Darwin" ]; then + mnt_cmd=`echo ${mnt_cmd} | sed -e 's| /var/folders/| /private/var/folders/|g'` + fi + echo "${mnt_cmd}" +} + +function update_path_variable() +{ + # Prepend /mnt/ in front of each entry of a PATH variable in which the arguments are + # separated by a colon ":" + # This allows for example to create the new MODELICAPATH + local pat="$1" + local new_pat=`(set -f; IFS=:; printf "/mnt%s:" ${pat})` + # Cut the trailing ':' + new_pat=${new_pat%?} + echo "${new_pat}" +} + +# Export the MODELICAPATH +if [ -z ${MODELICAPATH+x} ]; then + MODELICAPATH=`pwd` +else + # Add the current directory to the front of the Modelica path. + # This will export the directory to the docker, and also set + # it in the MODELICAPATH so that JModelica finds it. + MODELICAPATH=`pwd`:${MODELICAPATH} +fi + +# Create the command to mount all directories in read-only mode +# a) for MODELICAPATH +MOD_MOUNT=`create_mount_command ${MODELICAPATH}` +# b) for PYTHONPATH +PYT_MOUNT=`create_mount_command ${PYTHONPATH}` + +# Prepend /mnt/ in front of each entry, which will then be used as the MODELICAPATH +DOCKER_MODELICAPATH=`update_path_variable ${MODELICAPATH}` +DOCKER_PYTHONPATH=`update_path_variable ${PYTHONPATH}` + +# If the current directory is part of the argument list, +# replace it with . as the docker may have a different file structure +cur_dir=`pwd` +bas_nam=`basename ${cur_dir}` +arg_lis=`echo $@ | sed -e "s|${cur_dir}|.|g"` + +# Set variable for shared directory +sha_dir=`dirname ${cur_dir}` + +# Check if the python script should be run interactively (if -i is specified) +while [ $# -ne 0 ] +do + arg="$1" + case "$arg" in + -i) + interactive=true + DOCKER_INTERACTIVE=-t + ;; + esac + shift +done + +# --user=${UID} \ + +docker run \ + --user=${UID} \ + -i \ + $DOCKER_INTERACTIVE \ + --detach=false \ + ${MOD_MOUNT} \ + ${PYT_MOUNT} \ + -v ${sha_dir}:/mnt/shared \ + -e DISPLAY=${DISPLAY} \ + -v /tmp/.X11-unix:/tmp/.X11-unix \ + --rm \ + ${DOCKER_USERNAME}/${IMG_NAME} /bin/bash -c \ + "export MODELICAPATH=${DOCKER_MODELICAPATH}:/usr/local/JModelica/ThirdParty/MSL && \ + export PYTHONPATH=${DOCKER_PYTHONPATH} && \ + cd /mnt/shared/${bas_nam} && \ + /usr/local/JModelica/bin/jm_ipython.sh ${arg_lis}" +exit $? diff --git a/geojson_modelica_translator/modelica/lib/runner/jmodelica.py b/geojson_modelica_translator/modelica/lib/runner/jmodelica.py new file mode 100644 index 000000000..7dd55f490 --- /dev/null +++ b/geojson_modelica_translator/modelica/lib/runner/jmodelica.py @@ -0,0 +1,125 @@ +########################################################################## +# Script to simulate Modelica models with JModelica. +# +########################################################################## +# Import the function for compilation of models and the load_fmu method + +from pymodelica import compile_fmu +import traceback +import logging + +from pyfmi import load_fmu +import pymodelica + +import os +import shutil +import sys +# import matplotlib.pyplot as plt + +debug_solver = False +model="Buildings.Controls.OBC.CDL.Continuous.Validation.LimPID" +# Overwrite model with command line argument if specified +if len(sys.argv) > 1: + # If the argument is a file, then parse it to a model name + if os.path.isfile(sys.argv[1]): + model = sys.argv[1].replace(os.path.sep, '.')[:-3] + else: + model=sys.argv[1] + + +print("*** Compiling {}".format(model)) +# Increase memory +pymodelica.environ['JVM_ARGS'] = '-Xmx4096m' + + +sys.stdout.flush() + +###################################################################### +# Compile fmu +fmu_name = compile_fmu(model, + version="2.0", + compiler_log_level='warning', + compiler_options = {"generate_html_diagnostics" : False, + "nle_solver_tol_factor": 1e-2}) + +###################################################################### +# Load model +mod = load_fmu(fmu_name, log_level=3) + +###################################################################### +# Retrieve and set solver options +x_nominal = mod.nominal_continuous_states +opts = mod.simulate_options() #Retrieve the default options + +opts['solver'] = 'CVode' +opts['ncp'] = 5000 + +if opts['solver'].lower() == 'cvode': + # Set user-specified tolerance if it is smaller than the tolerance in the .mo file + rtol = 1.0e-6 + x_nominal = mod.nominal_continuous_states + + if len(x_nominal) > 0: + atol = rtol*x_nominal + else: + atol = rtol + + opts['CVode_options'] = { + 'external_event_detection': False, + 'maxh': (mod.get_default_experiment_stop_time()-mod.get_default_experiment_stop_time())/float(opts['ncp']), + 'iter': 'Newton', + 'discr': 'BDF', + 'rtol': rtol, + 'atol': atol, + 'store_event_points': True + } + +if debug_solver: + opts["logging"] = True #<- Turn on solver debug logging +mod.set("_log_level", 6) + +###################################################################### +# Simulate +res = mod.simulate(options=opts) +# logging.error(traceback.format_exc()) + +# plt.plot(res['time'], res['x1']) +# plt.plot(res['time'], res['x2']) +# plt.xlabel('time in [s]') +# plt.ylabel('line2.y') +# plt.grid() +# plt.show() +# plt.savefig("plot.pdf") + +###################################################################### +# Copy style sheets. +# This is a hack to get the css and js files to render the html diagnostics. +htm_dir = os.path.splitext(os.path.basename(fmu_name))[0] + "_html_diagnostics" +if os.path.exists(htm_dir): + for fil in ["scripts.js", "style.css", "zepto.min.js"]: + src = os.path.join(".jmodelica_html", fil) + if os.path.exists(src): + des = os.path.join(htm_dir, fil) + shutil.copyfile(src, des) + +###################################################################### +# Get debugging information +if debug_solver: + #Load the debug information + from pyfmi.debug import CVodeDebugInformation + debug = CVodeDebugInformation(model.replace(".", "_")+"_debug.txt") + + ### Below are options to plot the order, error and step-size evolution. + ### The error methos also take a threshold and a region if you want to + ### limit the plot to a certain interval. + + #Plot order evolution + debug.plot_order() + + #Plot error evolution + debug.plot_error() #Note see also the arguments to the method + + #Plot the used step-size + debug.plot_step_size() + + #See also debug? diff --git a/geojson_modelica_translator/modelica/lib/runner/license.txt b/geojson_modelica_translator/modelica/lib/runner/license.txt new file mode 100644 index 000000000..14c934007 --- /dev/null +++ b/geojson_modelica_translator/modelica/lib/runner/license.txt @@ -0,0 +1,43 @@ +Copyright (c) 2018, The Regents of the University of California, Department +of Energy contract-operators of the Lawrence Berkeley National Laboratory. +All rights reserved. + +1. Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + (1) Redistributions of source code must retain the copyright notice, this + list of conditions and the following disclaimer. + + (2) Redistributions in binary form must reproduce the copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + (3) Neither the name of the University of California, Lawrence Berkeley + National Laboratory, U.S. Dept. of Energy nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +2. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +3. You are under no obligation whatsoever to provide any bug fixes, patches, +or upgrades to the features, functionality or performance of the source code +("Enhancements") to anyone; however, if you choose to make your Enhancements +available either publicly, or directly to Lawrence Berkeley National +Laboratory, without imposing a separate written license agreement for such +Enhancements, then you hereby grant the following license: a non-exclusive, +royalty-free perpetual license to install, use, modify, prepare derivative +works, incorporate into other computer software, distribute, and sublicense +such enhancements or derivative works thereof, in binary and source code form. + +NOTE: This license corresponds to the "revised BSD" or "3-clause BSD" license +and includes the following modification: Paragraph 3. has been added by the +University of California. diff --git a/geojson_modelica_translator/modelica/modelica_runner.py b/geojson_modelica_translator/modelica/modelica_runner.py new file mode 100644 index 000000000..cd6c4f51d --- /dev/null +++ b/geojson_modelica_translator/modelica/modelica_runner.py @@ -0,0 +1,114 @@ +""" +**************************************************************************************************** +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted +provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions +and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this list of conditions +and the following disclaimer in the documentation and/or other materials provided with the +distribution. + +Neither the name of the copyright holder nor the names of its contributors may be used to endorse +or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**************************************************************************************************** +""" + +import os + +from jinja2 import FileSystemLoader, Environment +import shutil +import subprocess + + +class ModelicaRunner(object): + """ + Class to run Modelica models. This is a very simple implementation of what needs to be + a full CLI to run Modelica easily. At the moment, this probably only works on Linux/Mac + and perhaps in Windows with Docker. + + # TODO: test in windows + # Document how to install Docker + """ + + def __init__(self, modelica_lib_path=None): + """ + Initialize the runner with data needed for simulation + + :param modelica_lib_path: string, Path to the MBL to run against + """ + self.modelica_lib_path = modelica_lib_path + local_path = os.path.dirname(os.path.abspath(__file__)) + self.jmodelica_py_path = os.path.join(local_path, 'lib', 'runner', 'jmodelica.py') + self.jm_ipython_path = os.path.join(local_path, 'lib', 'runner', 'jm_ipython.sh') + + # Verify that docker is up and running + r = subprocess.call(['docker', 'ps']) + self.docker_configured = r == 0 + + def run_in_docker(self, file_to_run): + """ + Run the Modelica project in a docker-based environment. Results are saved into the path of the + file that was selected to run. + + stdout.log will store both stdout and stderr of the simulations + + :param file_to_run: string, name of the file (could be directory?) to simulate + """ + if not self.docker_configured: + raise Exception('Docker not configured on host computer, unable to run') + + if not os.path.exists(file_to_run): + raise Exception(f'File not found to run {file_to_run}') + + if not os.path.isfile(file_to_run): + raise Exception(f'Expecting to run a file, not a folder in {file_to_run}') + + run_path = os.path.dirname(file_to_run) + new_jm_ipython = os.path.join(run_path, os.path.basename(self.jm_ipython_path)) + shutil.copyfile(self.jm_ipython_path, new_jm_ipython) + os.chmod(new_jm_ipython, 0o775) + shutil.copyfile(self.jmodelica_py_path, os.path.join(run_path, os.path.basename(self.jmodelica_py_path))) + curdir = os.getcwd() + os.chdir(run_path) + stdout_log = open('stdout.log', 'w') + try: + p = subprocess.Popen( + ['./jm_ipython.sh', 'jmodelica.py', os.path.basename(file_to_run)], + stdout=stdout_log, + stderr=subprocess.STDOUT, + cwd=run_path + ) + exitcode = p.wait() + finally: + os.chdir(curdir) + stdout_log.close() + + return exitcode + + def cleanup_path(self, path): + """ + Clean up the files in the path that was presumably used to run the simulation + """ + remove_files = [ + 'jm_ipython.sh', + 'jmodelica.py', + ] + + for f in remove_files: + if os.path.exists(os.path.join(path, f)): + os.remove(os.path.join(path, f)) diff --git a/tests/modelica/data/BouncingBall.mo b/tests/modelica/data/BouncingBall.mo new file mode 100644 index 000000000..e6f51a306 --- /dev/null +++ b/tests/modelica/data/BouncingBall.mo @@ -0,0 +1,23 @@ +model BouncingBall + parameter Real e=0.7 "coefficient of restitution"; + parameter Real g=9.81 "gravity acceleration"; + Real h(fixed=true, start=1) "height of ball"; + Real v(fixed=true) "velocity of ball"; + Boolean flying(fixed=true, start=true) "true, if ball is flying"; + Boolean impact; + Real v_new(fixed=true); + Integer foo; + +equation + impact = h <= 0.0; + foo = if impact then 1 else 2; + der(v) = if flying then -g else 0; + der(h) = v; + + when {h <= 0.0 and v <= 0.0,impact} then + v_new = if edge(impact) then -e*pre(v) else 0; + flying = v_new > 0; + reinit(v, v_new); + end when; + +end BouncingBall; \ No newline at end of file diff --git a/tests/modelica/test_modelica_runner.py b/tests/modelica/test_modelica_runner.py new file mode 100644 index 000000000..cca9593c5 --- /dev/null +++ b/tests/modelica/test_modelica_runner.py @@ -0,0 +1,81 @@ +""" +**************************************************************************************************** +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted +provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions +and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this list of conditions +and the following disclaimer in the documentation and/or other materials provided with the +distribution. + +Neither the name of the copyright holder nor the names of its contributors may be used to endorse +or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**************************************************************************************************** +""" + +from ..context import geojson_modelica_translator # noqa - Do not remove this line + +import filecmp +import shutil +import os +import unittest + +from geojson_modelica_translator.modelica.modelica_runner import ModelicaRunner + + +class ModelicaRunnerTest(unittest.TestCase): + # create a run directory and copy in a project to test run + def setUp(self): + self.run_path = os.path.abspath('tests/modelica/output/simdir') + if not os.path.exists(self.run_path): + os.mkdir(self.run_path) + # copy in the test modelica file (teaser model) + shutil.copyfile(os.path.abspath('tests/modelica/data/BouncingBall.mo'), + os.path.join(self.run_path, 'BouncingBall.mo')) + + def test_run_setup(self): + mr = ModelicaRunner() + self.assertEqual(mr.modelica_lib_path, None) + print(mr.jmodelica_py_path) + self.assertTrue(os.path.exists(mr.jmodelica_py_path)) + self.assertTrue(os.path.exists(mr.jm_ipython_path)) + + def test_docker_enabled(self): + mr = ModelicaRunner() + self.assertTrue(mr.docker_configured, 'Docker is not running, unable to run all tests') + + def test_run_in_docker_errors(self): + mr = ModelicaRunner() + file_to_run = os.path.join(self.run_path, 'no_file.mo') + with self.assertRaises(Exception) as exc: + mr.run_in_docker(file_to_run) + self.assertEqual(f'File not found to run {file_to_run}', str(exc.exception)) + + file_to_run = os.path.join(self.run_path) + with self.assertRaises(Exception) as exc: + mr.run_in_docker(file_to_run) + self.assertEqual(f'Expecting to run a file, not a folder in {file_to_run}', str(exc.exception)) + + def test_run_in_docker(self): + mr = ModelicaRunner() + mr.run_in_docker(os.path.join(self.run_path, 'BouncingBall.mo')) + mr.cleanup_path(self.run_path) + + +if __name__ == '__main__': + unittest.main() From 45232020a7b058f8abfe459266e052c4ea4be049 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Wed, 4 Mar 2020 12:41:34 -0700 Subject: [PATCH 12/62] suppress stdout on docker ps call --- geojson_modelica_translator/modelica/modelica_runner.py | 2 +- tests/modelica/test_modelica_runner.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/geojson_modelica_translator/modelica/modelica_runner.py b/geojson_modelica_translator/modelica/modelica_runner.py index cd6c4f51d..a08b04873 100644 --- a/geojson_modelica_translator/modelica/modelica_runner.py +++ b/geojson_modelica_translator/modelica/modelica_runner.py @@ -57,7 +57,7 @@ def __init__(self, modelica_lib_path=None): self.jm_ipython_path = os.path.join(local_path, 'lib', 'runner', 'jm_ipython.sh') # Verify that docker is up and running - r = subprocess.call(['docker', 'ps']) + r = subprocess.call(['docker', 'ps'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) self.docker_configured = r == 0 def run_in_docker(self, file_to_run): diff --git a/tests/modelica/test_modelica_runner.py b/tests/modelica/test_modelica_runner.py index cca9593c5..99d678c5b 100644 --- a/tests/modelica/test_modelica_runner.py +++ b/tests/modelica/test_modelica_runner.py @@ -75,6 +75,8 @@ def test_run_in_docker(self): mr = ModelicaRunner() mr.run_in_docker(os.path.join(self.run_path, 'BouncingBall.mo')) mr.cleanup_path(self.run_path) + self.assertTrue(os.path.exists(os.path.join(self.run_path, 'stdout.log'))) + self.assertTrue(os.path.exists(os.path.join(self.run_path, 'BouncingBall_result.mat'))) if __name__ == '__main__': From 2850b94e580b060ded17752b20b3ad2f9546580e Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Wed, 4 Mar 2020 12:44:42 -0700 Subject: [PATCH 13/62] code cleanup --- .../modelica/lib/runner/jmodelica.py | 90 +++++++++---------- .../modelica/modelica_runner.py | 1 - tests/modelica/test_input_parser.py | 4 +- tests/modelica/test_modelica_runner.py | 1 - 4 files changed, 46 insertions(+), 50 deletions(-) diff --git a/geojson_modelica_translator/modelica/lib/runner/jmodelica.py b/geojson_modelica_translator/modelica/lib/runner/jmodelica.py index 7dd55f490..967ddc2d0 100644 --- a/geojson_modelica_translator/modelica/lib/runner/jmodelica.py +++ b/geojson_modelica_translator/modelica/lib/runner/jmodelica.py @@ -4,27 +4,25 @@ ########################################################################## # Import the function for compilation of models and the load_fmu method -from pymodelica import compile_fmu -import traceback -import logging +import os -from pyfmi import load_fmu import pymodelica - -import os import shutil import sys +from pyfmi import load_fmu +from pymodelica import compile_fmu + # import matplotlib.pyplot as plt debug_solver = False -model="Buildings.Controls.OBC.CDL.Continuous.Validation.LimPID" +model = "Buildings.Controls.OBC.CDL.Continuous.Validation.LimPID" # Overwrite model with command line argument if specified if len(sys.argv) > 1: - # If the argument is a file, then parse it to a model name - if os.path.isfile(sys.argv[1]): - model = sys.argv[1].replace(os.path.sep, '.')[:-3] - else: - model=sys.argv[1] + # If the argument is a file, then parse it to a model name + if os.path.isfile(sys.argv[1]): + model = sys.argv[1].replace(os.path.sep, '.')[:-3] + else: + model = sys.argv[1] print("*** Compiling {}".format(model)) @@ -39,8 +37,8 @@ fmu_name = compile_fmu(model, version="2.0", compiler_log_level='warning', - compiler_options = {"generate_html_diagnostics" : False, - "nle_solver_tol_factor": 1e-2}) + compiler_options={"generate_html_diagnostics": False, + "nle_solver_tol_factor": 1e-2}) ###################################################################### # Load model @@ -49,33 +47,33 @@ ###################################################################### # Retrieve and set solver options x_nominal = mod.nominal_continuous_states -opts = mod.simulate_options() #Retrieve the default options +opts = mod.simulate_options() # Retrieve the default options opts['solver'] = 'CVode' opts['ncp'] = 5000 if opts['solver'].lower() == 'cvode': - # Set user-specified tolerance if it is smaller than the tolerance in the .mo file - rtol = 1.0e-6 - x_nominal = mod.nominal_continuous_states - - if len(x_nominal) > 0: - atol = rtol*x_nominal - else: - atol = rtol - - opts['CVode_options'] = { - 'external_event_detection': False, - 'maxh': (mod.get_default_experiment_stop_time()-mod.get_default_experiment_stop_time())/float(opts['ncp']), - 'iter': 'Newton', - 'discr': 'BDF', - 'rtol': rtol, - 'atol': atol, - 'store_event_points': True + # Set user-specified tolerance if it is smaller than the tolerance in the .mo file + rtol = 1.0e-6 + x_nominal = mod.nominal_continuous_states + + if len(x_nominal) > 0: + atol = rtol * x_nominal + else: + atol = rtol + + opts['CVode_options'] = { + 'external_event_detection': False, + 'maxh': (mod.get_default_experiment_stop_time() - mod.get_default_experiment_stop_time()) / float(opts['ncp']), + 'iter': 'Newton', + 'discr': 'BDF', + 'rtol': rtol, + 'atol': atol, + 'store_event_points': True } if debug_solver: - opts["logging"] = True #<- Turn on solver debug logging + opts["logging"] = True # <- Turn on solver debug logging mod.set("_log_level", 6) ###################################################################### @@ -105,21 +103,21 @@ ###################################################################### # Get debugging information if debug_solver: - #Load the debug information - from pyfmi.debug import CVodeDebugInformation - debug = CVodeDebugInformation(model.replace(".", "_")+"_debug.txt") + # Load the debug information + from pyfmi.debug import CVodeDebugInformation + debug = CVodeDebugInformation(model.replace(".", "_") + "_debug.txt") - ### Below are options to plot the order, error and step-size evolution. - ### The error methos also take a threshold and a region if you want to - ### limit the plot to a certain interval. + # Below are options to plot the order, error and step-size evolution. + # The error methos also take a threshold and a region if you want to + # limit the plot to a certain interval. - #Plot order evolution - debug.plot_order() + # Plot order evolution + debug.plot_order() - #Plot error evolution - debug.plot_error() #Note see also the arguments to the method + # Plot error evolution + debug.plot_error() # Note see also the arguments to the method - #Plot the used step-size - debug.plot_step_size() + # Plot the used step-size + debug.plot_step_size() - #See also debug? + # See also debug? diff --git a/geojson_modelica_translator/modelica/modelica_runner.py b/geojson_modelica_translator/modelica/modelica_runner.py index a08b04873..5b2666f09 100644 --- a/geojson_modelica_translator/modelica/modelica_runner.py +++ b/geojson_modelica_translator/modelica/modelica_runner.py @@ -30,7 +30,6 @@ import os -from jinja2 import FileSystemLoader, Environment import shutil import subprocess diff --git a/tests/modelica/test_input_parser.py b/tests/modelica/test_input_parser.py index d30c64520..1c5c9bf8c 100644 --- a/tests/modelica/test_input_parser.py +++ b/tests/modelica/test_input_parser.py @@ -93,7 +93,7 @@ def test_rename_filename(self): f1.replace_model_string( 'Modelica.Blocks.Sources.CombiTimeTable', 'internalGains', - 'modelica://Project/B5a6b99ec37f4de7f94020090/B5a6b99ec37f4de7f94020090_Models/InternalGains_B5a6b99ec37f4de7f94020090Floor.mat', # noqa + 'modelica://Project/B5a6b99ec37f4de7f94020090/B5a6b99ec37f4de7f94020090_Models/InternalGains_B5a6b99ec37f4de7f94020090Floor.mat', # noqa # noqa 'modelica://a/new/path.mat' ) @@ -110,7 +110,7 @@ def test_add_model_obj(self): new_filename = os.path.abspath('tests/modelica/output/test_1_output_5.mo') f1 = InputParser(filename) data = [ - 'annotation (Placement(transformation(extent={{-10,90},{10,110}}), iconTransformation(extent={{-10,90},{10,110}})));' # noqa + 'annotation (Placement(transformation(extent={{-10,90},{10,110}}), iconTransformation(extent={{-10,90},{10,110}})));' # noqa # noqa ] f1.add_model_object('Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a', 'port_a', data) diff --git a/tests/modelica/test_modelica_runner.py b/tests/modelica/test_modelica_runner.py index 99d678c5b..621ed7956 100644 --- a/tests/modelica/test_modelica_runner.py +++ b/tests/modelica/test_modelica_runner.py @@ -30,7 +30,6 @@ from ..context import geojson_modelica_translator # noqa - Do not remove this line -import filecmp import shutil import os import unittest From 0781df4009280e49194466fce27ea2829bcdefbb Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Wed, 4 Mar 2020 13:22:14 -0700 Subject: [PATCH 14/62] add more tests --- CHANGELOG.rst | 5 +++-- .../model_connectors/templates/spawn_fmu.mot | 2 +- .../modelica/modelica_runner.py | 7 ++++++- tests/model_connectors/test_spawn.py | 11 ++++++++++- tests/modelica/test_modelica_runner.py | 9 +++++++-- 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 07bfe05bc..c983903d5 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,5 +6,6 @@ Version 0.1 (Unreleased) This is the initial release of the package and includes the following functionality: -* Create a ROM/RC model using Modelica 3.2.x, Modelica Buildings Library 7.0 and TEASER 0.7.2 -* Create a Spawn-based models which loads an IDF file +* Initial implementation of a ModelicaRunner to call a Docker container to run the model. +* Create a ROM/RC model using Modelica 3.2.x, Modelica Buildings Library 7.0 and TEASER 0.7.2. +* Create a Spawn-based models which loads an IDF file. diff --git a/geojson_modelica_translator/model_connectors/templates/spawn_fmu.mot b/geojson_modelica_translator/model_connectors/templates/spawn_fmu.mot index 2e523274a..6b3654127 100644 --- a/geojson_modelica_translator/model_connectors/templates/spawn_fmu.mot +++ b/geojson_modelica_translator/model_connectors/templates/spawn_fmu.mot @@ -26,7 +26,7 @@ model building weaName=weaName, energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial, usePrecompiledFMU=false, - fmuName = Modelica.Utilities.Files.loadResource("modelica://Buildings/Resources/src/EnergyPlus/FMUs/Zones1.fmu"), + fmuName = Modelica.Utilities.Files.loadResource("modelica://Buildings/Resources/src/ThermalZones/EnergyPlus/FMUs/Zones1.fmu"), zoneName="{{zone['spawn_object_name']}}") "Thermal zone" {% raw %} annotation (Placement(transformation(extent={{20,40},{60,80}}))); {% endraw %} {% endfor %} diff --git a/geojson_modelica_translator/modelica/modelica_runner.py b/geojson_modelica_translator/modelica/modelica_runner.py index 5b2666f09..011483999 100644 --- a/geojson_modelica_translator/modelica/modelica_runner.py +++ b/geojson_modelica_translator/modelica/modelica_runner.py @@ -50,7 +50,12 @@ def __init__(self, modelica_lib_path=None): :param modelica_lib_path: string, Path to the MBL to run against """ - self.modelica_lib_path = modelica_lib_path + # check if the user has defined a MODELICAPATH, is so, then use that. + if os.environ['MODELICAPATH']: + print('Using predefined MODELICAPATH') + self.modelica_lib_path = os.environ['MODELICAPATH'] + else: + self.modelica_lib_path = modelica_lib_path local_path = os.path.dirname(os.path.abspath(__file__)) self.jmodelica_py_path = os.path.join(local_path, 'lib', 'runner', 'jmodelica.py') self.jm_ipython_path = os.path.join(local_path, 'lib', 'runner', 'jm_ipython.sh') diff --git a/tests/model_connectors/test_spawn.py b/tests/model_connectors/test_spawn.py index 6cedd0d61..e4c97749a 100644 --- a/tests/model_connectors/test_spawn.py +++ b/tests/model_connectors/test_spawn.py @@ -35,6 +35,7 @@ from geojson_modelica_translator.system_parameters.system_parameters import SystemParameters from geojson_modelica_translator.geojson_modelica_translator import GeoJsonModelicaTranslator from geojson_modelica_translator.model_connectors.spawn import SpawnConnector +from geojson_modelica_translator.modelica.modelica_runner import ModelicaRunner class SpawnModelConnectorSingleBuildingTest(unittest.TestCase): @@ -83,5 +84,13 @@ def setUp(self): for b in gj.buildings: self.spawn.add_building(b) - def test_spawn_to_modelica(self): + def test_spawn_to_modelica_and_run(self): self.spawn.to_modelica('spawn_two_building', 'tests/model_connectors/output') + + # make sure the model can run using the ModelicaRunner class + mr = ModelicaRunner() + file_to_run = os.path.abspath( + 'tests/model_connectors/output/spawn_two_building/Loads/B5a6b99ec37f4de7f94020090/building.mo' + ) + exitcode = mr.run_in_docker(file_to_run) + self.assertEqual(0, exitcode) diff --git a/tests/modelica/test_modelica_runner.py b/tests/modelica/test_modelica_runner.py index 621ed7956..4d84d5bd0 100644 --- a/tests/modelica/test_modelica_runner.py +++ b/tests/modelica/test_modelica_runner.py @@ -48,8 +48,13 @@ def setUp(self): os.path.join(self.run_path, 'BouncingBall.mo')) def test_run_setup(self): - mr = ModelicaRunner() - self.assertEqual(mr.modelica_lib_path, None) + prev_mod_path = os.environ.get('MODELICAPATH', None) + try: + os.environ['MODELICAPATH'] = 'A_PATH/to_something' + mr = ModelicaRunner() + self.assertEqual(mr.modelica_lib_path, 'A_PATH/to_something') + finally: + os.environ['MODELICAPATH'] = prev_mod_path print(mr.jmodelica_py_path) self.assertTrue(os.path.exists(mr.jmodelica_py_path)) self.assertTrue(os.path.exists(mr.jm_ipython_path)) From 8cb65ac3480c71034162ee312c23f54f52f6cb87 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Wed, 4 Mar 2020 13:25:10 -0700 Subject: [PATCH 15/62] fix modelicapath check --- geojson_modelica_translator/modelica/modelica_runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geojson_modelica_translator/modelica/modelica_runner.py b/geojson_modelica_translator/modelica/modelica_runner.py index 011483999..d581892cb 100644 --- a/geojson_modelica_translator/modelica/modelica_runner.py +++ b/geojson_modelica_translator/modelica/modelica_runner.py @@ -51,7 +51,7 @@ def __init__(self, modelica_lib_path=None): :param modelica_lib_path: string, Path to the MBL to run against """ # check if the user has defined a MODELICAPATH, is so, then use that. - if os.environ['MODELICAPATH']: + if os.environ.get('MODELICAPATH', None): print('Using predefined MODELICAPATH') self.modelica_lib_path = os.environ['MODELICAPATH'] else: From 8572b5db49b343958d01a62ceb55e01e930fd111 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Wed, 4 Mar 2020 13:32:36 -0700 Subject: [PATCH 16/62] fix modelicapath test --- tests/modelica/test_modelica_runner.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/modelica/test_modelica_runner.py b/tests/modelica/test_modelica_runner.py index 4d84d5bd0..132d95480 100644 --- a/tests/modelica/test_modelica_runner.py +++ b/tests/modelica/test_modelica_runner.py @@ -54,7 +54,8 @@ def test_run_setup(self): mr = ModelicaRunner() self.assertEqual(mr.modelica_lib_path, 'A_PATH/to_something') finally: - os.environ['MODELICAPATH'] = prev_mod_path + if prev_mod_path: + os.environ['MODELICAPATH'] = prev_mod_path print(mr.jmodelica_py_path) self.assertTrue(os.path.exists(mr.jmodelica_py_path)) self.assertTrue(os.path.exists(mr.jm_ipython_path)) From 16ad635c7639db5e34c33aa955436bb352987d16 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Wed, 4 Mar 2020 13:35:29 -0700 Subject: [PATCH 17/62] travis mbl --- .travis.yml | 3 +++ tox.ini | 1 + 2 files changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index e85a8c212..adf8dea61 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,9 @@ env: - TOX_ENV=python - TOX_ENV=flake8 - TOX_ENV=docs +before_script: + - git clone --single-branch --branch issue1442_loadCoupling https://github.com/lbl-srg/modelica-buildings.git + - export MODELICAPATH=$(pwd)/modelica-buildings script: - tox -e $TOX_ENV after_success: diff --git a/tox.ini b/tox.ini index 8d8c7bcbb..146398c58 100644 --- a/tox.ini +++ b/tox.ini @@ -12,6 +12,7 @@ commands= py.test . -v --cov coveralls --cov-report term-missing passenv= COVERALLS_REPO_TOKEN + MODELICAPATH whitelist_externals= cp From 6bf1a2b6b6ca01d22dc5d6f537abb2c832f316ec Mon Sep 17 00:00:00 2001 From: goldcoder01 Date: Wed, 20 Nov 2019 15:42:43 -0700 Subject: [PATCH 18/62] Update README with python3/pip3 --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index d93f51829..545718cb9 100644 --- a/README.rst +++ b/README.rst @@ -20,6 +20,8 @@ The GeoJSON / Modelica Translator is still in early alpha-phase development and If installing this package for development then you must run the `setup.py build` command in order to install the MBL in the right location. +currently you need python3 and pip3 to install/build the packages. + .. code-block:: bash pip install -r requirements.txt From 469870f7ee819342f5aaa7aca483975242b16571 Mon Sep 17 00:00:00 2001 From: goldcoder01 Date: Wed, 20 Nov 2019 16:06:02 -0700 Subject: [PATCH 19/62] update README with python3/pip3 --- tests/model_connectors/data/jm_ipython.sh | 105 ++++++++++++++++++ tests/model_connectors/data/jmodelica.py | 125 ++++++++++++++++++++++ 2 files changed, 230 insertions(+) create mode 100644 tests/model_connectors/data/jm_ipython.sh create mode 100644 tests/model_connectors/data/jmodelica.py diff --git a/tests/model_connectors/data/jm_ipython.sh b/tests/model_connectors/data/jm_ipython.sh new file mode 100644 index 000000000..00aa2808c --- /dev/null +++ b/tests/model_connectors/data/jm_ipython.sh @@ -0,0 +1,105 @@ +#!/bin/bash +################################################# +# Shell script that simulates JModelica using +# a docker image of JModelica. +# +# The main purpose of this script is to export +# MODELICAPATH and PYTHONPATH with their values +# updated for the docker, and to mount the +# required directories. +################################################# +set -e +IMG_NAME=ubuntu-1804_jmodelica_trunk +DOCKER_USERNAME=michaelwetter + +# Function declarations +function create_mount_command() +{ + local pat="$1" + # Each entry in pat will be a mounted read-only volume + local mnt_cmd="" + for ele in ${pat//:/ }; do + mnt_cmd="${mnt_cmd} -v ${ele}:/mnt${ele}:ro" + done + + # On Darwin, the exported temporary folder needs to be /private/var/folders, not /var/folders + # see https://askubuntu.com/questions/600018/how-to-display-the-paths-in-path-separately + if [ `uname` == "Darwin" ]; then + mnt_cmd=`echo ${mnt_cmd} | sed -e 's| /var/folders/| /private/var/folders/|g'` + fi + echo "${mnt_cmd}" +} + +function update_path_variable() +{ + # Prepend /mnt/ in front of each entry of a PATH variable in which the arguments are + # separated by a colon ":" + # This allows for example to create the new MODELICAPATH + local pat="$1" + local new_pat=`(set -f; IFS=:; printf "/mnt%s:" ${pat})` + # Cut the trailing ':' + new_pat=${new_pat%?} + echo "${new_pat}" +} + +# Export the MODELICAPATH +if [ -z ${MODELICAPATH+x} ]; then + MODELICAPATH=`pwd` +else + # Add the current directory to the front of the Modelica path. + # This will export the directory to the docker, and also set + # it in the MODELICAPATH so that JModelica finds it. + MODELICAPATH=`pwd`:${MODELICAPATH} +fi + +# Create the command to mount all directories in read-only mode +# a) for MODELICAPATH +MOD_MOUNT=`create_mount_command ${MODELICAPATH}` +# b) for PYTHONPATH +PYT_MOUNT=`create_mount_command ${PYTHONPATH}` + +# Prepend /mnt/ in front of each entry, which will then be used as the MODELICAPATH +DOCKER_MODELICAPATH=`update_path_variable ${MODELICAPATH}` +DOCKER_PYTHONPATH=`update_path_variable ${PYTHONPATH}` + +# If the current directory is part of the argument list, +# replace it with . as the docker may have a different file structure +cur_dir=`pwd` +bas_nam=`basename ${cur_dir}` +arg_lis=`echo $@ | sed -e "s|${cur_dir}|.|g"` + +# Set variable for shared directory +sha_dir=`dirname ${cur_dir}` + +# Check if the python script should be run interactively (if -i is specified) +while [ $# -ne 0 ] +do + arg="$1" + case "$arg" in + -i) + interactive=true + DOCKER_INTERACTIVE=-t + ;; + esac + shift +done + +# --user=${UID} \ + +docker run \ + --user=${UID} \ + -i \ + $DOCKER_INTERACTIVE \ + --detach=false \ + ${MOD_MOUNT} \ + ${PYT_MOUNT} \ + -v ${sha_dir}:/mnt/shared \ + -e DISPLAY=${DISPLAY} \ + -v /tmp/.X11-unix:/tmp/.X11-unix \ + --rm \ + ${DOCKER_USERNAME}/${IMG_NAME} /bin/bash -c \ + "export MODELICAPATH=${DOCKER_MODELICAPATH}:/usr/local/JModelica/ThirdParty/MSL && \ + export PYTHONPATH=${DOCKER_PYTHONPATH} && \ + cd /mnt/shared/${bas_nam} && \ + /usr/local/JModelica/bin/jm_ipython.sh ${arg_lis}" +exit $? diff --git a/tests/model_connectors/data/jmodelica.py b/tests/model_connectors/data/jmodelica.py new file mode 100644 index 000000000..7dd55f490 --- /dev/null +++ b/tests/model_connectors/data/jmodelica.py @@ -0,0 +1,125 @@ +########################################################################## +# Script to simulate Modelica models with JModelica. +# +########################################################################## +# Import the function for compilation of models and the load_fmu method + +from pymodelica import compile_fmu +import traceback +import logging + +from pyfmi import load_fmu +import pymodelica + +import os +import shutil +import sys +# import matplotlib.pyplot as plt + +debug_solver = False +model="Buildings.Controls.OBC.CDL.Continuous.Validation.LimPID" +# Overwrite model with command line argument if specified +if len(sys.argv) > 1: + # If the argument is a file, then parse it to a model name + if os.path.isfile(sys.argv[1]): + model = sys.argv[1].replace(os.path.sep, '.')[:-3] + else: + model=sys.argv[1] + + +print("*** Compiling {}".format(model)) +# Increase memory +pymodelica.environ['JVM_ARGS'] = '-Xmx4096m' + + +sys.stdout.flush() + +###################################################################### +# Compile fmu +fmu_name = compile_fmu(model, + version="2.0", + compiler_log_level='warning', + compiler_options = {"generate_html_diagnostics" : False, + "nle_solver_tol_factor": 1e-2}) + +###################################################################### +# Load model +mod = load_fmu(fmu_name, log_level=3) + +###################################################################### +# Retrieve and set solver options +x_nominal = mod.nominal_continuous_states +opts = mod.simulate_options() #Retrieve the default options + +opts['solver'] = 'CVode' +opts['ncp'] = 5000 + +if opts['solver'].lower() == 'cvode': + # Set user-specified tolerance if it is smaller than the tolerance in the .mo file + rtol = 1.0e-6 + x_nominal = mod.nominal_continuous_states + + if len(x_nominal) > 0: + atol = rtol*x_nominal + else: + atol = rtol + + opts['CVode_options'] = { + 'external_event_detection': False, + 'maxh': (mod.get_default_experiment_stop_time()-mod.get_default_experiment_stop_time())/float(opts['ncp']), + 'iter': 'Newton', + 'discr': 'BDF', + 'rtol': rtol, + 'atol': atol, + 'store_event_points': True + } + +if debug_solver: + opts["logging"] = True #<- Turn on solver debug logging +mod.set("_log_level", 6) + +###################################################################### +# Simulate +res = mod.simulate(options=opts) +# logging.error(traceback.format_exc()) + +# plt.plot(res['time'], res['x1']) +# plt.plot(res['time'], res['x2']) +# plt.xlabel('time in [s]') +# plt.ylabel('line2.y') +# plt.grid() +# plt.show() +# plt.savefig("plot.pdf") + +###################################################################### +# Copy style sheets. +# This is a hack to get the css and js files to render the html diagnostics. +htm_dir = os.path.splitext(os.path.basename(fmu_name))[0] + "_html_diagnostics" +if os.path.exists(htm_dir): + for fil in ["scripts.js", "style.css", "zepto.min.js"]: + src = os.path.join(".jmodelica_html", fil) + if os.path.exists(src): + des = os.path.join(htm_dir, fil) + shutil.copyfile(src, des) + +###################################################################### +# Get debugging information +if debug_solver: + #Load the debug information + from pyfmi.debug import CVodeDebugInformation + debug = CVodeDebugInformation(model.replace(".", "_")+"_debug.txt") + + ### Below are options to plot the order, error and step-size evolution. + ### The error methos also take a threshold and a region if you want to + ### limit the plot to a certain interval. + + #Plot order evolution + debug.plot_order() + + #Plot error evolution + debug.plot_error() #Note see also the arguments to the method + + #Plot the used step-size + debug.plot_step_size() + + #See also debug? From 493e5b3d285e8d8b371777a087e4fd1254b0a541 Mon Sep 17 00:00:00 2001 From: goldcoder01 Date: Tue, 10 Dec 2019 15:14:11 -0700 Subject: [PATCH 20/62] add a template for ets --- .../model_connectors/ETS_template.py | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 geojson_modelica_translator/model_connectors/ETS_template.py diff --git a/geojson_modelica_translator/model_connectors/ETS_template.py b/geojson_modelica_translator/model_connectors/ETS_template.py new file mode 100644 index 000000000..97bb15efc --- /dev/null +++ b/geojson_modelica_translator/model_connectors/ETS_template.py @@ -0,0 +1,53 @@ +import os +import shutil +import json + +from jinja2 import FileSystemLoader, Environment + +from geojson_modelica_translator.model_connectors.base import Base as model_connector_base +from geojson_modelica_translator.modelica.input_parser import PackageParser +from geojson_modelica_translator.utils import ModelicaPath + +class ETS_template(): + def __init__(self, ets_geojsoni, building_modelica): + self.text = "ets_geojson preserves json data of ETS; + building_modelica holds building-only data in modelica; + i need to add ETS to the building_modelica." + self.ets_geojson = ets_geojson + self.building_modelica = building_modelica + + def check_ETS_geojson(self): + '''check if ETS info are in geojson file''' + data = json.read(self.ets_geojson) + pass + + def check_AirTerminal(self): + '''check if building has terminal or not''' + '''Currently i just use a aggregated heating/cooling load''' + pass + + def check_ETS_supply(self): + '''check the supply port of ETS''' + pass + + def check_ETS_return(self): + '''check the return port of ETS''' + pass + + def check_building(self): + '''check the supply/return port of building''' + pass + + def template(self): + '''convert ETS json to modelica''' + pass + + def connect(self): + '''connect ETS-modelica to building-modelica''' + pass + + + + + + From 6344e6eee4bf0e218e5cbe71e08b839855b29f5f Mon Sep 17 00:00:00 2001 From: goldcoder01 Date: Wed, 11 Dec 2019 14:49:22 -0700 Subject: [PATCH 21/62] template ETS modelica model (IndirectCooling) --- .../model_connectors/ETS_template.py | 53 ----------- .../model_connectors/ets_template.py | 94 +++++++++++++++++++ .../system_parameters/schema.json | 4 +- 3 files changed, 96 insertions(+), 55 deletions(-) delete mode 100644 geojson_modelica_translator/model_connectors/ETS_template.py create mode 100644 geojson_modelica_translator/model_connectors/ets_template.py diff --git a/geojson_modelica_translator/model_connectors/ETS_template.py b/geojson_modelica_translator/model_connectors/ETS_template.py deleted file mode 100644 index 97bb15efc..000000000 --- a/geojson_modelica_translator/model_connectors/ETS_template.py +++ /dev/null @@ -1,53 +0,0 @@ -import os -import shutil -import json - -from jinja2 import FileSystemLoader, Environment - -from geojson_modelica_translator.model_connectors.base import Base as model_connector_base -from geojson_modelica_translator.modelica.input_parser import PackageParser -from geojson_modelica_translator.utils import ModelicaPath - -class ETS_template(): - def __init__(self, ets_geojsoni, building_modelica): - self.text = "ets_geojson preserves json data of ETS; - building_modelica holds building-only data in modelica; - i need to add ETS to the building_modelica." - self.ets_geojson = ets_geojson - self.building_modelica = building_modelica - - def check_ETS_geojson(self): - '''check if ETS info are in geojson file''' - data = json.read(self.ets_geojson) - pass - - def check_AirTerminal(self): - '''check if building has terminal or not''' - '''Currently i just use a aggregated heating/cooling load''' - pass - - def check_ETS_supply(self): - '''check the supply port of ETS''' - pass - - def check_ETS_return(self): - '''check the return port of ETS''' - pass - - def check_building(self): - '''check the supply/return port of building''' - pass - - def template(self): - '''convert ETS json to modelica''' - pass - - def connect(self): - '''connect ETS-modelica to building-modelica''' - pass - - - - - - diff --git a/geojson_modelica_translator/model_connectors/ets_template.py b/geojson_modelica_translator/model_connectors/ets_template.py new file mode 100644 index 000000000..4f95e4540 --- /dev/null +++ b/geojson_modelica_translator/model_connectors/ets_template.py @@ -0,0 +1,94 @@ +import os +import shutil +import json + +from jinja2 import FileSystemLoader, Environment + +from geojson_modelica_translator.model_connectors.base import Base as model_connector_base +from geojson_modelica_translator.modelica.input_parser import PackageParser +from geojson_modelica_translator.utils import ModelicaPath + +class ETS_Template(): + '''This class will template the ETS modelica model.''' + def __init__(self, thermal_junction_properties_geojson, system_parameters_geojson, ets_from_building_modelica): + ''' + thermal_junction_properties_geojson contains the ETS at brief and at higher level; + system_parameters_geojson contains the ETS with details ; + ets_from_building_modelica contains the modelica model of ETS ; + ''' + self.thermal_junction_properties_geojson = thermal_junction_properties_geojson + self.system_parameters_geojson = system_parameters_geojson + self.ets_from_building_modelica = ets_from_building_modelica + print ("you are good here!!!!") + + def check_ets_thermal_junction(self): + '''check if ETS info are in thermal-junction-geojson file''' + with open(self.thermal_junction_properties_geojson,'r') as f: + data = json.load(f) + + ets_general = False + for key, value in data.items(): + if key == 'definitions': + # three levels down to get the ETS signal + junctions = data["definitions"]["ThermalJunctionType"]["enum"] + if 'ETS' in junctions: + ets_general=True + print ("ETS is there!!!") + else: + pass + + return ets_general + + def check_system_parameters(self): + '''check detailed parameters of ETS''' + with open(self.system_parameters_geojson, 'r') as f: + data = json.load(f) + + ets_details=False + for key, value in data.items(): + #print (key, " <==> " ,value) + # four levels down to get the details + ets_details = data["definitions"]["building_def"]["properties"]["ets"] + print (ets_details) + if ets_details: + print ("ETS details are here!!!") + + return ets_details + + def check_ets_from_building_modelica(self): + '''check if ETS-indirectCooling are in modelica building library''' + ets_modelica_available = os.path.isfile(self.ets_from_building_modelica) + print ("ets-available: ", ets_modelica_available) + + return ets_modelica_available + + def to_modelica(self): + '''convert ETS json to modelica''' + ets_modelica="" + if self.check_ets_from_building_modelica(): + with open(self.ets_from_building_modelica) as f: + ets_modelica = f.read() + print ( ets_modelica ) + else: + pass + + return ets_modelica + + def connect(self): + '''connect ETS-modelica to building-modelica (specifically TEASER modelica)''' + pass + + +###################################################################################################################### +###### For local test only ###### +###################################################################################################################### +thermal_junction_properties_geojson = "/home/mindcoder/geojson-modelica-translator/geojson_modelica_translator/geojson/data/schemas/thermal_junction_properties.json" +system_parameters_geojson = "/home/mindcoder/geojson-modelica-translator/geojson_modelica_translator/system_parameters/schema.json" +ets_from_building_modelica = "/home/mindcoder/geojson-modelica-translator/geojson_modelica_translator/modelica/buildingslibrary/Buildings/Applications/DHC/EnergyTransferStations/CoolingIndirect.mo" +print ( os.getcwd() ) +ets = ETS_Template(thermal_junction_properties_geojson, system_parameters_geojson, ets_from_building_modelica ) +ets.check_ets_thermal_junction() +ets.check_system_parameters() +ets.check_ets_from_building_modelica() +ets.to_modelica() + diff --git a/geojson_modelica_translator/system_parameters/schema.json b/geojson_modelica_translator/system_parameters/schema.json index a2139e9bb..db5e3c110 100755 --- a/geojson_modelica_translator/system_parameters/schema.json +++ b/geojson_modelica_translator/system_parameters/schema.json @@ -154,11 +154,11 @@ }, "ets": { "title": "ets", - "description": "ETS connection information", + "description": "energy transfer station model, one side is connected with district water loops, and the other side is connected with building water loops", "type": "object", "properties": { "system": { - "description": "Type of ETS system that the building is connected to.", + "description": "Indirect cooling ETS. The energy transfer is implemented through nondirect contact of water loops. ", "type": "string", "enum": [ "Booster Heater", From e3f3153dc0553dd021350f5049a5b72c030f0dc2 Mon Sep 17 00:00:00 2001 From: goldcoder01 Date: Wed, 11 Dec 2019 14:54:58 -0700 Subject: [PATCH 22/62] update README --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index 545718cb9..af4043b47 100644 --- a/README.rst +++ b/README.rst @@ -74,6 +74,7 @@ will automatically run the models without having to follow the steps below. * Copy jmodelica.py (from docker-ubuntu-jmodelica) to root of project where you will simulate (e.g., geojson-modelica-translator/tests/model_connectors/output) * Pull https://github.com/lbl-srg/modelica-buildings/tree/issue1442_loadCoupling * **Make sure you have git-lfs installed**. You may need to checkout out the library again after install lfs. + * Please make sure you are in the issue1442_loadCoupling branch. * Mac: `brew install git-lfs; git lfs install` * Ubuntu: `sudo apt install git-lfs; git lfs install` * Add the Buildings Library path to your MODELICAPATH environment variable (e.g., export MODELICAPATH=${MODELICAPATH}:/home//github/modelica-buildings). From 3143a52a991fe0f17f633c0c757273ed5f902d70 Mon Sep 17 00:00:00 2001 From: goldcoder01 Date: Wed, 11 Dec 2019 16:54:58 -0700 Subject: [PATCH 23/62] add testcase for ets_template --- .../model_connectors/ets_template.py | 7 ++-- tests/model_connectors/test_ets.py | 35 +++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 tests/model_connectors/test_ets.py diff --git a/geojson_modelica_translator/model_connectors/ets_template.py b/geojson_modelica_translator/model_connectors/ets_template.py index 4f95e4540..52568fd3d 100644 --- a/geojson_modelica_translator/model_connectors/ets_template.py +++ b/geojson_modelica_translator/model_connectors/ets_template.py @@ -58,7 +58,7 @@ def check_system_parameters(self): def check_ets_from_building_modelica(self): '''check if ETS-indirectCooling are in modelica building library''' ets_modelica_available = os.path.isfile(self.ets_from_building_modelica) - print ("ets-available: ", ets_modelica_available) + #print ("ets-available: ", ets_modelica_available) return ets_modelica_available @@ -68,7 +68,7 @@ def to_modelica(self): if self.check_ets_from_building_modelica(): with open(self.ets_from_building_modelica) as f: ets_modelica = f.read() - print ( ets_modelica ) + #print ( ets_modelica ) else: pass @@ -82,6 +82,7 @@ def connect(self): ###################################################################################################################### ###### For local test only ###### ###################################################################################################################### +''' thermal_junction_properties_geojson = "/home/mindcoder/geojson-modelica-translator/geojson_modelica_translator/geojson/data/schemas/thermal_junction_properties.json" system_parameters_geojson = "/home/mindcoder/geojson-modelica-translator/geojson_modelica_translator/system_parameters/schema.json" ets_from_building_modelica = "/home/mindcoder/geojson-modelica-translator/geojson_modelica_translator/modelica/buildingslibrary/Buildings/Applications/DHC/EnergyTransferStations/CoolingIndirect.mo" @@ -91,4 +92,4 @@ def connect(self): ets.check_system_parameters() ets.check_ets_from_building_modelica() ets.to_modelica() - +''' diff --git a/tests/model_connectors/test_ets.py b/tests/model_connectors/test_ets.py new file mode 100644 index 000000000..c52380bc2 --- /dev/null +++ b/tests/model_connectors/test_ets.py @@ -0,0 +1,35 @@ +from ..context import geojson_modelica_translator # noqa - Do not remove this line + +import unittest +from geojson_modelica_translator.model_connectors.ets_template import ETS_Template + + +class ETS_ModelConnectorSingleBuildingTest(unittest.TestCase): + def setUp(self): # the first method/member must be setUp + self.thermal_junction_properties_geojson = "/home/mindcoder/geojson-modelica-translator/geojson_modelica_translator/geojson/data/schemas/thermal_junction_properties.json" + self.system_parameters_geojson = "/home/mindcoder/geojson-modelica-translator/geojson_modelica_translator/system_parameters/schema.json" + self.ets_from_building_modelica = "/home/mindcoder/geojson-modelica-translator/geojson_modelica_translator/modelica/buildingslibrary/Buildings/Applications/DHC/EnergyTransferStations/CoolingIndirect.mo" + + self.ets = ETS_Template(self.thermal_junction_properties_geojson, self.system_parameters_geojson, self.ets_from_building_modelica) + self.assertIsNotNone(self.ets) + + return self.ets + + + def test_ets_thermal_junction(self): + ets_general = self.ets.check_ets_thermal_junction() + #print ("yanfei: ", ets_general) + self.assertTrue( ets_general ) + + def test_ets_system_parameters(self): + self.assertTrue( self.ets.check_system_parameters() ) + + def test_ets_from_building_modelica(self): + self.assertTrue( self.ets.check_ets_from_building_modelica() ) + + def test_ets_to_modelica(self): + self.assertTrue( self.ets.to_modelica() ) + + +if __name__ == '__main__': + unittest.main() From df776d0cb6917d5c78e4e9844e808aba613bea3c Mon Sep 17 00:00:00 2001 From: goldcoder01 Date: Thu, 12 Dec 2019 10:04:06 -0700 Subject: [PATCH 24/62] update testcase for ETS, to make it more general for users --- tests/model_connectors/test_ets.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/model_connectors/test_ets.py b/tests/model_connectors/test_ets.py index c52380bc2..80fe2b936 100644 --- a/tests/model_connectors/test_ets.py +++ b/tests/model_connectors/test_ets.py @@ -2,13 +2,14 @@ import unittest from geojson_modelica_translator.model_connectors.ets_template import ETS_Template - +import os class ETS_ModelConnectorSingleBuildingTest(unittest.TestCase): def setUp(self): # the first method/member must be setUp - self.thermal_junction_properties_geojson = "/home/mindcoder/geojson-modelica-translator/geojson_modelica_translator/geojson/data/schemas/thermal_junction_properties.json" - self.system_parameters_geojson = "/home/mindcoder/geojson-modelica-translator/geojson_modelica_translator/system_parameters/schema.json" - self.ets_from_building_modelica = "/home/mindcoder/geojson-modelica-translator/geojson_modelica_translator/modelica/buildingslibrary/Buildings/Applications/DHC/EnergyTransferStations/CoolingIndirect.mo" + folder_base= os.getcwd() + self.thermal_junction_properties_geojson = folder_base + "/geojson_modelica_translator/geojson/data/schemas/thermal_junction_properties.json" + self.system_parameters_geojson = folder_base + "/geojson_modelica_translator/system_parameters/schema.json" + self.ets_from_building_modelica = folder_base + "/geojson_modelica_translator/modelica/buildingslibrary/Buildings/Applications/DHC/EnergyTransferStations/CoolingIndirect.mo" self.ets = ETS_Template(self.thermal_junction_properties_geojson, self.system_parameters_geojson, self.ets_from_building_modelica) self.assertIsNotNone(self.ets) @@ -28,7 +29,7 @@ def test_ets_from_building_modelica(self): self.assertTrue( self.ets.check_ets_from_building_modelica() ) def test_ets_to_modelica(self): - self.assertTrue( self.ets.to_modelica() ) + self.assertIsNotNone( self.ets.to_modelica() ) if __name__ == '__main__': From 63035aca4b7c8726c35351c8606d81c8b67dc49e Mon Sep 17 00:00:00 2001 From: YanfeiNREL Date: Mon, 23 Dec 2019 12:44:29 -0700 Subject: [PATCH 25/62] update readme for testing using python3 from Ubuntu18.04 command line --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index af4043b47..19aca3cd7 100644 --- a/README.rst +++ b/README.rst @@ -24,9 +24,9 @@ currently you need python3 and pip3 to install/build the packages. .. code-block:: bash - pip install -r requirements.txt - python setup.py build - py.test + pip3 install -r requirements.txt + python3 setup.py build + python3 setup.py test The py.test tests should all pass assuming the libraries are installed correctly on your development computer. Also, there will be a set of Modelica models that are created and persisted into the `tests/output` folder. From 98a90f0eb0e14a745c9ea0838538c9d5ac84fb1d Mon Sep 17 00:00:00 2001 From: YanfeiNREL Date: Mon, 23 Dec 2019 13:00:30 -0700 Subject: [PATCH 26/62] add ets model: CoolingIndirect, to the buildings folder for ets test --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e6f301d64..8d69ff36a 100644 --- a/.gitignore +++ b/.gitignore @@ -95,4 +95,4 @@ ENV/ tests/output tests/modelica/output tests/model_connectors/output -/geojson_modelica_translator/modelica/buildingslibrary/ +#/geojson_modelica_translator/modelica/buildingslibrary/ From c049fb98a098d0034018d37f3153a4afbada22c3 Mon Sep 17 00:00:00 2001 From: YanfeiNREL Date: Mon, 23 Dec 2019 14:45:36 -0700 Subject: [PATCH 27/62] add a simple template for ets model, by varying key ets parameters --- .../model_connectors/ets_cooling_indirect.mo | 352 +++++++++++++++++ .../model_connectors/ets_template.py | 50 ++- .../templates/CoolingIndirect.mot | 363 ++++++++++++++++++ 3 files changed, 757 insertions(+), 8 deletions(-) create mode 100644 geojson_modelica_translator/model_connectors/ets_cooling_indirect.mo create mode 100644 geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot diff --git a/geojson_modelica_translator/model_connectors/ets_cooling_indirect.mo b/geojson_modelica_translator/model_connectors/ets_cooling_indirect.mo new file mode 100644 index 000000000..b9d052303 --- /dev/null +++ b/geojson_modelica_translator/model_connectors/ets_cooling_indirect.mo @@ -0,0 +1,352 @@ + +within Buildings.Applications.DHC.EnergyTransferStations; +model CoolingIndirect + + "Indirect cooling energy transfer station for district energy systems" + extends Buildings.Fluid.Interfaces.PartialFourPort( + redeclare final package Medium1 = Medium, + redeclare final package Medium2 = Medium); + + replaceable package Medium = + Modelica.Media.Interfaces.PartialMedium "Medium in the component"; + + + + // mass flow rates + parameter Modelica.SIunits.MassFlowRate m1_flow_nominal( + final min=0, + start=0.666) + "Nominal mass flow rate of primary (district) district cooling side"; + + + + + + + + // Heat exchanger + parameter Modelica.SIunits.PressureDifference dp1_nominal( + final min=0, + start=999, + final displayUnit="Pa") + "Nominal pressure difference on primary side" + annotation(Dialog(group="Heat exchanger")); + + + + parameter Modelica.SIunits.PressureDifference dp2_nominal( + final min=0, + start=999, + final displayUnit="Pa") + "Nominal pressure difference on secondary side" + annotation(Dialog(group="Heat exchanger")); + + + + parameter Boolean use_Q_flow_nominal=true + "Set to true to specify Q_flow_nominal and temperatures, or to false to specify effectiveness" + annotation(Dialog(group="Heat exchanger")); + + + + parameter Modelica.SIunits.HeatFlowRate Q_flow_nominal( + final min=0, + start=8000) + "Nominal heat transfer" + annotation(Dialog(group="Heat exchanger")); + + + + + parameter Modelica.SIunits.Temperature T_a1_nominal( + min=0+273, + max=100+273.15, + start=5+273.15, + final displayUnit="K") + "Nominal temperature at port a1" + annotation(Dialog(group="Heat exchanger")); + parameter Modelica.SIunits.Temperature T_a2_nominal( + min=0+273, + max=100+273.15, + start=7+273.15, + final displayUnit="K") + "Nominal temperature at port a2" + annotation(Dialog(group="Heat exchanger")); + + + + parameter Modelica.SIunits.Efficiency eta( + final min=0, + final max=1)=0.666 + "Constant effectiveness" + annotation(Dialog(group="Heat exchanger")); + + + + // Controller parameters + parameter Modelica.Blocks.Types.SimpleController controllerType= + Modelica.Blocks.Types.SimpleController.PI + "Type of controller" + annotation(Dialog(tab="Controller")); + parameter Real k(final min=0, final unit="1") = 1 + "Gain of controller" + annotation(Dialog(tab="Controller")); + parameter Modelica.SIunits.Time Ti( + min=Modelica.Constants.small)=120 + "Time constant of integrator block" + annotation (Dialog(tab="Controller", enable= + controllerType == Modelica.Blocks.Types.SimpleController.PI or + controllerType == Modelica.Blocks.Types.SimpleController.PID)); + parameter Modelica.SIunits.Time Td(final min=0)=0.1 + "Time constant of derivative block" + annotation (Dialog(tab="Controller", enable= + controllerType == Modelica.Blocks.Types.SimpleController.PD or + controllerType == Modelica.Blocks.Types.SimpleController.PID)); + parameter Real yMax(start=1)=1 + "Upper limit of output" + annotation(Dialog(tab="Controller")); + parameter Real yMin=0 + "Lower limit of output" + annotation(Dialog(tab="Controller")); + parameter Real wp(final min=0) = 1 + "Set-point weight for Proportional block (0..1)" + annotation(Dialog(tab="Controller")); + parameter Real wd(final min=0) = 0 + "Set-point weight for Derivative block (0..1)" + annotation(Dialog(tab="Controller", enable= + controllerType==Modelica.Blocks.Types.SimpleController.PD or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real Ni(min=100*Modelica.Constants.eps) = 0.9 + "Ni*Ti is time constant of anti-windup compensation" + annotation(Dialog(tab="Controller", enable= + controllerType==Modelica.Blocks.Types.SimpleController.PI or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real Nd(min=100*Modelica.Constants.eps) = 10 + "The higher Nd, the more ideal the derivative block" + annotation(Dialog(tab="Controller", enable= + controllerType==Modelica.Blocks.Types.SimpleController.PD or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Modelica.Blocks.Types.InitPID initType= + Modelica.Blocks.Types.InitPID.DoNotUse_InitialIntegratorState + "Type of initialization (1: no init, 2: steady state, 3: initial state, 4: initial output)" + annotation(Evaluate=true, Dialog(group="Initialization", tab="Controller")); + parameter Real xi_start=0 + "Initial or guess value value for integrator output (= integrator state)" + annotation (Dialog(group="Initialization", tab="Controller", + enable=controllerType==Modelica.Blocks.Types.SimpleController.PI or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real xd_start=0 + "Initial or guess value for state of derivative block" + annotation (Dialog(group="Initialization", tab="Controller", + enable=controllerType==Modelica.Blocks.Types.SimpleController.PD or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real yCon_start=0 + "Initial value of output from the controller" + annotation(Dialog(group="Initialization", tab="Controller", + enable=initType == Modelica.Blocks.Types.InitPID.InitialOutput)); + parameter Boolean reverseAction = true + "Set to true for throttling the water flow rate through a cooling coil controller" + annotation(Dialog(tab="Controller")); + + Modelica.Blocks.Interfaces.RealInput TSet( + final quantity="ThermodynamicTemperature", + final unit="K") + "Setpoint temperature" + annotation (Placement(transformation(extent={{-140,-20},{-100,20}}))); + + Modelica.Blocks.Interfaces.RealOutput Q_flow( + final quantity="Power", + final unit="W", + final displayUnit="kW") + "Measured power demand at the ETS" + annotation (Placement(transformation(extent={{100,140},{120,160}}))); + + Modelica.Blocks.Interfaces.RealOutput Q( + final quantity="Energy", + final unit="J", + final displayUnit="kWh") + "Measured energy consumption at the ETS" + annotation (Placement(transformation(extent={{100,100},{120,120}}))); + + Buildings.Fluid.HeatExchangers.PlateHeatExchangerEffectivenessNTU hex( + redeclare final package Medium1 = Medium, + redeclare final package Medium2 = Medium, + final m1_flow_nominal=m1_flow_nominal, + final m2_flow_nominal=m2_flow_nominal, + final dp1_nominal=dp1_nominal, + final dp2_nominal=dp2_nominal, + final configuration=Buildings.Fluid.Types.HeatExchangerConfiguration.CounterFlow, + final use_Q_flow_nominal=true, + final Q_flow_nominal=Q_flow_nominal, + final T_a1_nominal=T_a1_nominal, + final T_a2_nominal=T_a2_nominal) "Indirect cooling heat exchanger" + annotation (Placement(transformation(extent={{20,-10},{40,10}}))); + + Buildings.Controls.Continuous.LimPID con( + final controllerType=Modelica.Blocks.Types.SimpleController.PID, + final k=k, + final Td=Td, + final yMax=yMax, + final yMin=yMin, + final Ti=Ti, + final wp=wp, + final wd=wd, + final Ni=Ni, + final Nd=Nd, + final initType=Modelica.Blocks.Types.InitPID.InitialOutput, + final xi_start=xi_start, + final xd_start=xd_start, + final y_start=yCon_start, + final reverseAction=reverseAction) "Controller" + annotation (Placement(transformation(extent={{-90,-10},{-70,10}}))); + + Buildings.Fluid.Sensors.TemperatureTwoPort senTDisSup( + redeclare final package Medium = Medium, + final m_flow_nominal=m1_flow_nominal) + "District-side (primary) supply temperature sensor" + annotation (Placement(transformation(extent={{-90,50},{-70,70}}))); + + Buildings.Fluid.Sensors.TemperatureTwoPort senTDisRet( + redeclare final package Medium = Medium, + final m_flow_nominal=m1_flow_nominal) + "District-side (primary) return temperature sensor" + annotation (Placement(transformation(extent={{70,50},{90,70}}))); + + Modelica.Blocks.Continuous.Integrator int(k=1) "Integration" + annotation (Placement(transformation(extent={{60,120},{80,100}}))); + + Buildings.Fluid.Sensors.MassFlowRate senMasFlo( + redeclare package Medium = Medium) + annotation (Placement(transformation(extent={{-60,50},{-40,70}}))); + + Buildings.Fluid.Sensors.TemperatureTwoPort TBuiRet( + redeclare final package Medium = Medium, + final m_flow_nominal=m2_flow_nominal) + "Building-side (secondary) return temperature" + annotation (Placement(transformation(extent={{-70,-70},{-90,-50}}))); + + Buildings.Fluid.Actuators.Valves.TwoWayQuickOpening val( + redeclare final package Medium = Medium, + final m_flow_nominal=m1_flow_nominal, + final dpValve_nominal=dpValve_nominal, + riseTime(displayUnit="s") = 60, + y_start=0) "District-side (primary) control valve" + annotation (Placement(transformation(extent={{-30,70},{-10,50}}))); + + Modelica.Blocks.Math.Gain cp(final k=cp_default) + "Specifc heat multiplier to calculate heat flow rate" + annotation (Placement(transformation(extent={{20,100},{40,120}}))); + + Modelica.Blocks.Math.Product pro "Product" + annotation (Placement(transformation(extent={{-20,100},{0,120}}))); + + Modelica.Blocks.Math.Add dTDis(k1=-1, k2=+1) + "Temperatur difference on the district side" + annotation (Placement(transformation(extent={{-60,106},{-40,126}}))); + +protected + final parameter Medium.ThermodynamicState sta_default = Medium.setState_pTX( + T=Medium.T_default, + p=Medium.p_default, + X=Medium.X_default) "Medium state at default properties"; + final parameter Modelica.SIunits.SpecificHeatCapacity cp_default= + Medium.specificHeatCapacityCp(sta_default) + "Specific heat capacity of the fluid"; + +equation + + connect(hex.port_a2, port_a2) annotation (Line(points={{40,-6},{60,-6},{60,-60}, + {100,-60}}, color={0,127,255})); + connect(hex.port_b1, senTDisRet.port_a) annotation (Line(points={{40,6},{60,6}, + {60,60},{70,60}}, color={0,127,255})); + connect(val.port_b, hex.port_a1) annotation (Line(points={{-10,60},{0,60},{0,6}, + {20,6}}, color={0,127,255})); + connect(senMasFlo.port_b, val.port_a) + annotation (Line(points={{-40,60},{-30,60}}, color={0,127,255})); + connect(port_a1, senTDisSup.port_a) + annotation (Line(points={{-100,60},{-90,60}}, color={0,127,255})); + connect(senTDisSup.port_b, senMasFlo.port_a) + annotation (Line(points={{-70,60},{-60,60}}, color={0,127,255})); + connect(port_b2, TBuiRet.port_b) + annotation (Line(points={{-100,-60},{-90,-60}}, color={0,127,255})); + connect(senTDisRet.port_b, port_b1) + annotation (Line(points={{90,60},{100,60}}, color={0,127,255})); + connect(TSet, con.u_s) + annotation (Line(points={{-120,0},{-106,0},{-106,0},{-92,0}}, + color={0,0,127})); + connect(con.u_m, TBuiRet.T) + annotation (Line(points={{-80,-12},{-80,-49}}, color={0,0,127})); + connect(con.y, val.y) + annotation (Line(points={{-69,0},{-20,0},{-20,48}}, color={0,0,127})); + connect(TBuiRet.port_a, hex.port_b2) annotation (Line(points={{-70,-60},{0,-60}, + {0,-6},{20,-6}}, color={0,127,255})); + connect(pro.y, cp.u) + annotation (Line(points={{1,110},{18,110}}, color={0,0,127})); + connect(senMasFlo.m_flow, pro.u2) + annotation (Line(points={{-50,71},{-50,104},{-22,104}}, color={0,0,127})); + connect(senTDisSup.T, dTDis.u1) + annotation (Line(points={{-80,71},{-80,122},{-62,122}}, color={0,0,127})); + connect(senTDisRet.T, dTDis.u2) annotation (Line(points={{80,71},{80,80},{-70, + 80},{-70,110},{-62,110}}, color={0,0,127})); + connect(dTDis.y, pro.u1) + annotation (Line(points={{-39,116},{-22,116}}, color={0,0,127})); + connect(cp.y, int.u) + annotation (Line(points={{41,110},{58,110}}, color={0,0,127})); + connect(int.y, Q) + annotation (Line(points={{81,110},{110,110}}, color={0,0,127})); + connect(Q_flow, cp.y) annotation (Line(points={{110,150},{50,150},{50,110},{41, + 110}}, color={0,0,127})); + +annotation (defaultComponentName="coo", + Icon(coordinateSystem(preserveAspectRatio=false), graphics={ + Rectangle( + extent={{-100,-56},{100,-64}}, + fillColor={0,0,0}, + fillPattern=FillPattern.Solid, + pattern=LinePattern.None), + Rectangle( + extent={{-100,64},{100,56}}, + fillColor={0,0,0}, + fillPattern=FillPattern.Solid, + pattern=LinePattern.None), + Rectangle( + extent={{-80,80},{80,-80}}, + lineColor={175,175,175}, + fillColor={35,138,255}, + fillPattern=FillPattern.Solid), + Text( + extent={{-52,40},{54,-40}}, + lineColor={0,0,0}, + fillColor={35,138,255}, + fillPattern=FillPattern.Solid, + textStyle={TextStyle.Bold}, + textString="ETS")}), Diagram( + coordinateSystem(preserveAspectRatio=false, extent={{-100,-100},{100,160}})), + Documentation(info=" +

+Indirect cooling energy transfer station (ETS) model that controls +the building chilled water supply temperature by modulating a +primary control valve on the district supply side. The design is +based on a typical district cooling ETS described in ASHRAE's + +District Cooling Guide. +As shown in the figure below, the building pumping design (constant, +variable) is specified on the building side, not within the ETS. +

+

+\"DHC.ETS.CoolingIndirect\"/ +

+

Reference

+

+American Society of Heating, Refrigeration and Air-Conditioning +Engineers. (2013). Chapter 5: End User Interface. In +District Cooling Guide. 1st Edition. +

+", revisions=" +
    +
  • +November 1, 2019, by Kathryn Hinkelman:
    +First implementation.
  • +
+")); +end CoolingIndirect; \ No newline at end of file diff --git a/geojson_modelica_translator/model_connectors/ets_template.py b/geojson_modelica_translator/model_connectors/ets_template.py index 52568fd3d..16c6724c4 100644 --- a/geojson_modelica_translator/model_connectors/ets_template.py +++ b/geojson_modelica_translator/model_connectors/ets_template.py @@ -11,6 +11,7 @@ class ETS_Template(): '''This class will template the ETS modelica model.''' def __init__(self, thermal_junction_properties_geojson, system_parameters_geojson, ets_from_building_modelica): + super().__init__() ''' thermal_junction_properties_geojson contains the ETS at brief and at higher level; system_parameters_geojson contains the ETS with details ; @@ -19,7 +20,15 @@ def __init__(self, thermal_junction_properties_geojson, system_parameters_geojso self.thermal_junction_properties_geojson = thermal_junction_properties_geojson self.system_parameters_geojson = system_parameters_geojson self.ets_from_building_modelica = ets_from_building_modelica - print ("you are good here!!!!") + + #print ( "Yanfei-path: ", os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates') ) + + self.template_env = Environment( + loader=FileSystemLoader(searchpath=os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates')) + ) + + + print ("@@@@@@you are good here!!!!", self.template_env) def check_ets_thermal_junction(self): '''check if ETS info are in thermal-junction-geojson file''' @@ -72,6 +81,31 @@ def to_modelica(self): else: pass + curdir = os.getcwd() + print("Yanfei-2: ", curdir) + #loads_root_path = os.path.join(root_building_dir, project_name, 'Loads') + ets_template = self.template_env.get_template('CoolingIndirect.mot') + #print ("Yanfei-2: ", curdir) + + building_names = [] + ets_data = { + "Q_flow_nominal": [8000], + "eta_efficiency": [0.666], + "NominalFlow_district": [0.666], + "NominalFlow_buiding":[0.666], + "PressureDrop_Valve": [888], + "PressureDrop_HX_Secondary": [999], + "PressureDrop_HX_Primary": [999], + + } + + file_data = ets_template.render( + ets_data=ets_data + ) + + with open(os.path.join(os.path.join(os.getcwd(), 'ets_cooling_indirect.mo')), 'w') as f: + f.write(file_data) + return ets_modelica def connect(self): @@ -82,14 +116,14 @@ def connect(self): ###################################################################################################################### ###### For local test only ###### ###################################################################################################################### -''' -thermal_junction_properties_geojson = "/home/mindcoder/geojson-modelica-translator/geojson_modelica_translator/geojson/data/schemas/thermal_junction_properties.json" -system_parameters_geojson = "/home/mindcoder/geojson-modelica-translator/geojson_modelica_translator/system_parameters/schema.json" -ets_from_building_modelica = "/home/mindcoder/geojson-modelica-translator/geojson_modelica_translator/modelica/buildingslibrary/Buildings/Applications/DHC/EnergyTransferStations/CoolingIndirect.mo" + +thermal_junction_properties_geojson = "/home/Yanfei_Projects/geojson-modelica-translator/geojson_modelica_translator/geojson/data/schemas/thermal_junction_properties.json" +system_parameters_geojson = "/home/Yanfei_Projects/geojson-modelica-translator/geojson_modelica_translator/system_parameters/schema.json" +ets_from_building_modelica = "/home/Yanfei_Projects/geojson-modelica-translator/geojson_modelica_translator/modelica/buildingslibrary/Buildings/Applications/DHC/EnergyTransferStations/CoolingIndirect.mo" print ( os.getcwd() ) ets = ETS_Template(thermal_junction_properties_geojson, system_parameters_geojson, ets_from_building_modelica ) -ets.check_ets_thermal_junction() -ets.check_system_parameters() +#ets.check_ets_thermal_junction() +#ets.check_system_parameters() ets.check_ets_from_building_modelica() ets.to_modelica() -''' + diff --git a/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot b/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot new file mode 100644 index 000000000..67dd97754 --- /dev/null +++ b/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot @@ -0,0 +1,363 @@ + +within Buildings.Applications.DHC.EnergyTransferStations; +model CoolingIndirect +{% raw %} + "Indirect cooling energy transfer station for district energy systems" + extends Buildings.Fluid.Interfaces.PartialFourPort( + redeclare final package Medium1 = Medium, + redeclare final package Medium2 = Medium); + + replaceable package Medium = + Modelica.Media.Interfaces.PartialMedium "Medium in the component"; +{% endraw %} + + {% for mflow_district in ets_data["NominalFlow_district"]%} + // mass flow rates + parameter Modelica.SIunits.MassFlowRate m1_flow_nominal( + final min=0, + start={{mflow_district}}) + "Nominal mass flow rate of primary (district) district cooling side"; + {% endfor %} + + {% for mflow_building in ets_data["NominalFlow_building"]%} + parameter Modelica.SIunits.MassFlowRate m2_flow_nominal( + final min=0, + start=0.5) + "Nominal mass flow rate of secondary (building) district cooling side"; + {% endfor %} + + {% for dp in ets_data["PressureDrop_valve"]%} + // Primary supply control valve + parameter Modelica.SIunits.PressureDifference dpValve_nominal( + final min=0, + final displayUnit="Pa")={{dp}} + "Nominal pressure drop of fully open control valve"; + {% endfor %} + + {% for dp in ets_data["PressureDrop_HX_Primary"]%} + // Heat exchanger + parameter Modelica.SIunits.PressureDifference dp1_nominal( + final min=0, + start={{dp}}, + final displayUnit="Pa") + "Nominal pressure difference on primary side" + annotation(Dialog(group="Heat exchanger")); + {% endfor %} + + {% for dp in ets_data["PressureDrop_HX_Secondary"]%} + parameter Modelica.SIunits.PressureDifference dp2_nominal( + final min=0, + start={{dp}}, + final displayUnit="Pa") + "Nominal pressure difference on secondary side" + annotation(Dialog(group="Heat exchanger")); + {% endfor %} + + {% raw %} + parameter Boolean use_Q_flow_nominal=true + "Set to true to specify Q_flow_nominal and temperatures, or to false to specify effectiveness" + annotation(Dialog(group="Heat exchanger")); + {% endraw %} + + {% for Q_flow in ets_data['Q_flow_nominal'] %} + parameter Modelica.SIunits.HeatFlowRate Q_flow_nominal( + final min=0, + start={{Q_flow}}) + "Nominal heat transfer" + {% raw %}annotation(Dialog(group="Heat exchanger"));{% endraw %} + {% endfor %} + + + {% raw %} + parameter Modelica.SIunits.Temperature T_a1_nominal( + min=0+273, + max=100+273.15, + start=5+273.15, + final displayUnit="K") + "Nominal temperature at port a1" + annotation(Dialog(group="Heat exchanger")); + parameter Modelica.SIunits.Temperature T_a2_nominal( + min=0+273, + max=100+273.15, + start=7+273.15, + final displayUnit="K") + "Nominal temperature at port a2" + annotation(Dialog(group="Heat exchanger")); + {% endraw %} + + {% for efficiency in ets_data['eta_efficiency'] %} + parameter Modelica.SIunits.Efficiency eta( + final min=0, + final max=1)={{efficiency}} + "Constant effectiveness" + annotation(Dialog(group="Heat exchanger")); + {% endfor %} + + {%raw%} + // Controller parameters + parameter Modelica.Blocks.Types.SimpleController controllerType= + Modelica.Blocks.Types.SimpleController.PI + "Type of controller" + annotation(Dialog(tab="Controller")); + parameter Real k(final min=0, final unit="1") = 1 + "Gain of controller" + annotation(Dialog(tab="Controller")); + parameter Modelica.SIunits.Time Ti( + min=Modelica.Constants.small)=120 + "Time constant of integrator block" + annotation (Dialog(tab="Controller", enable= + controllerType == Modelica.Blocks.Types.SimpleController.PI or + controllerType == Modelica.Blocks.Types.SimpleController.PID)); + parameter Modelica.SIunits.Time Td(final min=0)=0.1 + "Time constant of derivative block" + annotation (Dialog(tab="Controller", enable= + controllerType == Modelica.Blocks.Types.SimpleController.PD or + controllerType == Modelica.Blocks.Types.SimpleController.PID)); + parameter Real yMax(start=1)=1 + "Upper limit of output" + annotation(Dialog(tab="Controller")); + parameter Real yMin=0 + "Lower limit of output" + annotation(Dialog(tab="Controller")); + parameter Real wp(final min=0) = 1 + "Set-point weight for Proportional block (0..1)" + annotation(Dialog(tab="Controller")); + parameter Real wd(final min=0) = 0 + "Set-point weight for Derivative block (0..1)" + annotation(Dialog(tab="Controller", enable= + controllerType==Modelica.Blocks.Types.SimpleController.PD or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real Ni(min=100*Modelica.Constants.eps) = 0.9 + "Ni*Ti is time constant of anti-windup compensation" + annotation(Dialog(tab="Controller", enable= + controllerType==Modelica.Blocks.Types.SimpleController.PI or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real Nd(min=100*Modelica.Constants.eps) = 10 + "The higher Nd, the more ideal the derivative block" + annotation(Dialog(tab="Controller", enable= + controllerType==Modelica.Blocks.Types.SimpleController.PD or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Modelica.Blocks.Types.InitPID initType= + Modelica.Blocks.Types.InitPID.DoNotUse_InitialIntegratorState + "Type of initialization (1: no init, 2: steady state, 3: initial state, 4: initial output)" + annotation(Evaluate=true, Dialog(group="Initialization", tab="Controller")); + parameter Real xi_start=0 + "Initial or guess value value for integrator output (= integrator state)" + annotation (Dialog(group="Initialization", tab="Controller", + enable=controllerType==Modelica.Blocks.Types.SimpleController.PI or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real xd_start=0 + "Initial or guess value for state of derivative block" + annotation (Dialog(group="Initialization", tab="Controller", + enable=controllerType==Modelica.Blocks.Types.SimpleController.PD or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real yCon_start=0 + "Initial value of output from the controller" + annotation(Dialog(group="Initialization", tab="Controller", + enable=initType == Modelica.Blocks.Types.InitPID.InitialOutput)); + parameter Boolean reverseAction = true + "Set to true for throttling the water flow rate through a cooling coil controller" + annotation(Dialog(tab="Controller")); + + Modelica.Blocks.Interfaces.RealInput TSet( + final quantity="ThermodynamicTemperature", + final unit="K") + "Setpoint temperature" + annotation (Placement(transformation(extent={{-140,-20},{-100,20}}))); + + Modelica.Blocks.Interfaces.RealOutput Q_flow( + final quantity="Power", + final unit="W", + final displayUnit="kW") + "Measured power demand at the ETS" + annotation (Placement(transformation(extent={{100,140},{120,160}}))); + + Modelica.Blocks.Interfaces.RealOutput Q( + final quantity="Energy", + final unit="J", + final displayUnit="kWh") + "Measured energy consumption at the ETS" + annotation (Placement(transformation(extent={{100,100},{120,120}}))); + + Buildings.Fluid.HeatExchangers.PlateHeatExchangerEffectivenessNTU hex( + redeclare final package Medium1 = Medium, + redeclare final package Medium2 = Medium, + final m1_flow_nominal=m1_flow_nominal, + final m2_flow_nominal=m2_flow_nominal, + final dp1_nominal=dp1_nominal, + final dp2_nominal=dp2_nominal, + final configuration=Buildings.Fluid.Types.HeatExchangerConfiguration.CounterFlow, + final use_Q_flow_nominal=true, + final Q_flow_nominal=Q_flow_nominal, + final T_a1_nominal=T_a1_nominal, + final T_a2_nominal=T_a2_nominal) "Indirect cooling heat exchanger" + annotation (Placement(transformation(extent={{20,-10},{40,10}}))); + + Buildings.Controls.Continuous.LimPID con( + final controllerType=Modelica.Blocks.Types.SimpleController.PID, + final k=k, + final Td=Td, + final yMax=yMax, + final yMin=yMin, + final Ti=Ti, + final wp=wp, + final wd=wd, + final Ni=Ni, + final Nd=Nd, + final initType=Modelica.Blocks.Types.InitPID.InitialOutput, + final xi_start=xi_start, + final xd_start=xd_start, + final y_start=yCon_start, + final reverseAction=reverseAction) "Controller" + annotation (Placement(transformation(extent={{-90,-10},{-70,10}}))); + + Buildings.Fluid.Sensors.TemperatureTwoPort senTDisSup( + redeclare final package Medium = Medium, + final m_flow_nominal=m1_flow_nominal) + "District-side (primary) supply temperature sensor" + annotation (Placement(transformation(extent={{-90,50},{-70,70}}))); + + Buildings.Fluid.Sensors.TemperatureTwoPort senTDisRet( + redeclare final package Medium = Medium, + final m_flow_nominal=m1_flow_nominal) + "District-side (primary) return temperature sensor" + annotation (Placement(transformation(extent={{70,50},{90,70}}))); + + Modelica.Blocks.Continuous.Integrator int(k=1) "Integration" + annotation (Placement(transformation(extent={{60,120},{80,100}}))); + + Buildings.Fluid.Sensors.MassFlowRate senMasFlo( + redeclare package Medium = Medium) + annotation (Placement(transformation(extent={{-60,50},{-40,70}}))); + + Buildings.Fluid.Sensors.TemperatureTwoPort TBuiRet( + redeclare final package Medium = Medium, + final m_flow_nominal=m2_flow_nominal) + "Building-side (secondary) return temperature" + annotation (Placement(transformation(extent={{-70,-70},{-90,-50}}))); + + Buildings.Fluid.Actuators.Valves.TwoWayQuickOpening val( + redeclare final package Medium = Medium, + final m_flow_nominal=m1_flow_nominal, + final dpValve_nominal=dpValve_nominal, + riseTime(displayUnit="s") = 60, + y_start=0) "District-side (primary) control valve" + annotation (Placement(transformation(extent={{-30,70},{-10,50}}))); + + Modelica.Blocks.Math.Gain cp(final k=cp_default) + "Specifc heat multiplier to calculate heat flow rate" + annotation (Placement(transformation(extent={{20,100},{40,120}}))); + + Modelica.Blocks.Math.Product pro "Product" + annotation (Placement(transformation(extent={{-20,100},{0,120}}))); + + Modelica.Blocks.Math.Add dTDis(k1=-1, k2=+1) + "Temperatur difference on the district side" + annotation (Placement(transformation(extent={{-60,106},{-40,126}}))); + +protected + final parameter Medium.ThermodynamicState sta_default = Medium.setState_pTX( + T=Medium.T_default, + p=Medium.p_default, + X=Medium.X_default) "Medium state at default properties"; + final parameter Modelica.SIunits.SpecificHeatCapacity cp_default= + Medium.specificHeatCapacityCp(sta_default) + "Specific heat capacity of the fluid"; + +equation + + connect(hex.port_a2, port_a2) annotation (Line(points={{40,-6},{60,-6},{60,-60}, + {100,-60}}, color={0,127,255})); + connect(hex.port_b1, senTDisRet.port_a) annotation (Line(points={{40,6},{60,6}, + {60,60},{70,60}}, color={0,127,255})); + connect(val.port_b, hex.port_a1) annotation (Line(points={{-10,60},{0,60},{0,6}, + {20,6}}, color={0,127,255})); + connect(senMasFlo.port_b, val.port_a) + annotation (Line(points={{-40,60},{-30,60}}, color={0,127,255})); + connect(port_a1, senTDisSup.port_a) + annotation (Line(points={{-100,60},{-90,60}}, color={0,127,255})); + connect(senTDisSup.port_b, senMasFlo.port_a) + annotation (Line(points={{-70,60},{-60,60}}, color={0,127,255})); + connect(port_b2, TBuiRet.port_b) + annotation (Line(points={{-100,-60},{-90,-60}}, color={0,127,255})); + connect(senTDisRet.port_b, port_b1) + annotation (Line(points={{90,60},{100,60}}, color={0,127,255})); + connect(TSet, con.u_s) + annotation (Line(points={{-120,0},{-106,0},{-106,0},{-92,0}}, + color={0,0,127})); + connect(con.u_m, TBuiRet.T) + annotation (Line(points={{-80,-12},{-80,-49}}, color={0,0,127})); + connect(con.y, val.y) + annotation (Line(points={{-69,0},{-20,0},{-20,48}}, color={0,0,127})); + connect(TBuiRet.port_a, hex.port_b2) annotation (Line(points={{-70,-60},{0,-60}, + {0,-6},{20,-6}}, color={0,127,255})); + connect(pro.y, cp.u) + annotation (Line(points={{1,110},{18,110}}, color={0,0,127})); + connect(senMasFlo.m_flow, pro.u2) + annotation (Line(points={{-50,71},{-50,104},{-22,104}}, color={0,0,127})); + connect(senTDisSup.T, dTDis.u1) + annotation (Line(points={{-80,71},{-80,122},{-62,122}}, color={0,0,127})); + connect(senTDisRet.T, dTDis.u2) annotation (Line(points={{80,71},{80,80},{-70, + 80},{-70,110},{-62,110}}, color={0,0,127})); + connect(dTDis.y, pro.u1) + annotation (Line(points={{-39,116},{-22,116}}, color={0,0,127})); + connect(cp.y, int.u) + annotation (Line(points={{41,110},{58,110}}, color={0,0,127})); + connect(int.y, Q) + annotation (Line(points={{81,110},{110,110}}, color={0,0,127})); + connect(Q_flow, cp.y) annotation (Line(points={{110,150},{50,150},{50,110},{41, + 110}}, color={0,0,127})); + +annotation (defaultComponentName="coo", + Icon(coordinateSystem(preserveAspectRatio=false), graphics={ + Rectangle( + extent={{-100,-56},{100,-64}}, + fillColor={0,0,0}, + fillPattern=FillPattern.Solid, + pattern=LinePattern.None), + Rectangle( + extent={{-100,64},{100,56}}, + fillColor={0,0,0}, + fillPattern=FillPattern.Solid, + pattern=LinePattern.None), + Rectangle( + extent={{-80,80},{80,-80}}, + lineColor={175,175,175}, + fillColor={35,138,255}, + fillPattern=FillPattern.Solid), + Text( + extent={{-52,40},{54,-40}}, + lineColor={0,0,0}, + fillColor={35,138,255}, + fillPattern=FillPattern.Solid, + textStyle={TextStyle.Bold}, + textString="ETS")}), Diagram( + coordinateSystem(preserveAspectRatio=false, extent={{-100,-100},{100,160}})), + Documentation(info=" +

+Indirect cooling energy transfer station (ETS) model that controls +the building chilled water supply temperature by modulating a +primary control valve on the district supply side. The design is +based on a typical district cooling ETS described in ASHRAE's + +District Cooling Guide. +As shown in the figure below, the building pumping design (constant, +variable) is specified on the building side, not within the ETS. +

+

+\"DHC.ETS.CoolingIndirect\"/ +

+

Reference

+

+American Society of Heating, Refrigeration and Air-Conditioning +Engineers. (2013). Chapter 5: End User Interface. In +District Cooling Guide. 1st Edition. +

+", revisions=" +
    +
  • +November 1, 2019, by Kathryn Hinkelman:
    +First implementation.
  • +
+"));{% endraw %} +end CoolingIndirect; \ No newline at end of file From 43542ee531857fb253aeeba5169164355c38ef45 Mon Sep 17 00:00:00 2001 From: YanfeiNREL Date: Thu, 26 Dec 2019 11:42:13 -0700 Subject: [PATCH 28/62] update the ets template by adding more key parameters --- ...ing_indirect.mo => ets_cooling_indirect.mo | 5 +- .../ets_cooling_indirect_yanfei.mo | 355 ++++++++++++++++++ .../model_connectors/ets_template.py | 24 +- .../templates/CoolingIndirect.mot | 13 +- 4 files changed, 384 insertions(+), 13 deletions(-) rename geojson_modelica_translator/model_connectors/ets_cooling_indirect.mo => ets_cooling_indirect.mo (99%) create mode 100644 geojson_modelica_translator/model_connectors/ets_cooling_indirect_yanfei.mo diff --git a/geojson_modelica_translator/model_connectors/ets_cooling_indirect.mo b/ets_cooling_indirect.mo similarity index 99% rename from geojson_modelica_translator/model_connectors/ets_cooling_indirect.mo rename to ets_cooling_indirect.mo index b9d052303..50e2c1612 100644 --- a/geojson_modelica_translator/model_connectors/ets_cooling_indirect.mo +++ b/ets_cooling_indirect.mo @@ -31,7 +31,7 @@ model CoolingIndirect final displayUnit="Pa") "Nominal pressure difference on primary side" annotation(Dialog(group="Heat exchanger")); - + parameter Modelica.SIunits.PressureDifference dp2_nominal( @@ -65,6 +65,9 @@ model CoolingIndirect final displayUnit="K") "Nominal temperature at port a1" annotation(Dialog(group="Heat exchanger")); + + + parameter Modelica.SIunits.Temperature T_a2_nominal( min=0+273, max=100+273.15, diff --git a/geojson_modelica_translator/model_connectors/ets_cooling_indirect_yanfei.mo b/geojson_modelica_translator/model_connectors/ets_cooling_indirect_yanfei.mo new file mode 100644 index 000000000..50e2c1612 --- /dev/null +++ b/geojson_modelica_translator/model_connectors/ets_cooling_indirect_yanfei.mo @@ -0,0 +1,355 @@ + +within Buildings.Applications.DHC.EnergyTransferStations; +model CoolingIndirect + + "Indirect cooling energy transfer station for district energy systems" + extends Buildings.Fluid.Interfaces.PartialFourPort( + redeclare final package Medium1 = Medium, + redeclare final package Medium2 = Medium); + + replaceable package Medium = + Modelica.Media.Interfaces.PartialMedium "Medium in the component"; + + + + // mass flow rates + parameter Modelica.SIunits.MassFlowRate m1_flow_nominal( + final min=0, + start=0.666) + "Nominal mass flow rate of primary (district) district cooling side"; + + + + + + + + // Heat exchanger + parameter Modelica.SIunits.PressureDifference dp1_nominal( + final min=0, + start=999, + final displayUnit="Pa") + "Nominal pressure difference on primary side" + annotation(Dialog(group="Heat exchanger")); + + + + parameter Modelica.SIunits.PressureDifference dp2_nominal( + final min=0, + start=999, + final displayUnit="Pa") + "Nominal pressure difference on secondary side" + annotation(Dialog(group="Heat exchanger")); + + + + parameter Boolean use_Q_flow_nominal=true + "Set to true to specify Q_flow_nominal and temperatures, or to false to specify effectiveness" + annotation(Dialog(group="Heat exchanger")); + + + + parameter Modelica.SIunits.HeatFlowRate Q_flow_nominal( + final min=0, + start=8000) + "Nominal heat transfer" + annotation(Dialog(group="Heat exchanger")); + + + + + parameter Modelica.SIunits.Temperature T_a1_nominal( + min=0+273, + max=100+273.15, + start=5+273.15, + final displayUnit="K") + "Nominal temperature at port a1" + annotation(Dialog(group="Heat exchanger")); + + + + parameter Modelica.SIunits.Temperature T_a2_nominal( + min=0+273, + max=100+273.15, + start=7+273.15, + final displayUnit="K") + "Nominal temperature at port a2" + annotation(Dialog(group="Heat exchanger")); + + + + parameter Modelica.SIunits.Efficiency eta( + final min=0, + final max=1)=0.666 + "Constant effectiveness" + annotation(Dialog(group="Heat exchanger")); + + + + // Controller parameters + parameter Modelica.Blocks.Types.SimpleController controllerType= + Modelica.Blocks.Types.SimpleController.PI + "Type of controller" + annotation(Dialog(tab="Controller")); + parameter Real k(final min=0, final unit="1") = 1 + "Gain of controller" + annotation(Dialog(tab="Controller")); + parameter Modelica.SIunits.Time Ti( + min=Modelica.Constants.small)=120 + "Time constant of integrator block" + annotation (Dialog(tab="Controller", enable= + controllerType == Modelica.Blocks.Types.SimpleController.PI or + controllerType == Modelica.Blocks.Types.SimpleController.PID)); + parameter Modelica.SIunits.Time Td(final min=0)=0.1 + "Time constant of derivative block" + annotation (Dialog(tab="Controller", enable= + controllerType == Modelica.Blocks.Types.SimpleController.PD or + controllerType == Modelica.Blocks.Types.SimpleController.PID)); + parameter Real yMax(start=1)=1 + "Upper limit of output" + annotation(Dialog(tab="Controller")); + parameter Real yMin=0 + "Lower limit of output" + annotation(Dialog(tab="Controller")); + parameter Real wp(final min=0) = 1 + "Set-point weight for Proportional block (0..1)" + annotation(Dialog(tab="Controller")); + parameter Real wd(final min=0) = 0 + "Set-point weight for Derivative block (0..1)" + annotation(Dialog(tab="Controller", enable= + controllerType==Modelica.Blocks.Types.SimpleController.PD or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real Ni(min=100*Modelica.Constants.eps) = 0.9 + "Ni*Ti is time constant of anti-windup compensation" + annotation(Dialog(tab="Controller", enable= + controllerType==Modelica.Blocks.Types.SimpleController.PI or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real Nd(min=100*Modelica.Constants.eps) = 10 + "The higher Nd, the more ideal the derivative block" + annotation(Dialog(tab="Controller", enable= + controllerType==Modelica.Blocks.Types.SimpleController.PD or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Modelica.Blocks.Types.InitPID initType= + Modelica.Blocks.Types.InitPID.DoNotUse_InitialIntegratorState + "Type of initialization (1: no init, 2: steady state, 3: initial state, 4: initial output)" + annotation(Evaluate=true, Dialog(group="Initialization", tab="Controller")); + parameter Real xi_start=0 + "Initial or guess value value for integrator output (= integrator state)" + annotation (Dialog(group="Initialization", tab="Controller", + enable=controllerType==Modelica.Blocks.Types.SimpleController.PI or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real xd_start=0 + "Initial or guess value for state of derivative block" + annotation (Dialog(group="Initialization", tab="Controller", + enable=controllerType==Modelica.Blocks.Types.SimpleController.PD or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real yCon_start=0 + "Initial value of output from the controller" + annotation(Dialog(group="Initialization", tab="Controller", + enable=initType == Modelica.Blocks.Types.InitPID.InitialOutput)); + parameter Boolean reverseAction = true + "Set to true for throttling the water flow rate through a cooling coil controller" + annotation(Dialog(tab="Controller")); + + Modelica.Blocks.Interfaces.RealInput TSet( + final quantity="ThermodynamicTemperature", + final unit="K") + "Setpoint temperature" + annotation (Placement(transformation(extent={{-140,-20},{-100,20}}))); + + Modelica.Blocks.Interfaces.RealOutput Q_flow( + final quantity="Power", + final unit="W", + final displayUnit="kW") + "Measured power demand at the ETS" + annotation (Placement(transformation(extent={{100,140},{120,160}}))); + + Modelica.Blocks.Interfaces.RealOutput Q( + final quantity="Energy", + final unit="J", + final displayUnit="kWh") + "Measured energy consumption at the ETS" + annotation (Placement(transformation(extent={{100,100},{120,120}}))); + + Buildings.Fluid.HeatExchangers.PlateHeatExchangerEffectivenessNTU hex( + redeclare final package Medium1 = Medium, + redeclare final package Medium2 = Medium, + final m1_flow_nominal=m1_flow_nominal, + final m2_flow_nominal=m2_flow_nominal, + final dp1_nominal=dp1_nominal, + final dp2_nominal=dp2_nominal, + final configuration=Buildings.Fluid.Types.HeatExchangerConfiguration.CounterFlow, + final use_Q_flow_nominal=true, + final Q_flow_nominal=Q_flow_nominal, + final T_a1_nominal=T_a1_nominal, + final T_a2_nominal=T_a2_nominal) "Indirect cooling heat exchanger" + annotation (Placement(transformation(extent={{20,-10},{40,10}}))); + + Buildings.Controls.Continuous.LimPID con( + final controllerType=Modelica.Blocks.Types.SimpleController.PID, + final k=k, + final Td=Td, + final yMax=yMax, + final yMin=yMin, + final Ti=Ti, + final wp=wp, + final wd=wd, + final Ni=Ni, + final Nd=Nd, + final initType=Modelica.Blocks.Types.InitPID.InitialOutput, + final xi_start=xi_start, + final xd_start=xd_start, + final y_start=yCon_start, + final reverseAction=reverseAction) "Controller" + annotation (Placement(transformation(extent={{-90,-10},{-70,10}}))); + + Buildings.Fluid.Sensors.TemperatureTwoPort senTDisSup( + redeclare final package Medium = Medium, + final m_flow_nominal=m1_flow_nominal) + "District-side (primary) supply temperature sensor" + annotation (Placement(transformation(extent={{-90,50},{-70,70}}))); + + Buildings.Fluid.Sensors.TemperatureTwoPort senTDisRet( + redeclare final package Medium = Medium, + final m_flow_nominal=m1_flow_nominal) + "District-side (primary) return temperature sensor" + annotation (Placement(transformation(extent={{70,50},{90,70}}))); + + Modelica.Blocks.Continuous.Integrator int(k=1) "Integration" + annotation (Placement(transformation(extent={{60,120},{80,100}}))); + + Buildings.Fluid.Sensors.MassFlowRate senMasFlo( + redeclare package Medium = Medium) + annotation (Placement(transformation(extent={{-60,50},{-40,70}}))); + + Buildings.Fluid.Sensors.TemperatureTwoPort TBuiRet( + redeclare final package Medium = Medium, + final m_flow_nominal=m2_flow_nominal) + "Building-side (secondary) return temperature" + annotation (Placement(transformation(extent={{-70,-70},{-90,-50}}))); + + Buildings.Fluid.Actuators.Valves.TwoWayQuickOpening val( + redeclare final package Medium = Medium, + final m_flow_nominal=m1_flow_nominal, + final dpValve_nominal=dpValve_nominal, + riseTime(displayUnit="s") = 60, + y_start=0) "District-side (primary) control valve" + annotation (Placement(transformation(extent={{-30,70},{-10,50}}))); + + Modelica.Blocks.Math.Gain cp(final k=cp_default) + "Specifc heat multiplier to calculate heat flow rate" + annotation (Placement(transformation(extent={{20,100},{40,120}}))); + + Modelica.Blocks.Math.Product pro "Product" + annotation (Placement(transformation(extent={{-20,100},{0,120}}))); + + Modelica.Blocks.Math.Add dTDis(k1=-1, k2=+1) + "Temperatur difference on the district side" + annotation (Placement(transformation(extent={{-60,106},{-40,126}}))); + +protected + final parameter Medium.ThermodynamicState sta_default = Medium.setState_pTX( + T=Medium.T_default, + p=Medium.p_default, + X=Medium.X_default) "Medium state at default properties"; + final parameter Modelica.SIunits.SpecificHeatCapacity cp_default= + Medium.specificHeatCapacityCp(sta_default) + "Specific heat capacity of the fluid"; + +equation + + connect(hex.port_a2, port_a2) annotation (Line(points={{40,-6},{60,-6},{60,-60}, + {100,-60}}, color={0,127,255})); + connect(hex.port_b1, senTDisRet.port_a) annotation (Line(points={{40,6},{60,6}, + {60,60},{70,60}}, color={0,127,255})); + connect(val.port_b, hex.port_a1) annotation (Line(points={{-10,60},{0,60},{0,6}, + {20,6}}, color={0,127,255})); + connect(senMasFlo.port_b, val.port_a) + annotation (Line(points={{-40,60},{-30,60}}, color={0,127,255})); + connect(port_a1, senTDisSup.port_a) + annotation (Line(points={{-100,60},{-90,60}}, color={0,127,255})); + connect(senTDisSup.port_b, senMasFlo.port_a) + annotation (Line(points={{-70,60},{-60,60}}, color={0,127,255})); + connect(port_b2, TBuiRet.port_b) + annotation (Line(points={{-100,-60},{-90,-60}}, color={0,127,255})); + connect(senTDisRet.port_b, port_b1) + annotation (Line(points={{90,60},{100,60}}, color={0,127,255})); + connect(TSet, con.u_s) + annotation (Line(points={{-120,0},{-106,0},{-106,0},{-92,0}}, + color={0,0,127})); + connect(con.u_m, TBuiRet.T) + annotation (Line(points={{-80,-12},{-80,-49}}, color={0,0,127})); + connect(con.y, val.y) + annotation (Line(points={{-69,0},{-20,0},{-20,48}}, color={0,0,127})); + connect(TBuiRet.port_a, hex.port_b2) annotation (Line(points={{-70,-60},{0,-60}, + {0,-6},{20,-6}}, color={0,127,255})); + connect(pro.y, cp.u) + annotation (Line(points={{1,110},{18,110}}, color={0,0,127})); + connect(senMasFlo.m_flow, pro.u2) + annotation (Line(points={{-50,71},{-50,104},{-22,104}}, color={0,0,127})); + connect(senTDisSup.T, dTDis.u1) + annotation (Line(points={{-80,71},{-80,122},{-62,122}}, color={0,0,127})); + connect(senTDisRet.T, dTDis.u2) annotation (Line(points={{80,71},{80,80},{-70, + 80},{-70,110},{-62,110}}, color={0,0,127})); + connect(dTDis.y, pro.u1) + annotation (Line(points={{-39,116},{-22,116}}, color={0,0,127})); + connect(cp.y, int.u) + annotation (Line(points={{41,110},{58,110}}, color={0,0,127})); + connect(int.y, Q) + annotation (Line(points={{81,110},{110,110}}, color={0,0,127})); + connect(Q_flow, cp.y) annotation (Line(points={{110,150},{50,150},{50,110},{41, + 110}}, color={0,0,127})); + +annotation (defaultComponentName="coo", + Icon(coordinateSystem(preserveAspectRatio=false), graphics={ + Rectangle( + extent={{-100,-56},{100,-64}}, + fillColor={0,0,0}, + fillPattern=FillPattern.Solid, + pattern=LinePattern.None), + Rectangle( + extent={{-100,64},{100,56}}, + fillColor={0,0,0}, + fillPattern=FillPattern.Solid, + pattern=LinePattern.None), + Rectangle( + extent={{-80,80},{80,-80}}, + lineColor={175,175,175}, + fillColor={35,138,255}, + fillPattern=FillPattern.Solid), + Text( + extent={{-52,40},{54,-40}}, + lineColor={0,0,0}, + fillColor={35,138,255}, + fillPattern=FillPattern.Solid, + textStyle={TextStyle.Bold}, + textString="ETS")}), Diagram( + coordinateSystem(preserveAspectRatio=false, extent={{-100,-100},{100,160}})), + Documentation(info=" +

+Indirect cooling energy transfer station (ETS) model that controls +the building chilled water supply temperature by modulating a +primary control valve on the district supply side. The design is +based on a typical district cooling ETS described in ASHRAE's + +District Cooling Guide. +As shown in the figure below, the building pumping design (constant, +variable) is specified on the building side, not within the ETS. +

+

+\"DHC.ETS.CoolingIndirect\"/ +

+

Reference

+

+American Society of Heating, Refrigeration and Air-Conditioning +Engineers. (2013). Chapter 5: End User Interface. In +District Cooling Guide. 1st Edition. +

+", revisions=" +
    +
  • +November 1, 2019, by Kathryn Hinkelman:
    +First implementation.
  • +
+")); +end CoolingIndirect; \ No newline at end of file diff --git a/geojson_modelica_translator/model_connectors/ets_template.py b/geojson_modelica_translator/model_connectors/ets_template.py index 16c6724c4..b4c7dc446 100644 --- a/geojson_modelica_translator/model_connectors/ets_template.py +++ b/geojson_modelica_translator/model_connectors/ets_template.py @@ -21,8 +21,17 @@ def __init__(self, thermal_junction_properties_geojson, system_parameters_geojso self.system_parameters_geojson = system_parameters_geojson self.ets_from_building_modelica = ets_from_building_modelica - #print ( "Yanfei-path: ", os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates') ) + # go up two levels of directory, to get the path of tests folder for ets + directory_up2levels = os.path.abspath(os.path.join(__file__, "../../..")) + self.directory_ets_templated = os.path.join(directory_up2levels + "/tests/output/ets/") + if not os.path.exists(self.directory_ets_templated): + os.mkdir(self.directory_ets_templated) + else: + print ("test/ets folder is already there!!!\n") + pass + + # here comes the Jinja2 function: Environment() self.template_env = Environment( loader=FileSystemLoader(searchpath=os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates')) ) @@ -81,9 +90,8 @@ def to_modelica(self): else: pass - curdir = os.getcwd() - print("Yanfei-2: ", curdir) - #loads_root_path = os.path.join(root_building_dir, project_name, 'Loads') + + # Here come the Jinja2 function: get_template() ets_template = self.template_env.get_template('CoolingIndirect.mot') #print ("Yanfei-2: ", curdir) @@ -96,14 +104,16 @@ def to_modelica(self): "PressureDrop_Valve": [888], "PressureDrop_HX_Secondary": [999], "PressureDrop_HX_Primary": [999], + "SWT_District": [5], + "SWT_Building":[7] } - + #Here comes the Jina2 function: render() file_data = ets_template.render( ets_data=ets_data ) - - with open(os.path.join(os.path.join(os.getcwd(), 'ets_cooling_indirect.mo')), 'w') as f: + # write templated ETS back to modelica file + with open(os.path.join( self.directory_ets_templated, 'ets_cooling_indirect_templated_yanfei.mo'), 'w') as f: f.write(file_data) return ets_modelica diff --git a/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot b/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot index 67dd97754..747a39d02 100644 --- a/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot +++ b/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot @@ -42,7 +42,7 @@ model CoolingIndirect final displayUnit="Pa") "Nominal pressure difference on primary side" annotation(Dialog(group="Heat exchanger")); - {% endfor %} + {% endfor %} {% for dp in ets_data["PressureDrop_HX_Secondary"]%} parameter Modelica.SIunits.PressureDifference dp2_nominal( @@ -68,22 +68,25 @@ model CoolingIndirect {% endfor %} - {% raw %} + {% for supply_water_temp_district in ets_data['SWT_District'] %} parameter Modelica.SIunits.Temperature T_a1_nominal( min=0+273, max=100+273.15, - start=5+273.15, + start={{supply_water_temp_district}}+273.15, final displayUnit="K") "Nominal temperature at port a1" annotation(Dialog(group="Heat exchanger")); + {% endfor %} + + {% for supply_water_temp_building in ets_data['SWT_Building'] %} parameter Modelica.SIunits.Temperature T_a2_nominal( min=0+273, max=100+273.15, - start=7+273.15, + start={{supply_water_temp_building}}+273.15, final displayUnit="K") "Nominal temperature at port a2" annotation(Dialog(group="Heat exchanger")); - {% endraw %} + {% endfor %} {% for efficiency in ets_data['eta_efficiency'] %} parameter Modelica.SIunits.Efficiency eta( From 38f5fc034c6e6c691f42e473021af28817e0aa75 Mon Sep 17 00:00:00 2001 From: YanfeiNREL Date: Thu, 26 Dec 2019 13:06:15 -0700 Subject: [PATCH 29/62] add a ETS-Example-File-OpenLoops, to test the creditability of ETS-templated --- .../model_connectors/ets_template.py | 51 +++++++++++++++++-- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/geojson_modelica_translator/model_connectors/ets_template.py b/geojson_modelica_translator/model_connectors/ets_template.py index b4c7dc446..a7ac6163b 100644 --- a/geojson_modelica_translator/model_connectors/ets_template.py +++ b/geojson_modelica_translator/model_connectors/ets_template.py @@ -2,6 +2,7 @@ import shutil import json + from jinja2 import FileSystemLoader, Environment from geojson_modelica_translator.model_connectors.base import Base as model_connector_base @@ -21,16 +22,21 @@ def __init__(self, thermal_junction_properties_geojson, system_parameters_geojso self.system_parameters_geojson = system_parameters_geojson self.ets_from_building_modelica = ets_from_building_modelica + # get the path of modelica-buildings library + directory_up_1_levels = os.path.abspath( (os.path.join(__file__, "../../")) ) + print ("yanfei-modelica: ", directory_up_1_levels) + self.directory_modelica_building = os.path.join( directory_up_1_levels + "/modelica/buildingslibrary/Buildings/Applications/DHC/EnergyTransferStations/") + # go up two levels of directory, to get the path of tests folder for ets - directory_up2levels = os.path.abspath(os.path.join(__file__, "../../..")) - self.directory_ets_templated = os.path.join(directory_up2levels + "/tests/output/ets/") + directory_up_2_levels = os.path.abspath(os.path.join(__file__, "../../..")) + self.directory_ets_templated = os.path.join(directory_up_2_levels + "/tests/output/ets/") if not os.path.exists(self.directory_ets_templated): os.mkdir(self.directory_ets_templated) else: print ("test/ets folder is already there!!!\n") pass - + # here comes the Jinja2 function: Environment() self.template_env = Environment( loader=FileSystemLoader(searchpath=os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates')) @@ -112,11 +118,44 @@ def to_modelica(self): file_data = ets_template.render( ets_data=ets_data ) - # write templated ETS back to modelica file + + # write templated ETS back to modelica file , to the tests folder for further test with open(os.path.join( self.directory_ets_templated, 'ets_cooling_indirect_templated_yanfei.mo'), 'w') as f: f.write(file_data) - return ets_modelica + # write templated ETS back to modelica file for further test + with open(os.path.join( self.directory_modelica_building, 'ets_cooling_indirect_templated.mo'), 'w') as f: + f.write(file_data) + + return file_data + + def test_templated_ets_openloops(self): + '''after we creating the templated ets, we need to test it in open loops. + Here we refactor the example file: CoolingIndirectOpenLoops, to test our templated ets model. + ''' + if os.path.exists(self.directory_modelica_building + "/Examples/CoolingIndirectOpenLoops.mo"): + print ("file exists!") + #shutil.copyfile( "CoolingIndirectOpenLoops.mo", "CoolingIndirectTemplatedOpenLoops.mo") + else: + print("CoolingIndirectOpenLoops.mo not exist") + + + file = open(self.directory_modelica_building+"/Examples/CoolingIndirectOpenLoops.mo","r") + with open(self.directory_modelica_building + "/Examples/CoolingIndirectTemplatedOpenLoops.mo", "w") as examplefile: + for f in file: + if "Buildings.Applications.DHC.EnergyTransferStations.CoolingIndirect coo" in f: + fx = f.replace( "Buildings.Applications.DHC.EnergyTransferStations.CoolingIndirect coo", \ + "Buildings.Applications.DHC.EnergyTransferStations.ets_cooling_indirect_templated.mo coo") + examplefile.write( fx) + else: + examplefile.write(f) + + + + + + + def connect(self): '''connect ETS-modelica to building-modelica (specifically TEASER modelica)''' @@ -136,4 +175,6 @@ def connect(self): #ets.check_system_parameters() ets.check_ets_from_building_modelica() ets.to_modelica() +ets.test_templated_ets_openloops() + From 1ad161c7bace4e0322e95eb6863aabf39fb66469 Mon Sep 17 00:00:00 2001 From: YanfeiNREL Date: Thu, 26 Dec 2019 14:26:51 -0700 Subject: [PATCH 30/62] update ets-template --- geojson_modelica_translator/model_connectors/ets_template.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/geojson_modelica_translator/model_connectors/ets_template.py b/geojson_modelica_translator/model_connectors/ets_template.py index a7ac6163b..8982ca057 100644 --- a/geojson_modelica_translator/model_connectors/ets_template.py +++ b/geojson_modelica_translator/model_connectors/ets_template.py @@ -106,7 +106,7 @@ def to_modelica(self): "Q_flow_nominal": [8000], "eta_efficiency": [0.666], "NominalFlow_district": [0.666], - "NominalFlow_buiding":[0.666], + "NominalFlow_building":[0.666], "PressureDrop_Valve": [888], "PressureDrop_HX_Secondary": [999], "PressureDrop_HX_Primary": [999], @@ -120,7 +120,7 @@ def to_modelica(self): ) # write templated ETS back to modelica file , to the tests folder for further test - with open(os.path.join( self.directory_ets_templated, 'ets_cooling_indirect_templated_yanfei.mo'), 'w') as f: + with open(os.path.join( self.directory_ets_templated, 'ets_cooling_indirect_templated.mo'), 'w') as f: f.write(file_data) # write templated ETS back to modelica file for further test From cc3f354f653b1bc73ba2bc149fdddd8e8d442a95 Mon Sep 17 00:00:00 2001 From: YanfeiNREL Date: Thu, 26 Dec 2019 14:36:04 -0700 Subject: [PATCH 31/62] update minor items for tes-template --- .../model_connectors/ets_template.py | 8 ++++---- .../model_connectors/templates/CoolingIndirect.mot | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/geojson_modelica_translator/model_connectors/ets_template.py b/geojson_modelica_translator/model_connectors/ets_template.py index 8982ca057..511cc3f8c 100644 --- a/geojson_modelica_translator/model_connectors/ets_template.py +++ b/geojson_modelica_translator/model_connectors/ets_template.py @@ -103,10 +103,10 @@ def to_modelica(self): building_names = [] ets_data = { - "Q_flow_nominal": [8000], - "eta_efficiency": [0.666], - "NominalFlow_district": [0.666], - "NominalFlow_building":[0.666], + "Q_Flow_Nominal": [8000], + "Eta_Efficiency": [0.666], + "NominalFlow_District": [0.666], + "NominalFlow_Building":[0.666], "PressureDrop_Valve": [888], "PressureDrop_HX_Secondary": [999], "PressureDrop_HX_Primary": [999], diff --git a/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot b/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot index 747a39d02..79c85bfb6 100644 --- a/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot +++ b/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot @@ -11,7 +11,7 @@ model CoolingIndirect Modelica.Media.Interfaces.PartialMedium "Medium in the component"; {% endraw %} - {% for mflow_district in ets_data["NominalFlow_district"]%} + {% for mflow_district in ets_data["NominalFlow_District"]%} // mass flow rates parameter Modelica.SIunits.MassFlowRate m1_flow_nominal( final min=0, @@ -19,14 +19,14 @@ model CoolingIndirect "Nominal mass flow rate of primary (district) district cooling side"; {% endfor %} - {% for mflow_building in ets_data["NominalFlow_building"]%} + {% for mflow_building in ets_data["NominalFlow_Building"]%} parameter Modelica.SIunits.MassFlowRate m2_flow_nominal( final min=0, start=0.5) "Nominal mass flow rate of secondary (building) district cooling side"; {% endfor %} - {% for dp in ets_data["PressureDrop_valve"]%} + {% for dp in ets_data["PressureDrop_Valve"]%} // Primary supply control valve parameter Modelica.SIunits.PressureDifference dpValve_nominal( final min=0, @@ -59,7 +59,7 @@ model CoolingIndirect annotation(Dialog(group="Heat exchanger")); {% endraw %} - {% for Q_flow in ets_data['Q_flow_nominal'] %} + {% for Q_flow in ets_data['Q_Flow_Nominal'] %} parameter Modelica.SIunits.HeatFlowRate Q_flow_nominal( final min=0, start={{Q_flow}}) @@ -88,7 +88,7 @@ model CoolingIndirect annotation(Dialog(group="Heat exchanger")); {% endfor %} - {% for efficiency in ets_data['eta_efficiency'] %} + {% for efficiency in ets_data['Eta_Efficiency'] %} parameter Modelica.SIunits.Efficiency eta( final min=0, final max=1)={{efficiency}} From 401ab5e22f991264bfb87892273e65bb4ab1be72 Mon Sep 17 00:00:00 2001 From: YanfeiNREL Date: Thu, 26 Dec 2019 14:52:00 -0700 Subject: [PATCH 32/62] updates templates --- .../model_connectors/ets_template.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/geojson_modelica_translator/model_connectors/ets_template.py b/geojson_modelica_translator/model_connectors/ets_template.py index 511cc3f8c..a593ca474 100644 --- a/geojson_modelica_translator/model_connectors/ets_template.py +++ b/geojson_modelica_translator/model_connectors/ets_template.py @@ -129,13 +129,13 @@ def to_modelica(self): return file_data + def test_templated_ets_openloops(self): '''after we creating the templated ets, we need to test it in open loops. Here we refactor the example file: CoolingIndirectOpenLoops, to test our templated ets model. ''' if os.path.exists(self.directory_modelica_building + "/Examples/CoolingIndirectOpenLoops.mo"): print ("file exists!") - #shutil.copyfile( "CoolingIndirectOpenLoops.mo", "CoolingIndirectTemplatedOpenLoops.mo") else: print("CoolingIndirectOpenLoops.mo not exist") @@ -151,12 +151,6 @@ def test_templated_ets_openloops(self): examplefile.write(f) - - - - - - def connect(self): '''connect ETS-modelica to building-modelica (specifically TEASER modelica)''' pass From 3fb454555c1027971ac83afb1b46664d7cb57d94 Mon Sep 17 00:00:00 2001 From: YanfeiNREL Date: Thu, 26 Dec 2019 15:33:08 -0700 Subject: [PATCH 33/62] add templated-ets to OpenLoops for Dymola test --- .../model_connectors/ets_template.py | 60 ++++++++++++------- .../templates/CoolingIndirect.mot | 6 +- tests/model_connectors/test_ets.py | 2 + 3 files changed, 46 insertions(+), 22 deletions(-) diff --git a/geojson_modelica_translator/model_connectors/ets_template.py b/geojson_modelica_translator/model_connectors/ets_template.py index a593ca474..569a79fd8 100644 --- a/geojson_modelica_translator/model_connectors/ets_template.py +++ b/geojson_modelica_translator/model_connectors/ets_template.py @@ -36,14 +36,11 @@ def __init__(self, thermal_junction_properties_geojson, system_parameters_geojso print ("test/ets folder is already there!!!\n") pass - # here comes the Jinja2 function: Environment() self.template_env = Environment( loader=FileSystemLoader(searchpath=os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates')) ) - - - print ("@@@@@@you are good here!!!!", self.template_env) + #print ("@@@@@@you are good here!!!!", self.template_env) def check_ets_thermal_junction(self): '''check if ETS info are in thermal-junction-geojson file''' @@ -86,6 +83,7 @@ def check_ets_from_building_modelica(self): return ets_modelica_available + def to_modelica(self): '''convert ETS json to modelica''' ets_modelica="" @@ -96,13 +94,13 @@ def to_modelica(self): else: pass - # Here come the Jinja2 function: get_template() ets_template = self.template_env.get_template('CoolingIndirect.mot') #print ("Yanfei-2: ", curdir) building_names = [] ets_data = { + "ModelName":"ets_cooling_indirect_templated", "Q_Flow_Nominal": [8000], "Eta_Efficiency": [0.666], "NominalFlow_District": [0.666], @@ -112,26 +110,29 @@ def to_modelica(self): "PressureDrop_HX_Primary": [999], "SWT_District": [5], "SWT_Building":[7] - } #Here comes the Jina2 function: render() file_data = ets_template.render( ets_data=ets_data ) - # write templated ETS back to modelica file , to the tests folder for further test + # write templated ETS back to modelica file , to the tests folder for Dymola test + if os.path.exists( os.path.join( self.directory_ets_templated, 'ets_cooling_indirect_templated.mo') ): + os.remove(os.path.join( self.directory_ets_templated, 'ets_cooling_indirect_templated.mo')) with open(os.path.join( self.directory_ets_templated, 'ets_cooling_indirect_templated.mo'), 'w') as f: f.write(file_data) - # write templated ETS back to modelica file for further test + # write templated ETS back to building-modelica folder for Dymola test + if os.path.exists( os.path.join( self.directory_modelica_building, 'ets_cooling_indirect_templated.mo') ): + os.remove(os.path.join( self.directory_modelica_building, 'ets_cooling_indirect_templated.mo')) with open(os.path.join( self.directory_modelica_building, 'ets_cooling_indirect_templated.mo'), 'w') as f: f.write(file_data) return file_data - def test_templated_ets_openloops(self): - '''after we creating the templated ets, we need to test it in open loops. + def templated_ets_openloops_Dymola(self): + '''after we creating the templated ets, we need to test it in Dymola under open loops. Here we refactor the example file: CoolingIndirectOpenLoops, to test our templated ets model. ''' if os.path.exists(self.directory_modelica_building + "/Examples/CoolingIndirectOpenLoops.mo"): @@ -141,25 +142,44 @@ def test_templated_ets_openloops(self): file = open(self.directory_modelica_building+"/Examples/CoolingIndirectOpenLoops.mo","r") - with open(self.directory_modelica_building + "/Examples/CoolingIndirectTemplatedOpenLoops.mo", "w") as examplefile: + + #if the modelica example file is existed, delete it first + if os.path.exists(self.directory_modelica_building + "/Examples/CoolingIndirectOpenLoops_Templated.mo"): + os.remove( self.directory_modelica_building + "/Examples/CoolingIndirectOpenLoops_Templated.mo" ) + + # create the modelica example file for Dymola test + with open(self.directory_modelica_building + "/Examples/CoolingIndirectOpenLoops_Templated.mo", "w") as examplefile: for f in file: - if "Buildings.Applications.DHC.EnergyTransferStations.CoolingIndirect coo" in f: - fx = f.replace( "Buildings.Applications.DHC.EnergyTransferStations.CoolingIndirect coo", \ - "Buildings.Applications.DHC.EnergyTransferStations.ets_cooling_indirect_templated.mo coo") - examplefile.write( fx) + if f.strip() == "model CoolingIndirectOpenLoops": + print ("model header!!!") + fx = f.replace("model CoolingIndirectOpenLoops", "model CoolingIndirectOpenLoops_Templated"+"\n") + + elif f.strip() == "Buildings.Applications.DHC.EnergyTransferStations.CoolingIndirect coo(": + print("model refer!!!") + fx = f.replace( "Buildings.Applications.DHC.EnergyTransferStations.CoolingIndirect coo(", \ + "Buildings.Applications.DHC.EnergyTransferStations.ets_cooling_indirect_templated coo(") + elif f.strip() == "end CoolingIndirectOpenLoops;": + print ("model ender!!!") + fx = f.replace("end CoolingIndirectOpenLoops;", "end CoolingIndirectOpenLoops_Templated;") else: - examplefile.write(f) + fx =f + + examplefile.write(fx) + + return examplefile def connect(self): - '''connect ETS-modelica to building-modelica (specifically TEASER modelica)''' + '''connect ETS-modelica to building-modelica (specifically TEASER modelica). + This function will be modified in future''' + pass ###################################################################################################################### ###### For local test only ###### ###################################################################################################################### - +''' thermal_junction_properties_geojson = "/home/Yanfei_Projects/geojson-modelica-translator/geojson_modelica_translator/geojson/data/schemas/thermal_junction_properties.json" system_parameters_geojson = "/home/Yanfei_Projects/geojson-modelica-translator/geojson_modelica_translator/system_parameters/schema.json" ets_from_building_modelica = "/home/Yanfei_Projects/geojson-modelica-translator/geojson_modelica_translator/modelica/buildingslibrary/Buildings/Applications/DHC/EnergyTransferStations/CoolingIndirect.mo" @@ -169,6 +189,6 @@ def connect(self): #ets.check_system_parameters() ets.check_ets_from_building_modelica() ets.to_modelica() -ets.test_templated_ets_openloops() - +ets.templated_ets_openloops_Dymola() +''' diff --git a/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot b/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot index 79c85bfb6..2a27c720e 100644 --- a/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot +++ b/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot @@ -1,6 +1,8 @@ within Buildings.Applications.DHC.EnergyTransferStations; -model CoolingIndirect + +model {{ets_data["ModelName"]}} + {% raw %} "Indirect cooling energy transfer station for district energy systems" extends Buildings.Fluid.Interfaces.PartialFourPort( @@ -363,4 +365,4 @@ November 1, 2019, by Kathryn Hinkelman:
First implementation. "));{% endraw %} -end CoolingIndirect; \ No newline at end of file +end {{ets_data["ModelName"]}}; \ No newline at end of file diff --git a/tests/model_connectors/test_ets.py b/tests/model_connectors/test_ets.py index 80fe2b936..00b0b0c4f 100644 --- a/tests/model_connectors/test_ets.py +++ b/tests/model_connectors/test_ets.py @@ -31,6 +31,8 @@ def test_ets_from_building_modelica(self): def test_ets_to_modelica(self): self.assertIsNotNone( self.ets.to_modelica() ) + def test_ets_in_Dymola(self): + self.assertIsNotNone( self.ets.templated_ets_openloops_Dymola()) if __name__ == '__main__': unittest.main() From 69417c61aba04f162581708d7a313fc79ab56cc1 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Thu, 16 Jan 2020 12:52:48 -0700 Subject: [PATCH 34/62] code cleanup --- .../model_connectors/ets_template.py | 66 ++++++++------- tests/model_connectors/data/jmodelica.py | 80 +++++++++---------- tests/model_connectors/test_ets.py | 17 ++-- 3 files changed, 81 insertions(+), 82 deletions(-) diff --git a/geojson_modelica_translator/model_connectors/ets_template.py b/geojson_modelica_translator/model_connectors/ets_template.py index 569a79fd8..47b07e882 100644 --- a/geojson_modelica_translator/model_connectors/ets_template.py +++ b/geojson_modelica_translator/model_connectors/ets_template.py @@ -9,8 +9,10 @@ from geojson_modelica_translator.modelica.input_parser import PackageParser from geojson_modelica_translator.utils import ModelicaPath + class ETS_Template(): '''This class will template the ETS modelica model.''' + def __init__(self, thermal_junction_properties_geojson, system_parameters_geojson, ets_from_building_modelica): super().__init__() ''' @@ -23,9 +25,9 @@ def __init__(self, thermal_junction_properties_geojson, system_parameters_geojso self.ets_from_building_modelica = ets_from_building_modelica # get the path of modelica-buildings library - directory_up_1_levels = os.path.abspath( (os.path.join(__file__, "../../")) ) - print ("yanfei-modelica: ", directory_up_1_levels) - self.directory_modelica_building = os.path.join( directory_up_1_levels + "/modelica/buildingslibrary/Buildings/Applications/DHC/EnergyTransferStations/") + directory_up_1_levels = os.path.abspath((os.path.join(__file__, "../../"))) + print("yanfei-modelica: ", directory_up_1_levels) + self.directory_modelica_building = os.path.join(directory_up_1_levels + "/modelica/buildingslibrary/Buildings/Applications/DHC/EnergyTransferStations/") # go up two levels of directory, to get the path of tests folder for ets directory_up_2_levels = os.path.abspath(os.path.join(__file__, "../../..")) @@ -33,7 +35,7 @@ def __init__(self, thermal_junction_properties_geojson, system_parameters_geojso if not os.path.exists(self.directory_ets_templated): os.mkdir(self.directory_ets_templated) else: - print ("test/ets folder is already there!!!\n") + print("test/ets folder is already there!!!\n") pass # here comes the Jinja2 function: Environment() @@ -44,7 +46,7 @@ def __init__(self, thermal_junction_properties_geojson, system_parameters_geojso def check_ets_thermal_junction(self): '''check if ETS info are in thermal-junction-geojson file''' - with open(self.thermal_junction_properties_geojson,'r') as f: + with open(self.thermal_junction_properties_geojson, 'r') as f: data = json.load(f) ets_general = False @@ -53,8 +55,8 @@ def check_ets_thermal_junction(self): # three levels down to get the ETS signal junctions = data["definitions"]["ThermalJunctionType"]["enum"] if 'ETS' in junctions: - ets_general=True - print ("ETS is there!!!") + ets_general = True + print("ETS is there!!!") else: pass @@ -65,14 +67,14 @@ def check_system_parameters(self): with open(self.system_parameters_geojson, 'r') as f: data = json.load(f) - ets_details=False + ets_details = False for key, value in data.items(): #print (key, " <==> " ,value) # four levels down to get the details ets_details = data["definitions"]["building_def"]["properties"]["ets"] - print (ets_details) + print(ets_details) if ets_details: - print ("ETS details are here!!!") + print("ETS details are here!!!") return ets_details @@ -83,10 +85,9 @@ def check_ets_from_building_modelica(self): return ets_modelica_available - def to_modelica(self): '''convert ETS json to modelica''' - ets_modelica="" + ets_modelica = "" if self.check_ets_from_building_modelica(): with open(self.ets_from_building_modelica) as f: ets_modelica = f.read() @@ -100,75 +101,72 @@ def to_modelica(self): building_names = [] ets_data = { - "ModelName":"ets_cooling_indirect_templated", + "ModelName": "ets_cooling_indirect_templated", "Q_Flow_Nominal": [8000], "Eta_Efficiency": [0.666], "NominalFlow_District": [0.666], - "NominalFlow_Building":[0.666], + "NominalFlow_Building": [0.666], "PressureDrop_Valve": [888], "PressureDrop_HX_Secondary": [999], "PressureDrop_HX_Primary": [999], "SWT_District": [5], - "SWT_Building":[7] + "SWT_Building": [7] } - #Here comes the Jina2 function: render() + # Here comes the Jina2 function: render() file_data = ets_template.render( ets_data=ets_data ) # write templated ETS back to modelica file , to the tests folder for Dymola test - if os.path.exists( os.path.join( self.directory_ets_templated, 'ets_cooling_indirect_templated.mo') ): - os.remove(os.path.join( self.directory_ets_templated, 'ets_cooling_indirect_templated.mo')) - with open(os.path.join( self.directory_ets_templated, 'ets_cooling_indirect_templated.mo'), 'w') as f: + if os.path.exists(os.path.join(self.directory_ets_templated, 'ets_cooling_indirect_templated.mo')): + os.remove(os.path.join(self.directory_ets_templated, 'ets_cooling_indirect_templated.mo')) + with open(os.path.join(self.directory_ets_templated, 'ets_cooling_indirect_templated.mo'), 'w') as f: f.write(file_data) # write templated ETS back to building-modelica folder for Dymola test - if os.path.exists( os.path.join( self.directory_modelica_building, 'ets_cooling_indirect_templated.mo') ): - os.remove(os.path.join( self.directory_modelica_building, 'ets_cooling_indirect_templated.mo')) - with open(os.path.join( self.directory_modelica_building, 'ets_cooling_indirect_templated.mo'), 'w') as f: + if os.path.exists(os.path.join(self.directory_modelica_building, 'ets_cooling_indirect_templated.mo')): + os.remove(os.path.join(self.directory_modelica_building, 'ets_cooling_indirect_templated.mo')) + with open(os.path.join(self.directory_modelica_building, 'ets_cooling_indirect_templated.mo'), 'w') as f: f.write(file_data) return file_data - def templated_ets_openloops_Dymola(self): '''after we creating the templated ets, we need to test it in Dymola under open loops. Here we refactor the example file: CoolingIndirectOpenLoops, to test our templated ets model. ''' if os.path.exists(self.directory_modelica_building + "/Examples/CoolingIndirectOpenLoops.mo"): - print ("file exists!") + print("file exists!") else: print("CoolingIndirectOpenLoops.mo not exist") + file = open(self.directory_modelica_building + "/Examples/CoolingIndirectOpenLoops.mo", "r") - file = open(self.directory_modelica_building+"/Examples/CoolingIndirectOpenLoops.mo","r") - - #if the modelica example file is existed, delete it first + # if the modelica example file is existed, delete it first if os.path.exists(self.directory_modelica_building + "/Examples/CoolingIndirectOpenLoops_Templated.mo"): - os.remove( self.directory_modelica_building + "/Examples/CoolingIndirectOpenLoops_Templated.mo" ) + os.remove(self.directory_modelica_building + "/Examples/CoolingIndirectOpenLoops_Templated.mo") # create the modelica example file for Dymola test with open(self.directory_modelica_building + "/Examples/CoolingIndirectOpenLoops_Templated.mo", "w") as examplefile: for f in file: if f.strip() == "model CoolingIndirectOpenLoops": - print ("model header!!!") - fx = f.replace("model CoolingIndirectOpenLoops", "model CoolingIndirectOpenLoops_Templated"+"\n") + print("model header!!!") + fx = f.replace("model CoolingIndirectOpenLoops", "model CoolingIndirectOpenLoops_Templated" + "\n") elif f.strip() == "Buildings.Applications.DHC.EnergyTransferStations.CoolingIndirect coo(": print("model refer!!!") - fx = f.replace( "Buildings.Applications.DHC.EnergyTransferStations.CoolingIndirect coo(", \ + fx = f.replace("Buildings.Applications.DHC.EnergyTransferStations.CoolingIndirect coo(", "Buildings.Applications.DHC.EnergyTransferStations.ets_cooling_indirect_templated coo(") elif f.strip() == "end CoolingIndirectOpenLoops;": - print ("model ender!!!") + print("model ender!!!") fx = f.replace("end CoolingIndirectOpenLoops;", "end CoolingIndirectOpenLoops_Templated;") else: - fx =f + fx = f examplefile.write(fx) return examplefile - def connect(self): '''connect ETS-modelica to building-modelica (specifically TEASER modelica). This function will be modified in future''' diff --git a/tests/model_connectors/data/jmodelica.py b/tests/model_connectors/data/jmodelica.py index 7dd55f490..d59b274a4 100644 --- a/tests/model_connectors/data/jmodelica.py +++ b/tests/model_connectors/data/jmodelica.py @@ -17,14 +17,14 @@ # import matplotlib.pyplot as plt debug_solver = False -model="Buildings.Controls.OBC.CDL.Continuous.Validation.LimPID" +model = "Buildings.Controls.OBC.CDL.Continuous.Validation.LimPID" # Overwrite model with command line argument if specified if len(sys.argv) > 1: - # If the argument is a file, then parse it to a model name - if os.path.isfile(sys.argv[1]): - model = sys.argv[1].replace(os.path.sep, '.')[:-3] - else: - model=sys.argv[1] + # If the argument is a file, then parse it to a model name + if os.path.isfile(sys.argv[1]): + model = sys.argv[1].replace(os.path.sep, '.')[:-3] + else: + model = sys.argv[1] print("*** Compiling {}".format(model)) @@ -39,8 +39,8 @@ fmu_name = compile_fmu(model, version="2.0", compiler_log_level='warning', - compiler_options = {"generate_html_diagnostics" : False, - "nle_solver_tol_factor": 1e-2}) + compiler_options={"generate_html_diagnostics": False, + "nle_solver_tol_factor": 1e-2}) ###################################################################### # Load model @@ -49,33 +49,33 @@ ###################################################################### # Retrieve and set solver options x_nominal = mod.nominal_continuous_states -opts = mod.simulate_options() #Retrieve the default options +opts = mod.simulate_options() # Retrieve the default options opts['solver'] = 'CVode' opts['ncp'] = 5000 if opts['solver'].lower() == 'cvode': - # Set user-specified tolerance if it is smaller than the tolerance in the .mo file - rtol = 1.0e-6 - x_nominal = mod.nominal_continuous_states - - if len(x_nominal) > 0: - atol = rtol*x_nominal - else: - atol = rtol - - opts['CVode_options'] = { - 'external_event_detection': False, - 'maxh': (mod.get_default_experiment_stop_time()-mod.get_default_experiment_stop_time())/float(opts['ncp']), - 'iter': 'Newton', - 'discr': 'BDF', - 'rtol': rtol, - 'atol': atol, - 'store_event_points': True + # Set user-specified tolerance if it is smaller than the tolerance in the .mo file + rtol = 1.0e-6 + x_nominal = mod.nominal_continuous_states + + if len(x_nominal) > 0: + atol = rtol * x_nominal + else: + atol = rtol + + opts['CVode_options'] = { + 'external_event_detection': False, + 'maxh': (mod.get_default_experiment_stop_time() - mod.get_default_experiment_stop_time()) / float(opts['ncp']), + 'iter': 'Newton', + 'discr': 'BDF', + 'rtol': rtol, + 'atol': atol, + 'store_event_points': True } if debug_solver: - opts["logging"] = True #<- Turn on solver debug logging + opts["logging"] = True # <- Turn on solver debug logging mod.set("_log_level", 6) ###################################################################### @@ -105,21 +105,21 @@ ###################################################################### # Get debugging information if debug_solver: - #Load the debug information - from pyfmi.debug import CVodeDebugInformation - debug = CVodeDebugInformation(model.replace(".", "_")+"_debug.txt") + # Load the debug information + from pyfmi.debug import CVodeDebugInformation + debug = CVodeDebugInformation(model.replace(".", "_") + "_debug.txt") - ### Below are options to plot the order, error and step-size evolution. - ### The error methos also take a threshold and a region if you want to - ### limit the plot to a certain interval. + # Below are options to plot the order, error and step-size evolution. + # The error methos also take a threshold and a region if you want to + # limit the plot to a certain interval. - #Plot order evolution - debug.plot_order() + # Plot order evolution + debug.plot_order() - #Plot error evolution - debug.plot_error() #Note see also the arguments to the method + # Plot error evolution + debug.plot_error() # Note see also the arguments to the method - #Plot the used step-size - debug.plot_step_size() + # Plot the used step-size + debug.plot_step_size() - #See also debug? + # See also debug? diff --git a/tests/model_connectors/test_ets.py b/tests/model_connectors/test_ets.py index 00b0b0c4f..b9685fbb8 100644 --- a/tests/model_connectors/test_ets.py +++ b/tests/model_connectors/test_ets.py @@ -4,9 +4,10 @@ from geojson_modelica_translator.model_connectors.ets_template import ETS_Template import os + class ETS_ModelConnectorSingleBuildingTest(unittest.TestCase): - def setUp(self): # the first method/member must be setUp - folder_base= os.getcwd() + def setUp(self): # the first method/member must be setUp + folder_base = os.getcwd() self.thermal_junction_properties_geojson = folder_base + "/geojson_modelica_translator/geojson/data/schemas/thermal_junction_properties.json" self.system_parameters_geojson = folder_base + "/geojson_modelica_translator/system_parameters/schema.json" self.ets_from_building_modelica = folder_base + "/geojson_modelica_translator/modelica/buildingslibrary/Buildings/Applications/DHC/EnergyTransferStations/CoolingIndirect.mo" @@ -16,23 +17,23 @@ def setUp(self): # the first method/member must be setUp return self.ets - def test_ets_thermal_junction(self): ets_general = self.ets.check_ets_thermal_junction() #print ("yanfei: ", ets_general) - self.assertTrue( ets_general ) + self.assertTrue(ets_general) def test_ets_system_parameters(self): - self.assertTrue( self.ets.check_system_parameters() ) + self.assertTrue(self.ets.check_system_parameters()) def test_ets_from_building_modelica(self): - self.assertTrue( self.ets.check_ets_from_building_modelica() ) + self.assertTrue(self.ets.check_ets_from_building_modelica()) def test_ets_to_modelica(self): - self.assertIsNotNone( self.ets.to_modelica() ) + self.assertIsNotNone(self.ets.to_modelica()) def test_ets_in_Dymola(self): - self.assertIsNotNone( self.ets.templated_ets_openloops_Dymola()) + self.assertIsNotNone(self.ets.templated_ets_openloops_Dymola()) + if __name__ == '__main__': unittest.main() From 15ec2ea24ff0c925b24a3a94d6d14e35abef0059 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Thu, 16 Jan 2020 20:42:11 -0700 Subject: [PATCH 35/62] updating testcase for ets-template --- .gitignore | 2 +- docs/conf.py | 2 +- .../geojson/urbanopt_geojson.py | 3 +- ...ct_yanfei.mo => ets_cooling_indirect_2.mo} | 23 -- .../model_connectors/ets_template.py | 189 ++++----- .../model_connectors/spawn.py | 1 - .../model_connectors/teaser.py | 1 - .../templates/CoolingIndirect.mot | 1 + .../system_parameters/schema.json | 16 + setup.py | 3 +- tests/context.py | 3 +- tests/geojson/test_geojson.py | 2 +- tests/geojson/test_schemas.py | 3 +- tests/model_connectors/data/jmodelica.py | 11 +- tests/model_connectors/test_ets.py | 37 +- tests/model_connectors/test_spawn.py | 4 +- tests/modelica/test_input_parser.py | 3 +- .../ets_cooling_indirect_templated.mo | 369 ++++++++++++++++++ tests/test_translator.py | 4 +- tests/test_utils.py | 3 +- 20 files changed, 513 insertions(+), 167 deletions(-) rename geojson_modelica_translator/model_connectors/{ets_cooling_indirect_yanfei.mo => ets_cooling_indirect_2.mo} (99%) create mode 100644 tests/output_ets/ets_cooling_indirect_templated.mo diff --git a/.gitignore b/.gitignore index 8d69ff36a..e6f301d64 100644 --- a/.gitignore +++ b/.gitignore @@ -95,4 +95,4 @@ ENV/ tests/output tests/modelica/output tests/model_connectors/output -#/geojson_modelica_translator/modelica/buildingslibrary/ +/geojson_modelica_translator/modelica/buildingslibrary/ diff --git a/docs/conf.py b/docs/conf.py index f6dfb5d9e..6a1793ef5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -11,8 +11,8 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys import os +import sys # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the diff --git a/geojson_modelica_translator/geojson/urbanopt_geojson.py b/geojson_modelica_translator/geojson/urbanopt_geojson.py index 13ae70cc4..6e6e6dd22 100644 --- a/geojson_modelica_translator/geojson/urbanopt_geojson.py +++ b/geojson_modelica_translator/geojson/urbanopt_geojson.py @@ -28,10 +28,11 @@ **************************************************************************************************** """ +import geojson import logging import os from collections import defaultdict -import geojson + from geojson_modelica_translator.geojson.schemas import Schemas _log = logging.getLogger(__name__) diff --git a/geojson_modelica_translator/model_connectors/ets_cooling_indirect_yanfei.mo b/geojson_modelica_translator/model_connectors/ets_cooling_indirect_2.mo similarity index 99% rename from geojson_modelica_translator/model_connectors/ets_cooling_indirect_yanfei.mo rename to geojson_modelica_translator/model_connectors/ets_cooling_indirect_2.mo index 50e2c1612..472bfa259 100644 --- a/geojson_modelica_translator/model_connectors/ets_cooling_indirect_yanfei.mo +++ b/geojson_modelica_translator/model_connectors/ets_cooling_indirect_2.mo @@ -10,20 +10,12 @@ model CoolingIndirect replaceable package Medium = Modelica.Media.Interfaces.PartialMedium "Medium in the component"; - - // mass flow rates parameter Modelica.SIunits.MassFlowRate m1_flow_nominal( final min=0, start=0.666) "Nominal mass flow rate of primary (district) district cooling side"; - - - - - - // Heat exchanger parameter Modelica.SIunits.PressureDifference dp1_nominal( final min=0, @@ -31,33 +23,24 @@ model CoolingIndirect final displayUnit="Pa") "Nominal pressure difference on primary side" annotation(Dialog(group="Heat exchanger")); - - parameter Modelica.SIunits.PressureDifference dp2_nominal( final min=0, start=999, final displayUnit="Pa") "Nominal pressure difference on secondary side" annotation(Dialog(group="Heat exchanger")); - - parameter Boolean use_Q_flow_nominal=true "Set to true to specify Q_flow_nominal and temperatures, or to false to specify effectiveness" annotation(Dialog(group="Heat exchanger")); - - parameter Modelica.SIunits.HeatFlowRate Q_flow_nominal( final min=0, start=8000) "Nominal heat transfer" annotation(Dialog(group="Heat exchanger")); - - - parameter Modelica.SIunits.Temperature T_a1_nominal( min=0+273, max=100+273.15, @@ -65,9 +48,7 @@ model CoolingIndirect final displayUnit="K") "Nominal temperature at port a1" annotation(Dialog(group="Heat exchanger")); - - parameter Modelica.SIunits.Temperature T_a2_nominal( min=0+273, max=100+273.15, @@ -75,17 +56,13 @@ model CoolingIndirect final displayUnit="K") "Nominal temperature at port a2" annotation(Dialog(group="Heat exchanger")); - - parameter Modelica.SIunits.Efficiency eta( final min=0, final max=1)=0.666 "Constant effectiveness" annotation(Dialog(group="Heat exchanger")); - - // Controller parameters parameter Modelica.Blocks.Types.SimpleController controllerType= Modelica.Blocks.Types.SimpleController.PI diff --git a/geojson_modelica_translator/model_connectors/ets_template.py b/geojson_modelica_translator/model_connectors/ets_template.py index 47b07e882..30d23db1d 100644 --- a/geojson_modelica_translator/model_connectors/ets_template.py +++ b/geojson_modelica_translator/model_connectors/ets_template.py @@ -1,51 +1,56 @@ -import os -import shutil import json - +import os from jinja2 import FileSystemLoader, Environment -from geojson_modelica_translator.model_connectors.base import Base as model_connector_base -from geojson_modelica_translator.modelica.input_parser import PackageParser -from geojson_modelica_translator.utils import ModelicaPath - - -class ETS_Template(): - '''This class will template the ETS modelica model.''' +# TODO: Class name should be upper camel case, not a mix of camel and snake case. +class ETSTemplate(): + """This class will template the ETS modelica model.""" def __init__(self, thermal_junction_properties_geojson, system_parameters_geojson, ets_from_building_modelica): super().__init__() - ''' + """ thermal_junction_properties_geojson contains the ETS at brief and at higher level; system_parameters_geojson contains the ETS with details ; ets_from_building_modelica contains the modelica model of ETS ; - ''' + """ self.thermal_junction_properties_geojson = thermal_junction_properties_geojson + self.thermal_junction_properties_geojson = self.thermal_junction_properties_geojson.replace("\\", "/") + self.system_parameters_geojson = system_parameters_geojson + if "\\" in self.system_parameters_geojson: + self.system_parameters_geojson = self.system_parameters_geojson.replace("\\" , "/") + self.ets_from_building_modelica = ets_from_building_modelica + if "\\" in self.ets_from_building_modelica: + self.ets_from_building_modelica = self.ets_from_building_modelica.replace("\\", "/") # get the path of modelica-buildings library - directory_up_1_levels = os.path.abspath((os.path.join(__file__, "../../"))) - print("yanfei-modelica: ", directory_up_1_levels) - self.directory_modelica_building = os.path.join(directory_up_1_levels + "/modelica/buildingslibrary/Buildings/Applications/DHC/EnergyTransferStations/") + directory_up_one_levels = os.path.abspath((os.path.join(__file__, "../../"))) + dest_path = "/modelica/buildingslibrary/Buildings/Applications/DHC/EnergyTransferStations/" + self.directory_modelica_building = os.path.join(directory_up_one_levels + dest_path) + if "\\" in self.directory_modelica_building: + self.directory_modelica_building = self.directory_modelica_building.replace("\\", "/") # go up two levels of directory, to get the path of tests folder for ets - directory_up_2_levels = os.path.abspath(os.path.join(__file__, "../../..")) - self.directory_ets_templated = os.path.join(directory_up_2_levels + "/tests/output/ets/") - if not os.path.exists(self.directory_ets_templated): + directory_up_two_levels = os.path.abspath(os.path.join(__file__, "../../..")) + self.directory_ets_templated = os.path.join(directory_up_two_levels + "/tests/output_ets") + if "\\" in self.directory_ets_templated: + self.directory_ets_templated = self.directory_ets_templated.replace("\\", "/") + + if not os.path.isdir(self.directory_ets_templated): os.mkdir(self.directory_ets_templated) else: - print("test/ets folder is already there!!!\n") pass # here comes the Jinja2 function: Environment() + # it loads all the "*.mot" files into an environment by Jinja2 self.template_env = Environment( loader=FileSystemLoader(searchpath=os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates')) ) - #print ("@@@@@@you are good here!!!!", self.template_env) def check_ets_thermal_junction(self): - '''check if ETS info are in thermal-junction-geojson file''' + """check if ETS info are in thermal-junction-geojson file""" with open(self.thermal_junction_properties_geojson, 'r') as f: data = json.load(f) @@ -56,62 +61,41 @@ def check_ets_thermal_junction(self): junctions = data["definitions"]["ThermalJunctionType"]["enum"] if 'ETS' in junctions: ets_general = True - print("ETS is there!!!") else: pass return ets_general - def check_system_parameters(self): - '''check detailed parameters of ETS''' + def check_ets_system_parameters(self): + """check detailed parameters of ETS""" with open(self.system_parameters_geojson, 'r') as f: data = json.load(f) - ets_details = False - for key, value in data.items(): - #print (key, " <==> " ,value) - # four levels down to get the details - ets_details = data["definitions"]["building_def"]["properties"]["ets"] - print(ets_details) - if ets_details: - print("ETS details are here!!!") - - return ets_details + ets_parameters = False + # four levels down to get the ets model description + ets_overall = data["definitions"]["building_def"]["properties"]["ets"] + # three levels down to get the parameters + ets_parameters = data["definitions"]["ets_parameters"]["properties"] + #print ("est_parameters are: ", type(ets_parameters) ) + return ets_parameters def check_ets_from_building_modelica(self): - '''check if ETS-indirectCooling are in modelica building library''' + """check if ETS-indirectCooling are in modelica building library""" ets_modelica_available = os.path.isfile(self.ets_from_building_modelica) - #print ("ets-available: ", ets_modelica_available) return ets_modelica_available def to_modelica(self): - '''convert ETS json to modelica''' - ets_modelica = "" - if self.check_ets_from_building_modelica(): - with open(self.ets_from_building_modelica) as f: - ets_modelica = f.read() - #print ( ets_modelica ) - else: - pass - - # Here come the Jinja2 function: get_template() + """convert ETS json to modelica""" + # Here come the Jinja2 function: get_template(), which will read into the templated_ets model. + # CoolingIndirect.mot was manually created as a starting point, by adding stuff following Jinja2. + # it has all the necessary parameters which need to be changed through templating. ets_template = self.template_env.get_template('CoolingIndirect.mot') - #print ("Yanfei-2: ", curdir) - - building_names = [] - ets_data = { - "ModelName": "ets_cooling_indirect_templated", - "Q_Flow_Nominal": [8000], - "Eta_Efficiency": [0.666], - "NominalFlow_District": [0.666], - "NominalFlow_Building": [0.666], - "PressureDrop_Valve": [888], - "PressureDrop_HX_Secondary": [999], - "PressureDrop_HX_Primary": [999], - "SWT_District": [5], - "SWT_Building": [7] - } + + # TODO: Seems like the ets_data below should allow defaults from the system parameters JSON file, correct? + # ets model parameters are from the schema.json file, default values only. + ets_data = self.check_ets_system_parameters() + # Here comes the Jina2 function: render() file_data = ets_template.render( ets_data=ets_data @@ -131,62 +115,61 @@ def to_modelica(self): return file_data - def templated_ets_openloops_Dymola(self): - '''after we creating the templated ets, we need to test it in Dymola under open loops. + def templated_ets_openloops_dymola(self): + """after we creating the templated ets, we need to test it in Dymola under open loops. Here we refactor the example file: CoolingIndirectOpenLoops, to test our templated ets model. - ''' - if os.path.exists(self.directory_modelica_building + "/Examples/CoolingIndirectOpenLoops.mo"): - print("file exists!") - else: - print("CoolingIndirectOpenLoops.mo not exist") - + """ file = open(self.directory_modelica_building + "/Examples/CoolingIndirectOpenLoops.mo", "r") + cooling_indirect_filename = "/Examples/CoolingIndirectOpenLoops_Templated.mo" # if the modelica example file is existed, delete it first - if os.path.exists(self.directory_modelica_building + "/Examples/CoolingIndirectOpenLoops_Templated.mo"): - os.remove(self.directory_modelica_building + "/Examples/CoolingIndirectOpenLoops_Templated.mo") + if os.path.exists(self.directory_modelica_building + cooling_indirect_filename): + os.remove(self.directory_modelica_building + cooling_indirect_filename) # create the modelica example file for Dymola test - with open(self.directory_modelica_building + "/Examples/CoolingIndirectOpenLoops_Templated.mo", "w") as examplefile: + # TODO: Replace this with the ModelicaFile Class -- extend ModelicaFile class if does not support. + # Theoretically it is doable using extend clause from Modelica. But we need to change the original ETS model first, in order to extend. + # This is Michael Wetter suggested approach. + # if so, we don't need to template modelica models, but we need to connect the modelica components + repl_dict = {} + from_str = "model CoolingIndirectOpenLoops" + to_str = "model CoolingIndirectOpenLoops_Templated\n" + repl_dict[from_str] = to_str + from_str = "Buildings.Applications.DHC.EnergyTransferStations.CoolingIndirect coo(" + to_str = "Buildings.Applications.DHC.EnergyTransferStations.ets_cooling_indirect_templated coo(" + repl_dict[from_str] = to_str + from_str = "end CoolingIndirectOpenLoops;" + to_str = "end CoolingIndirectOpenLoops_Templated;" + repl_dict[from_str] = to_str + + with open(self.directory_modelica_building + cooling_indirect_filename, "w") as examplefile: for f in file: - if f.strip() == "model CoolingIndirectOpenLoops": - print("model header!!!") - fx = f.replace("model CoolingIndirectOpenLoops", "model CoolingIndirectOpenLoops_Templated" + "\n") - - elif f.strip() == "Buildings.Applications.DHC.EnergyTransferStations.CoolingIndirect coo(": - print("model refer!!!") - fx = f.replace("Buildings.Applications.DHC.EnergyTransferStations.CoolingIndirect coo(", - "Buildings.Applications.DHC.EnergyTransferStations.ets_cooling_indirect_templated coo(") - elif f.strip() == "end CoolingIndirectOpenLoops;": - print("model ender!!!") - fx = f.replace("end CoolingIndirectOpenLoops;", "end CoolingIndirectOpenLoops_Templated;") - else: - fx = f + fx = f + for from_str, to_str in repl_dict.items(): + # TODO: f.string() causes errors, check code + if fx.strip() == from_str.strip(): + fx = f.replace(from_str, to_str) examplefile.write(fx) return examplefile def connect(self): - '''connect ETS-modelica to building-modelica (specifically TEASER modelica). - This function will be modified in future''' - + """connect ETS-modelica to building-modelica (specifically TEASER modelica). + This function will be modified in future""" pass - -###################################################################################################################### -###### For local test only ###### -###################################################################################################################### -''' -thermal_junction_properties_geojson = "/home/Yanfei_Projects/geojson-modelica-translator/geojson_modelica_translator/geojson/data/schemas/thermal_junction_properties.json" -system_parameters_geojson = "/home/Yanfei_Projects/geojson-modelica-translator/geojson_modelica_translator/system_parameters/schema.json" -ets_from_building_modelica = "/home/Yanfei_Projects/geojson-modelica-translator/geojson_modelica_translator/modelica/buildingslibrary/Buildings/Applications/DHC/EnergyTransferStations/CoolingIndirect.mo" -print ( os.getcwd() ) -ets = ETS_Template(thermal_junction_properties_geojson, system_parameters_geojson, ets_from_building_modelica ) -#ets.check_ets_thermal_junction() -#ets.check_system_parameters() +######################################################################################################################## +################################# For Local Debugging Purpose Only ################################## +######################################################################################################################## + +thermal_junction_properties_geojson = "C:/Users/YLI3/Yanfei_Projects/UrbanOPT/geojson-modelica-translator/geojson_modelica_translator/geojson/data/schemas/thermal_junction_properties.json" +system_parameters_geojson = "C:/Users/YLI3/Yanfei_Projects/UrbanOPT/geojson-modelica-translator/geojson_modelica_translator/system_parameters/schema.json" +ets_from_building_modelica = "C:/Users/YLI3/Yanfei_Projects/UrbanOPT/geojson-modelica-translator/geojson_modelica_translator/modelica/buildingslibrary/Buildings/Applications/DHC/EnergyTransferStations/CoolingIndirect.mo" +#print ( "current folder: ", os.getcwd(), "\n") +ets = ETSTemplate(thermal_junction_properties_geojson, system_parameters_geojson, ets_from_building_modelica ) +ets.check_ets_thermal_junction() +ets.check_ets_system_parameters() ets.check_ets_from_building_modelica() ets.to_modelica() -ets.templated_ets_openloops_Dymola() - -''' +ets.templated_ets_openloops_dymola() diff --git a/geojson_modelica_translator/model_connectors/spawn.py b/geojson_modelica_translator/model_connectors/spawn.py index 204791c23..d8d055074 100644 --- a/geojson_modelica_translator/model_connectors/spawn.py +++ b/geojson_modelica_translator/model_connectors/spawn.py @@ -30,7 +30,6 @@ import os import shutil - from jinja2 import FileSystemLoader, Environment from geojson_modelica_translator.model_connectors.base import Base as model_connector_base diff --git a/geojson_modelica_translator/model_connectors/teaser.py b/geojson_modelica_translator/model_connectors/teaser.py index 1f4ada2ef..97d47316d 100644 --- a/geojson_modelica_translator/model_connectors/teaser.py +++ b/geojson_modelica_translator/model_connectors/teaser.py @@ -31,7 +31,6 @@ import glob import os import shutil - from teaser.project import Project from geojson_modelica_translator.model_connectors.base import Base as model_connector_base diff --git a/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot b/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot index 2a27c720e..40a65d7ca 100644 --- a/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot +++ b/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot @@ -363,6 +363,7 @@ Engineers. (2013). Chapter 5: End User Interface. In
  • November 1, 2019, by Kathryn Hinkelman:
    First implementation.
  • +
  • 12/15/2020, Yanfei Li, Templating ETS model.
  • "));{% endraw %} end {{ets_data["ModelName"]}}; \ No newline at end of file diff --git a/geojson_modelica_translator/system_parameters/schema.json b/geojson_modelica_translator/system_parameters/schema.json index db5e3c110..4daf2c60a 100755 --- a/geojson_modelica_translator/system_parameters/schema.json +++ b/geojson_modelica_translator/system_parameters/schema.json @@ -283,6 +283,22 @@ } }, "additionalProperties": false + }, + "ets_parameters": { + "description": "Parameters associated with ETS models", + "type": "object", + "properties": { + "ModelName": "ets_cooling_indirect_templated", + "Q_Flow_Nominal": [8000], + "Eta_Efficiency": [0.666], + "NominalFlow_District": [0.666], + "NominalFlow_Building": [0.666], + "PressureDrop_Valve": [888], + "PressureDrop_HX_Secondary": [999], + "PressureDrop_HX_Primary": [999], + "SWT_District": [5], + "SWT_Building": [7] + } } } } \ No newline at end of file diff --git a/setup.py b/setup.py index 2c5f04cbb..b26c5631f 100755 --- a/setup.py +++ b/setup.py @@ -33,10 +33,9 @@ import os import shutil from io import BytesIO -from zipfile import ZipFile - from requests import get from setuptools import setup, find_packages +from zipfile import ZipFile from management.update_licenses import UpdateLicenses from management.update_schemas import UpdateSchemas diff --git a/tests/context.py b/tests/context.py index 8e7ad4658..0a3b3e298 100644 --- a/tests/context.py +++ b/tests/context.py @@ -31,8 +31,9 @@ # Helper file to allow for easy setup of test files. This allows for running tests in PyCharm and # the command line using py.test. -import sys import os +import sys + sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) import geojson_modelica_translator # noqa - Do not remove this line. diff --git a/tests/geojson/test_geojson.py b/tests/geojson/test_geojson.py index 0ae724c05..7e8b7e9f3 100644 --- a/tests/geojson/test_geojson.py +++ b/tests/geojson/test_geojson.py @@ -31,8 +31,8 @@ import os import unittest -from ..context import geojson_modelica_translator # noqa - Do not remove this line from geojson_modelica_translator.geojson.urbanopt_geojson import UrbanOptGeoJson +from ..context import geojson_modelica_translator # noqa - Do not remove this line class GeoJSONTest(unittest.TestCase): diff --git a/tests/geojson/test_schemas.py b/tests/geojson/test_schemas.py index 6ca4ade0c..1d077a7e4 100644 --- a/tests/geojson/test_schemas.py +++ b/tests/geojson/test_schemas.py @@ -28,11 +28,10 @@ **************************************************************************************************** """ -from ..context import geojson_modelica_translator # noqa - Do not remove this line - import unittest from geojson_modelica_translator.geojson.schemas import Schemas +from ..context import geojson_modelica_translator # noqa - Do not remove this line class SchemasTest(unittest.TestCase): diff --git a/tests/model_connectors/data/jmodelica.py b/tests/model_connectors/data/jmodelica.py index d59b274a4..87d468a1e 100644 --- a/tests/model_connectors/data/jmodelica.py +++ b/tests/model_connectors/data/jmodelica.py @@ -4,16 +4,13 @@ ########################################################################## # Import the function for compilation of models and the load_fmu method -from pymodelica import compile_fmu -import traceback -import logging - -from pyfmi import load_fmu -import pymodelica - import os +import pymodelica import shutil import sys +from pyfmi import load_fmu +from pymodelica import compile_fmu + # import matplotlib.pyplot as plt debug_solver = False diff --git a/tests/model_connectors/test_ets.py b/tests/model_connectors/test_ets.py index b9685fbb8..eaf2c7427 100644 --- a/tests/model_connectors/test_ets.py +++ b/tests/model_connectors/test_ets.py @@ -1,29 +1,36 @@ -from ..context import geojson_modelica_translator # noqa - Do not remove this line - -import unittest -from geojson_modelica_translator.model_connectors.ets_template import ETS_Template import os +import unittest +from geojson_modelica_translator.model_connectors.ets_template import ETSTemplate +from ..context import geojson_modelica_translator # noqa - Do not remove this line -class ETS_ModelConnectorSingleBuildingTest(unittest.TestCase): - def setUp(self): # the first method/member must be setUp - folder_base = os.getcwd() - self.thermal_junction_properties_geojson = folder_base + "/geojson_modelica_translator/geojson/data/schemas/thermal_junction_properties.json" - self.system_parameters_geojson = folder_base + "/geojson_modelica_translator/system_parameters/schema.json" - self.ets_from_building_modelica = folder_base + "/geojson_modelica_translator/modelica/buildingslibrary/Buildings/Applications/DHC/EnergyTransferStations/CoolingIndirect.mo" - self.ets = ETS_Template(self.thermal_junction_properties_geojson, self.system_parameters_geojson, self.ets_from_building_modelica) +# TODO: do not mix upper camel case and snake case. Spell out ETS or use ETSModelConnector... +class ETSModelConnectorSingleBuildingTest(unittest.TestCase): + def setUp(self): # the first method/member must be setUp + base_folder = os.path.join(os.getcwd(), 'geojson_modelica_translator') + dest_path = "/geojson/data/schemas/thermal_junction_properties.json" + self.thermal_junction_properties_geojson = base_folder + dest_path + dest_path = "/system_parameters/schema.json" + self.system_parameters_geojson = base_folder + dest_path + dest_path = "/modelica/buildingslibrary/Buildings/Applications/DHC/EnergyTransferStations/CoolingIndirect.mo" + self.ets_from_building_modelica = base_folder + dest_path + + self.ets = ETSTemplate( + self.thermal_junction_properties_geojson, + self.system_parameters_geojson, + self.ets_from_building_modelica + ) self.assertIsNotNone(self.ets) return self.ets def test_ets_thermal_junction(self): ets_general = self.ets.check_ets_thermal_junction() - #print ("yanfei: ", ets_general) self.assertTrue(ets_general) def test_ets_system_parameters(self): - self.assertTrue(self.ets.check_system_parameters()) + self.assertIsNotNone(self.ets.check_ets_system_parameters()) def test_ets_from_building_modelica(self): self.assertTrue(self.ets.check_ets_from_building_modelica()) @@ -31,8 +38,8 @@ def test_ets_from_building_modelica(self): def test_ets_to_modelica(self): self.assertIsNotNone(self.ets.to_modelica()) - def test_ets_in_Dymola(self): - self.assertIsNotNone(self.ets.templated_ets_openloops_Dymola()) + def test_ets_in_dymola(self): + self.assertIsNotNone(self.ets.templated_ets_openloops_dymola()) if __name__ == '__main__': diff --git a/tests/model_connectors/test_spawn.py b/tests/model_connectors/test_spawn.py index 6cedd0d61..bcdf99e70 100644 --- a/tests/model_connectors/test_spawn.py +++ b/tests/model_connectors/test_spawn.py @@ -31,10 +31,10 @@ import os import unittest -from ..context import geojson_modelica_translator # noqa - Do not remove this line -from geojson_modelica_translator.system_parameters.system_parameters import SystemParameters from geojson_modelica_translator.geojson_modelica_translator import GeoJsonModelicaTranslator from geojson_modelica_translator.model_connectors.spawn import SpawnConnector +from geojson_modelica_translator.system_parameters.system_parameters import SystemParameters +from ..context import geojson_modelica_translator # noqa - Do not remove this line class SpawnModelConnectorSingleBuildingTest(unittest.TestCase): diff --git a/tests/modelica/test_input_parser.py b/tests/modelica/test_input_parser.py index d30c64520..35a66c868 100644 --- a/tests/modelica/test_input_parser.py +++ b/tests/modelica/test_input_parser.py @@ -28,13 +28,12 @@ **************************************************************************************************** """ -from ..context import geojson_modelica_translator # noqa - Do not remove this line - import filecmp import os import unittest from geojson_modelica_translator.modelica.input_parser import InputParser +from ..context import geojson_modelica_translator # noqa - Do not remove this line class InputParserTest(unittest.TestCase): diff --git a/tests/output_ets/ets_cooling_indirect_templated.mo b/tests/output_ets/ets_cooling_indirect_templated.mo new file mode 100644 index 000000000..13c8f8a1f --- /dev/null +++ b/tests/output_ets/ets_cooling_indirect_templated.mo @@ -0,0 +1,369 @@ + +within Buildings.Applications.DHC.EnergyTransferStations; + +model ets_cooling_indirect_templated + + + "Indirect cooling energy transfer station for district energy systems" + extends Buildings.Fluid.Interfaces.PartialFourPort( + redeclare final package Medium1 = Medium, + redeclare final package Medium2 = Medium); + + replaceable package Medium = + Modelica.Media.Interfaces.PartialMedium "Medium in the component"; + + + + // mass flow rates + parameter Modelica.SIunits.MassFlowRate m1_flow_nominal( + final min=0, + start=0.666) + "Nominal mass flow rate of primary (district) district cooling side"; + + + + parameter Modelica.SIunits.MassFlowRate m2_flow_nominal( + final min=0, + start=0.5) + "Nominal mass flow rate of secondary (building) district cooling side"; + + + + // Primary supply control valve + parameter Modelica.SIunits.PressureDifference dpValve_nominal( + final min=0, + final displayUnit="Pa")=888 + "Nominal pressure drop of fully open control valve"; + + + + // Heat exchanger + parameter Modelica.SIunits.PressureDifference dp1_nominal( + final min=0, + start=999, + final displayUnit="Pa") + "Nominal pressure difference on primary side" + annotation(Dialog(group="Heat exchanger")); + + + + parameter Modelica.SIunits.PressureDifference dp2_nominal( + final min=0, + start=999, + final displayUnit="Pa") + "Nominal pressure difference on secondary side" + annotation(Dialog(group="Heat exchanger")); + + + + parameter Boolean use_Q_flow_nominal=true + "Set to true to specify Q_flow_nominal and temperatures, or to false to specify effectiveness" + annotation(Dialog(group="Heat exchanger")); + + + + parameter Modelica.SIunits.HeatFlowRate Q_flow_nominal( + final min=0, + start=8000) + "Nominal heat transfer" + annotation(Dialog(group="Heat exchanger")); + + + + + parameter Modelica.SIunits.Temperature T_a1_nominal( + min=0+273, + max=100+273.15, + start=5+273.15, + final displayUnit="K") + "Nominal temperature at port a1" + annotation(Dialog(group="Heat exchanger")); + + + + parameter Modelica.SIunits.Temperature T_a2_nominal( + min=0+273, + max=100+273.15, + start=7+273.15, + final displayUnit="K") + "Nominal temperature at port a2" + annotation(Dialog(group="Heat exchanger")); + + + + parameter Modelica.SIunits.Efficiency eta( + final min=0, + final max=1)=0.666 + "Constant effectiveness" + annotation(Dialog(group="Heat exchanger")); + + + + // Controller parameters + parameter Modelica.Blocks.Types.SimpleController controllerType= + Modelica.Blocks.Types.SimpleController.PI + "Type of controller" + annotation(Dialog(tab="Controller")); + parameter Real k(final min=0, final unit="1") = 1 + "Gain of controller" + annotation(Dialog(tab="Controller")); + parameter Modelica.SIunits.Time Ti( + min=Modelica.Constants.small)=120 + "Time constant of integrator block" + annotation (Dialog(tab="Controller", enable= + controllerType == Modelica.Blocks.Types.SimpleController.PI or + controllerType == Modelica.Blocks.Types.SimpleController.PID)); + parameter Modelica.SIunits.Time Td(final min=0)=0.1 + "Time constant of derivative block" + annotation (Dialog(tab="Controller", enable= + controllerType == Modelica.Blocks.Types.SimpleController.PD or + controllerType == Modelica.Blocks.Types.SimpleController.PID)); + parameter Real yMax(start=1)=1 + "Upper limit of output" + annotation(Dialog(tab="Controller")); + parameter Real yMin=0 + "Lower limit of output" + annotation(Dialog(tab="Controller")); + parameter Real wp(final min=0) = 1 + "Set-point weight for Proportional block (0..1)" + annotation(Dialog(tab="Controller")); + parameter Real wd(final min=0) = 0 + "Set-point weight for Derivative block (0..1)" + annotation(Dialog(tab="Controller", enable= + controllerType==Modelica.Blocks.Types.SimpleController.PD or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real Ni(min=100*Modelica.Constants.eps) = 0.9 + "Ni*Ti is time constant of anti-windup compensation" + annotation(Dialog(tab="Controller", enable= + controllerType==Modelica.Blocks.Types.SimpleController.PI or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real Nd(min=100*Modelica.Constants.eps) = 10 + "The higher Nd, the more ideal the derivative block" + annotation(Dialog(tab="Controller", enable= + controllerType==Modelica.Blocks.Types.SimpleController.PD or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Modelica.Blocks.Types.InitPID initType= + Modelica.Blocks.Types.InitPID.DoNotUse_InitialIntegratorState + "Type of initialization (1: no init, 2: steady state, 3: initial state, 4: initial output)" + annotation(Evaluate=true, Dialog(group="Initialization", tab="Controller")); + parameter Real xi_start=0 + "Initial or guess value value for integrator output (= integrator state)" + annotation (Dialog(group="Initialization", tab="Controller", + enable=controllerType==Modelica.Blocks.Types.SimpleController.PI or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real xd_start=0 + "Initial or guess value for state of derivative block" + annotation (Dialog(group="Initialization", tab="Controller", + enable=controllerType==Modelica.Blocks.Types.SimpleController.PD or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real yCon_start=0 + "Initial value of output from the controller" + annotation(Dialog(group="Initialization", tab="Controller", + enable=initType == Modelica.Blocks.Types.InitPID.InitialOutput)); + parameter Boolean reverseAction = true + "Set to true for throttling the water flow rate through a cooling coil controller" + annotation(Dialog(tab="Controller")); + + Modelica.Blocks.Interfaces.RealInput TSet( + final quantity="ThermodynamicTemperature", + final unit="K") + "Setpoint temperature" + annotation (Placement(transformation(extent={{-140,-20},{-100,20}}))); + + Modelica.Blocks.Interfaces.RealOutput Q_flow( + final quantity="Power", + final unit="W", + final displayUnit="kW") + "Measured power demand at the ETS" + annotation (Placement(transformation(extent={{100,140},{120,160}}))); + + Modelica.Blocks.Interfaces.RealOutput Q( + final quantity="Energy", + final unit="J", + final displayUnit="kWh") + "Measured energy consumption at the ETS" + annotation (Placement(transformation(extent={{100,100},{120,120}}))); + + Buildings.Fluid.HeatExchangers.PlateHeatExchangerEffectivenessNTU hex( + redeclare final package Medium1 = Medium, + redeclare final package Medium2 = Medium, + final m1_flow_nominal=m1_flow_nominal, + final m2_flow_nominal=m2_flow_nominal, + final dp1_nominal=dp1_nominal, + final dp2_nominal=dp2_nominal, + final configuration=Buildings.Fluid.Types.HeatExchangerConfiguration.CounterFlow, + final use_Q_flow_nominal=true, + final Q_flow_nominal=Q_flow_nominal, + final T_a1_nominal=T_a1_nominal, + final T_a2_nominal=T_a2_nominal) "Indirect cooling heat exchanger" + annotation (Placement(transformation(extent={{20,-10},{40,10}}))); + + Buildings.Controls.Continuous.LimPID con( + final controllerType=Modelica.Blocks.Types.SimpleController.PID, + final k=k, + final Td=Td, + final yMax=yMax, + final yMin=yMin, + final Ti=Ti, + final wp=wp, + final wd=wd, + final Ni=Ni, + final Nd=Nd, + final initType=Modelica.Blocks.Types.InitPID.InitialOutput, + final xi_start=xi_start, + final xd_start=xd_start, + final y_start=yCon_start, + final reverseAction=reverseAction) "Controller" + annotation (Placement(transformation(extent={{-90,-10},{-70,10}}))); + + Buildings.Fluid.Sensors.TemperatureTwoPort senTDisSup( + redeclare final package Medium = Medium, + final m_flow_nominal=m1_flow_nominal) + "District-side (primary) supply temperature sensor" + annotation (Placement(transformation(extent={{-90,50},{-70,70}}))); + + Buildings.Fluid.Sensors.TemperatureTwoPort senTDisRet( + redeclare final package Medium = Medium, + final m_flow_nominal=m1_flow_nominal) + "District-side (primary) return temperature sensor" + annotation (Placement(transformation(extent={{70,50},{90,70}}))); + + Modelica.Blocks.Continuous.Integrator int(k=1) "Integration" + annotation (Placement(transformation(extent={{60,120},{80,100}}))); + + Buildings.Fluid.Sensors.MassFlowRate senMasFlo( + redeclare package Medium = Medium) + annotation (Placement(transformation(extent={{-60,50},{-40,70}}))); + + Buildings.Fluid.Sensors.TemperatureTwoPort TBuiRet( + redeclare final package Medium = Medium, + final m_flow_nominal=m2_flow_nominal) + "Building-side (secondary) return temperature" + annotation (Placement(transformation(extent={{-70,-70},{-90,-50}}))); + + Buildings.Fluid.Actuators.Valves.TwoWayQuickOpening val( + redeclare final package Medium = Medium, + final m_flow_nominal=m1_flow_nominal, + final dpValve_nominal=dpValve_nominal, + riseTime(displayUnit="s") = 60, + y_start=0) "District-side (primary) control valve" + annotation (Placement(transformation(extent={{-30,70},{-10,50}}))); + + Modelica.Blocks.Math.Gain cp(final k=cp_default) + "Specifc heat multiplier to calculate heat flow rate" + annotation (Placement(transformation(extent={{20,100},{40,120}}))); + + Modelica.Blocks.Math.Product pro "Product" + annotation (Placement(transformation(extent={{-20,100},{0,120}}))); + + Modelica.Blocks.Math.Add dTDis(k1=-1, k2=+1) + "Temperatur difference on the district side" + annotation (Placement(transformation(extent={{-60,106},{-40,126}}))); + +protected + final parameter Medium.ThermodynamicState sta_default = Medium.setState_pTX( + T=Medium.T_default, + p=Medium.p_default, + X=Medium.X_default) "Medium state at default properties"; + final parameter Modelica.SIunits.SpecificHeatCapacity cp_default= + Medium.specificHeatCapacityCp(sta_default) + "Specific heat capacity of the fluid"; + +equation + + connect(hex.port_a2, port_a2) annotation (Line(points={{40,-6},{60,-6},{60,-60}, + {100,-60}}, color={0,127,255})); + connect(hex.port_b1, senTDisRet.port_a) annotation (Line(points={{40,6},{60,6}, + {60,60},{70,60}}, color={0,127,255})); + connect(val.port_b, hex.port_a1) annotation (Line(points={{-10,60},{0,60},{0,6}, + {20,6}}, color={0,127,255})); + connect(senMasFlo.port_b, val.port_a) + annotation (Line(points={{-40,60},{-30,60}}, color={0,127,255})); + connect(port_a1, senTDisSup.port_a) + annotation (Line(points={{-100,60},{-90,60}}, color={0,127,255})); + connect(senTDisSup.port_b, senMasFlo.port_a) + annotation (Line(points={{-70,60},{-60,60}}, color={0,127,255})); + connect(port_b2, TBuiRet.port_b) + annotation (Line(points={{-100,-60},{-90,-60}}, color={0,127,255})); + connect(senTDisRet.port_b, port_b1) + annotation (Line(points={{90,60},{100,60}}, color={0,127,255})); + connect(TSet, con.u_s) + annotation (Line(points={{-120,0},{-106,0},{-106,0},{-92,0}}, + color={0,0,127})); + connect(con.u_m, TBuiRet.T) + annotation (Line(points={{-80,-12},{-80,-49}}, color={0,0,127})); + connect(con.y, val.y) + annotation (Line(points={{-69,0},{-20,0},{-20,48}}, color={0,0,127})); + connect(TBuiRet.port_a, hex.port_b2) annotation (Line(points={{-70,-60},{0,-60}, + {0,-6},{20,-6}}, color={0,127,255})); + connect(pro.y, cp.u) + annotation (Line(points={{1,110},{18,110}}, color={0,0,127})); + connect(senMasFlo.m_flow, pro.u2) + annotation (Line(points={{-50,71},{-50,104},{-22,104}}, color={0,0,127})); + connect(senTDisSup.T, dTDis.u1) + annotation (Line(points={{-80,71},{-80,122},{-62,122}}, color={0,0,127})); + connect(senTDisRet.T, dTDis.u2) annotation (Line(points={{80,71},{80,80},{-70, + 80},{-70,110},{-62,110}}, color={0,0,127})); + connect(dTDis.y, pro.u1) + annotation (Line(points={{-39,116},{-22,116}}, color={0,0,127})); + connect(cp.y, int.u) + annotation (Line(points={{41,110},{58,110}}, color={0,0,127})); + connect(int.y, Q) + annotation (Line(points={{81,110},{110,110}}, color={0,0,127})); + connect(Q_flow, cp.y) annotation (Line(points={{110,150},{50,150},{50,110},{41, + 110}}, color={0,0,127})); + +annotation (defaultComponentName="coo", + Icon(coordinateSystem(preserveAspectRatio=false), graphics={ + Rectangle( + extent={{-100,-56},{100,-64}}, + fillColor={0,0,0}, + fillPattern=FillPattern.Solid, + pattern=LinePattern.None), + Rectangle( + extent={{-100,64},{100,56}}, + fillColor={0,0,0}, + fillPattern=FillPattern.Solid, + pattern=LinePattern.None), + Rectangle( + extent={{-80,80},{80,-80}}, + lineColor={175,175,175}, + fillColor={35,138,255}, + fillPattern=FillPattern.Solid), + Text( + extent={{-52,40},{54,-40}}, + lineColor={0,0,0}, + fillColor={35,138,255}, + fillPattern=FillPattern.Solid, + textStyle={TextStyle.Bold}, + textString="ETS")}), Diagram( + coordinateSystem(preserveAspectRatio=false, extent={{-100,-100},{100,160}})), + Documentation(info=" +

    +Indirect cooling energy transfer station (ETS) model that controls +the building chilled water supply temperature by modulating a +primary control valve on the district supply side. The design is +based on a typical district cooling ETS described in ASHRAE's + +District Cooling Guide. +As shown in the figure below, the building pumping design (constant, +variable) is specified on the building side, not within the ETS. +

    +

    +\"DHC.ETS.CoolingIndirect\"/ +

    +

    Reference

    +

    +American Society of Heating, Refrigeration and Air-Conditioning +Engineers. (2013). Chapter 5: End User Interface. In +District Cooling Guide. 1st Edition. +

    +", revisions=" +
      +
    • +November 1, 2019, by Kathryn Hinkelman:
      +First implementation.
    • +
    • 12/15/2020, Yanfei Li, Templating ETS model.
    • +
    +")); +end ets_cooling_indirect_templated; \ No newline at end of file diff --git a/tests/test_translator.py b/tests/test_translator.py index 8db539492..770ed093f 100644 --- a/tests/test_translator.py +++ b/tests/test_translator.py @@ -28,14 +28,14 @@ **************************************************************************************************** """ +import itertools import os import shutil import unittest -import itertools -from .context import geojson_modelica_translator # noqa - Do not remove this line from geojson_modelica_translator.geojson_modelica_translator import GeoJsonModelicaTranslator from geojson_modelica_translator.system_parameters.system_parameters import SystemParameters +from .context import geojson_modelica_translator # noqa - Do not remove this line class GeoJSONTranslatorTest(unittest.TestCase): diff --git a/tests/test_utils.py b/tests/test_utils.py index 56ae1567a..2241d1eeb 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -28,12 +28,11 @@ **************************************************************************************************** """ -from .context import geojson_modelica_translator # noqa - Do not remove this line - import os import unittest from geojson_modelica_translator.utils import ModelicaPath +from .context import geojson_modelica_translator # noqa - Do not remove this line class ModelicaPathTest(unittest.TestCase): From c735df69ca907ec9ca4ee4706d7a250e951d68bf Mon Sep 17 00:00:00 2001 From: Li Date: Wed, 26 Feb 2020 10:50:46 -0700 Subject: [PATCH 36/62] final version of template_ets, with: integration of develop-branch and pre-commit revise long comment lines update ets_template comments --- .coveragerc | 2 +- .pre-commit-config.yaml | 30 +++ CHANGELOG.rst | 1 - Makefile | 1 - README.rst | 10 +- docs/conf.py | 132 ++++++------ docs/index.rst | 1 - ets_cooling_indirect.mo | 60 +++--- geojson_modelica_translator/cli.py | 9 +- .../data/schemas/building_properties.json | 2 +- .../schemas/district_system_properties.json | 2 +- .../electrical_connector_properties.json | 2 +- .../electrical_junction_properties.json | 2 +- .../data/schemas/region_properties.json | 2 +- .../geojson/data/schemas/site_properties.json | 2 +- .../schemas/thermal_connector_properties.json | 2 +- .../schemas/thermal_junction_properties.json | 2 +- .../geojson/schemas.py | 22 +- .../geojson/urbanopt_geojson.py | 19 +- .../geojson_modelica_translator.py | 34 +-- .../ets_cooling_indirect_2.mo | 22 +- .../model_connectors/ets_template.py | 156 +++++++++----- .../model_connectors/spawn.py | 141 ++++++++----- .../model_connectors/teaser.py | 197 ++++++++++++------ .../templates/CoolingIndirect.mot | 22 +- .../modelica/input_parser.py | 136 +++++++----- .../system_parameters/schema.json | 2 +- .../system_parameters/system_parameters.py | 20 +- geojson_modelica_translator/utils.py | 6 +- management/update_licenses.py | 37 ++-- management/update_schemas.py | 33 ++- setup.cfg | 1 - setup.py | 61 +++--- tests/context.py | 2 +- tests/geojson/test_geojson.py | 17 +- tests/geojson/test_schemas.py | 20 +- .../RefBldgSmallOfficeNew2004_Chicago.idf | 1 - tests/model_connectors/data/jmodelica.py | 45 ++-- .../data/spawn_system_params_ex2.json | 2 - tests/model_connectors/test_ets.py | 6 +- tests/model_connectors/test_spawn.py | 41 ++-- tests/modelica/data/package.mo | 2 +- tests/modelica/test_input_parser.py | 86 +++++--- .../ets_cooling_indirect_templated.mo | 20 +- .../test_system_parameters.py | 74 ++++--- tests/test_translator.py | 124 +++++++---- tests/test_utils.py | 20 +- 47 files changed, 971 insertions(+), 660 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.coveragerc b/.coveragerc index 481f6cb74..1b8dfd227 100644 --- a/.coveragerc +++ b/.coveragerc @@ -11,7 +11,7 @@ source = [report] omit = geojson_modelica_translator/cli.py - + # Regexes for lines to exclude from consideration exclude_lines = # Have to re-enable the standard pragma diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..755a0fcc8 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,30 @@ +exclude: | + (?x)( + ^docs/conf.py| + ^modelica_builder/modelica_parser/ + ) + +repos: +- repo: git://github.com/pre-commit/pre-commit-hooks + rev: v2.2.3 + hooks: + - id: trailing-whitespace + - id: check-added-large-files + - id: check-ast + - id: check-json + - id: check-merge-conflict + - id: check-xml + - id: check-yaml + - id: debug-statements + - id: end-of-file-fixer + - id: requirements-txt-fixer + - id: mixed-line-ending + args: ['--fix=no'] + - id: flake8 + args: ['--max-line-length=100'] # default of Black + +- repo: https://github.com/pre-commit/mirrors-isort + rev: v4.3.4 + hooks: + - id: isort + diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f697415d6..dca6656b4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -8,4 +8,3 @@ This is the initial release of the package and includes the following functional * Create a ROM/RC model using Modelica 3.2.x, Modelica Buildings Library 7.0 and TEASER 0.7.2 (prerelease) * Create a Spawn-based models which loads an IDF file - diff --git a/Makefile b/Makefile index 3e5d83211..e3f11e3e6 100644 --- a/Makefile +++ b/Makefile @@ -3,4 +3,3 @@ init: test: py.test - diff --git a/README.rst b/README.rst index 19aca3cd7..7aed1c393 100644 --- a/README.rst +++ b/README.rst @@ -24,9 +24,9 @@ currently you need python3 and pip3 to install/build the packages. .. code-block:: bash - pip3 install -r requirements.txt - python3 setup.py build - python3 setup.py test + pip install -r requirements.txt + python setup.py build + python setup.py test The py.test tests should all pass assuming the libraries are installed correctly on your development computer. Also, there will be a set of Modelica models that are created and persisted into the `tests/output` folder. @@ -72,13 +72,13 @@ will automatically run the models without having to follow the steps below. * Clone https://github.com/lbl-srg/docker-ubuntu-jmodelica and follow the set up instructions. * Copy jmodelica.py (from docker-ubuntu-jmodelica) to root of project where you will simulate (e.g., geojson-modelica-translator/tests/model_connectors/output) -* Pull https://github.com/lbl-srg/modelica-buildings/tree/issue1442_loadCoupling +* Pull https://github.com/lbl-srg/modelica-buildings/tree/issue1442_loadCoupling * **Make sure you have git-lfs installed**. You may need to checkout out the library again after install lfs. * Please make sure you are in the issue1442_loadCoupling branch. * Mac: `brew install git-lfs; git lfs install` * Ubuntu: `sudo apt install git-lfs; git lfs install` * Add the Buildings Library path to your MODELICAPATH environment variable (e.g., export MODELICAPATH=${MODELICAPATH}:/home//github/modelica-buildings). -* Example simulation: +* Example simulation: * `jm_ipython.sh jmodelica.py spawn_two_building.Loads.B5a6b99ec37f4de7f94020090.building` * `jm_ipython.sh jmodelica.py spawn_two_building/Loads/B5a6b99ec37f4de7f94020090/building.mo` * Visualize the results by inspecting the resulting mat file using BuildingsPy. diff --git a/docs/conf.py b/docs/conf.py index 6a1793ef5..6eb9e28ff 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,32 +17,32 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) +# sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'GeoJSON Modelica Translator' -copyright = u'2019, Alliance for Sustainable Energy, LLC' +project = u"GeoJSON Modelica Translator" +copyright = u"2019, Alliance for Sustainable Energy, LLC" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -50,74 +50,74 @@ # # TODO: grab version from setup.py # The short X.Y version. -version = 'v0.0.1' +version = "v0.0.1" # The full version, including alpha/beta/rc tags. -release = 'v0.0.1' +release = "v0.0.1" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] +exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -126,47 +126,47 @@ # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'sampledoc' +htmlhelp_basename = "sampledoc" # -- Options for LaTeX output -------------------------------------------------- @@ -174,10 +174,8 @@ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. # 'preamble': '', } @@ -185,29 +183,34 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'GeoJSON Modelica Translator.tex', u'GeoJSON Modelica Translator Documentation', - u'Nicholas Long', 'manual'), + ( + "index", + "GeoJSON Modelica Translator.tex", + u"GeoJSON Modelica Translator Documentation", + u"Nicholas Long", + "manual", + ) ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output -------------------------------------------- @@ -215,12 +218,17 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'GeoJSON Modelica Translator', u'GeoJSON Modelica Translator Documentation', - [u'Nicholas Long'], 1) + ( + "index", + "GeoJSON Modelica Translator", + u"GeoJSON Modelica Translator Documentation", + [u"Nicholas Long"], + 1, + ) ] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ @@ -229,16 +237,22 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'GeoJSON Modelica Translator', u'GeoJSON Modelica Translator Documentation', - u'Nicholas Long', 'GeoJSON Modelica Translator', 'One line description of project.', - 'Miscellaneous'), + ( + "index", + "GeoJSON Modelica Translator", + u"GeoJSON Modelica Translator Documentation", + u"Nicholas Long", + "GeoJSON Modelica Translator", + "One line description of project.", + "Miscellaneous", + ) ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' diff --git a/docs/index.rst b/docs/index.rst index 4ee513ee5..057428500 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -17,4 +17,3 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` - diff --git a/ets_cooling_indirect.mo b/ets_cooling_indirect.mo index 50e2c1612..eb1702f07 100644 --- a/ets_cooling_indirect.mo +++ b/ets_cooling_indirect.mo @@ -11,19 +11,19 @@ model CoolingIndirect Modelica.Media.Interfaces.PartialMedium "Medium in the component"; - + // mass flow rates parameter Modelica.SIunits.MassFlowRate m1_flow_nominal( final min=0, start=0.666) "Nominal mass flow rate of primary (district) district cooling side"; - - - - + + + + // Heat exchanger parameter Modelica.SIunits.PressureDifference dp1_nominal( final min=0, @@ -31,33 +31,33 @@ model CoolingIndirect final displayUnit="Pa") "Nominal pressure difference on primary side" annotation(Dialog(group="Heat exchanger")); - - + + parameter Modelica.SIunits.PressureDifference dp2_nominal( final min=0, start=999, final displayUnit="Pa") "Nominal pressure difference on secondary side" annotation(Dialog(group="Heat exchanger")); - - + + parameter Boolean use_Q_flow_nominal=true "Set to true to specify Q_flow_nominal and temperatures, or to false to specify effectiveness" annotation(Dialog(group="Heat exchanger")); - - + + parameter Modelica.SIunits.HeatFlowRate Q_flow_nominal( final min=0, start=8000) "Nominal heat transfer" annotation(Dialog(group="Heat exchanger")); - - + + parameter Modelica.SIunits.Temperature T_a1_nominal( min=0+273, max=100+273.15, @@ -65,9 +65,9 @@ model CoolingIndirect final displayUnit="K") "Nominal temperature at port a1" annotation(Dialog(group="Heat exchanger")); - - + + parameter Modelica.SIunits.Temperature T_a2_nominal( min=0+273, max=100+273.15, @@ -75,17 +75,17 @@ model CoolingIndirect final displayUnit="K") "Nominal temperature at port a2" annotation(Dialog(group="Heat exchanger")); - - + + parameter Modelica.SIunits.Efficiency eta( final min=0, final max=1)=0.666 "Constant effectiveness" annotation(Dialog(group="Heat exchanger")); - - + + // Controller parameters parameter Modelica.Blocks.Types.SimpleController controllerType= Modelica.Blocks.Types.SimpleController.PI @@ -327,23 +327,23 @@ annotation (defaultComponentName="coo", coordinateSystem(preserveAspectRatio=false, extent={{-100,-100},{100,160}})), Documentation(info="

    -Indirect cooling energy transfer station (ETS) model that controls -the building chilled water supply temperature by modulating a -primary control valve on the district supply side. The design is -based on a typical district cooling ETS described in ASHRAE's +Indirect cooling energy transfer station (ETS) model that controls +the building chilled water supply temperature by modulating a +primary control valve on the district supply side. The design is +based on a typical district cooling ETS described in ASHRAE's -District Cooling Guide. -As shown in the figure below, the building pumping design (constant, -variable) is specified on the building side, not within the ETS. +District Cooling Guide. +As shown in the figure below, the building pumping design (constant, +variable) is specified on the building side, not within the ETS.

    \"DHC.ETS.CoolingIndirect\"/

    Reference

    -American Society of Heating, Refrigeration and Air-Conditioning -Engineers. (2013). Chapter 5: End User Interface. In -District Cooling Guide. 1st Edition. +American Society of Heating, Refrigeration and Air-Conditioning +Engineers. (2013). Chapter 5: End User Interface. In +District Cooling Guide. 1st Edition.

    ", revisions="
      @@ -352,4 +352,4 @@ November 1, 2019, by Kathryn Hinkelman:
      First implementation.
    ")); -end CoolingIndirect; \ No newline at end of file +end CoolingIndirect; diff --git a/geojson_modelica_translator/cli.py b/geojson_modelica_translator/cli.py index d56724ea8..b463cb638 100644 --- a/geojson_modelica_translator/cli.py +++ b/geojson_modelica_translator/cli.py @@ -35,11 +35,10 @@ # Work in progress + def parse_args(args): parser = argparse.ArgumentParser(description="Parser") - parser.set_defaults(log_level=logging.WARNING, - extensions=[], - command=run) + parser.set_defaults(log_level=logging.WARNING, extensions=[], command=run) opts = vars(parser.parse_args(args)) return opts @@ -51,7 +50,7 @@ def main(args): args ([str]): command line arguments """ opts = parse_args(args) - opts['command'](opts) + opts["command"](opts) def run(): @@ -59,5 +58,5 @@ def run(): main(sys.argv[1:]) -if __name__ == '__main__': +if __name__ == "__main__": main(sys.argv[1:]) diff --git a/geojson_modelica_translator/geojson/data/schemas/building_properties.json b/geojson_modelica_translator/geojson/data/schemas/building_properties.json index 3c936d1b5..15abbe51f 100644 --- a/geojson_modelica_translator/geojson/data/schemas/building_properties.json +++ b/geojson_modelica_translator/geojson/data/schemas/building_properties.json @@ -353,4 +353,4 @@ ] } } -} \ No newline at end of file +} diff --git a/geojson_modelica_translator/geojson/data/schemas/district_system_properties.json b/geojson_modelica_translator/geojson/data/schemas/district_system_properties.json index 0fb110213..b0abfff6b 100644 --- a/geojson_modelica_translator/geojson/data/schemas/district_system_properties.json +++ b/geojson_modelica_translator/geojson/data/schemas/district_system_properties.json @@ -131,4 +131,4 @@ ] } } -} \ No newline at end of file +} diff --git a/geojson_modelica_translator/geojson/data/schemas/electrical_connector_properties.json b/geojson_modelica_translator/geojson/data/schemas/electrical_connector_properties.json index c2dbcbe52..f3e80c936 100644 --- a/geojson_modelica_translator/geojson/data/schemas/electrical_connector_properties.json +++ b/geojson_modelica_translator/geojson/data/schemas/electrical_connector_properties.json @@ -72,4 +72,4 @@ ] } } -} \ No newline at end of file +} diff --git a/geojson_modelica_translator/geojson/data/schemas/electrical_junction_properties.json b/geojson_modelica_translator/geojson/data/schemas/electrical_junction_properties.json index d507c22f2..a3f72d0b6 100644 --- a/geojson_modelica_translator/geojson/data/schemas/electrical_junction_properties.json +++ b/geojson_modelica_translator/geojson/data/schemas/electrical_junction_properties.json @@ -58,4 +58,4 @@ ] } } -} \ No newline at end of file +} diff --git a/geojson_modelica_translator/geojson/data/schemas/region_properties.json b/geojson_modelica_translator/geojson/data/schemas/region_properties.json index f07e74c51..4aea02211 100644 --- a/geojson_modelica_translator/geojson/data/schemas/region_properties.json +++ b/geojson_modelica_translator/geojson/data/schemas/region_properties.json @@ -88,4 +88,4 @@ "region_type" ], "additionalProperties": false -} \ No newline at end of file +} diff --git a/geojson_modelica_translator/geojson/data/schemas/site_properties.json b/geojson_modelica_translator/geojson/data/schemas/site_properties.json index 48a9c0911..db44a6bee 100644 --- a/geojson_modelica_translator/geojson/data/schemas/site_properties.json +++ b/geojson_modelica_translator/geojson/data/schemas/site_properties.json @@ -75,4 +75,4 @@ "longitude" ], "additionalProperties": false -} \ No newline at end of file +} diff --git a/geojson_modelica_translator/geojson/data/schemas/thermal_connector_properties.json b/geojson_modelica_translator/geojson/data/schemas/thermal_connector_properties.json index 56e2fff9f..0c87bb4bc 100644 --- a/geojson_modelica_translator/geojson/data/schemas/thermal_connector_properties.json +++ b/geojson_modelica_translator/geojson/data/schemas/thermal_connector_properties.json @@ -100,4 +100,4 @@ ] } } -} \ No newline at end of file +} diff --git a/geojson_modelica_translator/geojson/data/schemas/thermal_junction_properties.json b/geojson_modelica_translator/geojson/data/schemas/thermal_junction_properties.json index 318ec952b..f496d6c30 100644 --- a/geojson_modelica_translator/geojson/data/schemas/thermal_junction_properties.json +++ b/geojson_modelica_translator/geojson/data/schemas/thermal_junction_properties.json @@ -76,4 +76,4 @@ ] } } -} \ No newline at end of file +} diff --git a/geojson_modelica_translator/geojson/schemas.py b/geojson_modelica_translator/geojson/schemas.py index 764fcc482..5424ef9ad 100644 --- a/geojson_modelica_translator/geojson/schemas.py +++ b/geojson_modelica_translator/geojson/schemas.py @@ -42,18 +42,20 @@ class Schemas(object): def __init__(self): """Load in the schemas""" self.schemas = { - 'building': None, - 'district_system': None, - 'electrical_connector': None, - 'electrical_junction': None, - 'region': None, - 'site': None, - 'thermal_connector': None, - 'thermal_junction': None, + "building": None, + "district_system": None, + "electrical_connector": None, + "electrical_junction": None, + "region": None, + "site": None, + "thermal_connector": None, + "thermal_junction": None, } for s in self.schemas.keys(): - path = os.path.join(os.path.dirname(__file__), "data/schemas/%s_properties.json" % s) + path = os.path.join( + os.path.dirname(__file__), "data/schemas/%s_properties.json" % s + ) self.schemas[s] = json.load(open(path, "r")) def retrieve(self, name): @@ -61,7 +63,7 @@ def retrieve(self, name): if self.schemas.get(name): return self.schemas[name] else: - raise Exception('Schema for %s does not exist' % name) + raise Exception("Schema for %s does not exist" % name) def validate(self, name, instance): """ diff --git a/geojson_modelica_translator/geojson/urbanopt_geojson.py b/geojson_modelica_translator/geojson/urbanopt_geojson.py index 6e6e6dd22..e2bb6c69d 100644 --- a/geojson_modelica_translator/geojson/urbanopt_geojson.py +++ b/geojson_modelica_translator/geojson/urbanopt_geojson.py @@ -46,8 +46,8 @@ class UrbanOptBuilding(object): def __init__(self, feature): self.feature = feature - self.id = feature.get('properties', {}).get('id', 'NO ID') - self.dirname = f'B{self.id}' + self.id = feature.get("properties", {}).get("id", "NO ID") + self.dirname = f"B{self.id}" class UrbanOptGeoJson(object): @@ -60,7 +60,7 @@ def __init__(self, filename): if os.path.exists(filename): self.data = geojson.load(open(filename)) else: - raise Exception(f'URBANopt GeoJSON file does not exist: {filename}') + raise Exception(f"URBANopt GeoJSON file does not exist: {filename}") # load the shemas self.schemas = Schemas() @@ -68,7 +68,7 @@ def __init__(self, filename): # break up the file based on the various features self.buildings = [] for f in self.data.features: - if f['properties']['type'] == 'Building': + if f["properties"]["type"] == "Building": self.buildings.append(UrbanOptBuilding(f)) def validate(self): @@ -78,18 +78,15 @@ def validate(self): :return: dict of lists, errors for each of the types """ validations = defaultdict(dict) - validations['building'] = [] + validations["building"] = [] status = True # go through building properties for validation for b in self.buildings: - val_res = self.schemas.validate('building', b.feature.properties) + val_res = self.schemas.validate("building", b.feature.properties) if len(val_res) > 0: status = False - res = { - "id": b.feature.properties['id'], - "errors": val_res, - } - validations['building'].append(res) + res = {"id": b.feature.properties["id"], "errors": val_res} + validations["building"].append(res) return status, validations diff --git a/geojson_modelica_translator/geojson_modelica_translator.py b/geojson_modelica_translator/geojson_modelica_translator.py index e9b948058..e2ef35477 100644 --- a/geojson_modelica_translator/geojson_modelica_translator.py +++ b/geojson_modelica_translator/geojson_modelica_translator.py @@ -71,7 +71,7 @@ def from_geojson(cls, filename): klass.buildings = json.buildings return klass else: - raise Exception(f'GeoJSON file does not exist: {filename}') + raise Exception(f"GeoJSON file does not exist: {filename}") def set_system_parameters(self, sys_params): """ @@ -91,16 +91,20 @@ def scaffold_directory(self, root_dir, overwrite=False): # TODO: need to be careful with this. If we are mixing load models, then we need to not remove the entire path if os.path.exists(root_dir): if overwrite: - raise Exception("Directory already exists and overwrite is false for %s" % root_dir) + raise Exception( + "Directory already exists and overwrite is false for %s" % root_dir + ) else: shutil.rmtree(root_dir) - self.loads_path = ModelicaPath('Loads', root_dir=root_dir) - self.substations_path = ModelicaPath('Substations', root_dir=root_dir) - self.plants_path = ModelicaPath('Plants', root_dir=root_dir) - self.districts_path = ModelicaPath('Districts', root_dir=root_dir) + self.loads_path = ModelicaPath("Loads", root_dir=root_dir) + self.substations_path = ModelicaPath("Substations", root_dir=root_dir) + self.plants_path = ModelicaPath("Plants", root_dir=root_dir) + self.districts_path = ModelicaPath("Districts", root_dir=root_dir) - def to_modelica(self, project_name, save_dir, model_connector_str='TeaserConnector'): + def to_modelica( + self, project_name, save_dir, model_connector_str="TeaserConnector" + ): """ Convert the data in the GeoJSON to modelica based-objects @@ -108,30 +112,34 @@ def to_modelica(self, project_name, save_dir, model_connector_str='TeaserConnect {save_dir}/{project_name} :param model_connector_str: str, which model_connector to use """ - prj_dir = f'{save_dir}/{project_name}' + prj_dir = f"{save_dir}/{project_name}" self.scaffold_directory(prj_dir) # TODO: Handle other connectors -- create map based on model_connector_str import geojson_modelica_translator.model_connectors.teaser + import geojson_modelica_translator.model_connectors.spawn mc_klass = getattr(geojson_modelica_translator.model_connectors.teaser, model_connector_str) + model_connector = mc_klass(self.system_parameters) - _log.info('Exporting to Modelica') + _log.info("Exporting to Modelica") for building in self.buildings: - _log.info(f'Adding building to model connector: {mc_klass.__class__}') + _log.info(f"Adding building to model connector: {mc_klass.__class__}") model_connector.add_building(building) - _log.info(f'Translating building to model {building}') - model_connector.to_modelica(project_name, self.loads_path.files_dir, keep_original_models=False) + _log.info(f"Translating building to model {building}") + model_connector.to_modelica( + project_name, self.loads_path.files_dir, keep_original_models=False + ) # add in Substations # add in Districts # add in Plants # now add in the top level package. - pp = PackageParser.new_from_template(prj_dir, project_name, ['Loads']) + pp = PackageParser.new_from_template(prj_dir, project_name, ["Loads"]) pp.save() # TODO: BuildingModelClass diff --git a/geojson_modelica_translator/model_connectors/ets_cooling_indirect_2.mo b/geojson_modelica_translator/model_connectors/ets_cooling_indirect_2.mo index 472bfa259..9d2b886e0 100644 --- a/geojson_modelica_translator/model_connectors/ets_cooling_indirect_2.mo +++ b/geojson_modelica_translator/model_connectors/ets_cooling_indirect_2.mo @@ -304,23 +304,23 @@ annotation (defaultComponentName="coo", coordinateSystem(preserveAspectRatio=false, extent={{-100,-100},{100,160}})), Documentation(info="

    -Indirect cooling energy transfer station (ETS) model that controls -the building chilled water supply temperature by modulating a -primary control valve on the district supply side. The design is -based on a typical district cooling ETS described in ASHRAE's +Indirect cooling energy transfer station (ETS) model that controls +the building chilled water supply temperature by modulating a +primary control valve on the district supply side. The design is +based on a typical district cooling ETS described in ASHRAE's -District Cooling Guide. -As shown in the figure below, the building pumping design (constant, -variable) is specified on the building side, not within the ETS. +District Cooling Guide. +As shown in the figure below, the building pumping design (constant, +variable) is specified on the building side, not within the ETS.

    \"DHC.ETS.CoolingIndirect\"/

    Reference

    -American Society of Heating, Refrigeration and Air-Conditioning -Engineers. (2013). Chapter 5: End User Interface. In -District Cooling Guide. 1st Edition. +American Society of Heating, Refrigeration and Air-Conditioning +Engineers. (2013). Chapter 5: End User Interface. In +District Cooling Guide. 1st Edition.

    ", revisions="
      @@ -329,4 +329,4 @@ November 1, 2019, by Kathryn Hinkelman:
      First implementation.
    ")); -end CoolingIndirect; \ No newline at end of file +end CoolingIndirect; diff --git a/geojson_modelica_translator/model_connectors/ets_template.py b/geojson_modelica_translator/model_connectors/ets_template.py index 30d23db1d..e721276e6 100644 --- a/geojson_modelica_translator/model_connectors/ets_template.py +++ b/geojson_modelica_translator/model_connectors/ets_template.py @@ -1,13 +1,20 @@ import json import os -from jinja2 import FileSystemLoader, Environment +from jinja2 import Environment, FileSystemLoader # TODO: Class name should be upper camel case, not a mix of camel and snake case. -class ETSTemplate(): + + +class ETSTemplate: """This class will template the ETS modelica model.""" - def __init__(self, thermal_junction_properties_geojson, system_parameters_geojson, ets_from_building_modelica): + def __init__( + self, + thermal_junction_properties_geojson, + system_parameters_geojson, + ets_from_building_modelica, + ): super().__init__() """ thermal_junction_properties_geojson contains the ETS at brief and at higher level; @@ -15,28 +22,42 @@ def __init__(self, thermal_junction_properties_geojson, system_parameters_geojso ets_from_building_modelica contains the modelica model of ETS ; """ self.thermal_junction_properties_geojson = thermal_junction_properties_geojson - self.thermal_junction_properties_geojson = self.thermal_junction_properties_geojson.replace("\\", "/") + self.thermal_junction_properties_geojson = self.thermal_junction_properties_geojson.replace( + "\\", "/" + ) self.system_parameters_geojson = system_parameters_geojson if "\\" in self.system_parameters_geojson: - self.system_parameters_geojson = self.system_parameters_geojson.replace("\\" , "/") + self.system_parameters_geojson = self.system_parameters_geojson.replace( + "\\", "/" + ) self.ets_from_building_modelica = ets_from_building_modelica if "\\" in self.ets_from_building_modelica: - self.ets_from_building_modelica = self.ets_from_building_modelica.replace("\\", "/") + self.ets_from_building_modelica = self.ets_from_building_modelica.replace( + "\\", "/" + ) # get the path of modelica-buildings library directory_up_one_levels = os.path.abspath((os.path.join(__file__, "../../"))) dest_path = "/modelica/buildingslibrary/Buildings/Applications/DHC/EnergyTransferStations/" - self.directory_modelica_building = os.path.join(directory_up_one_levels + dest_path) + self.directory_modelica_building = os.path.join( + directory_up_one_levels + dest_path + ) if "\\" in self.directory_modelica_building: - self.directory_modelica_building = self.directory_modelica_building.replace("\\", "/") + self.directory_modelica_building = self.directory_modelica_building.replace( + "\\", "/" + ) # go up two levels of directory, to get the path of tests folder for ets directory_up_two_levels = os.path.abspath(os.path.join(__file__, "../../..")) - self.directory_ets_templated = os.path.join(directory_up_two_levels + "/tests/output_ets") + self.directory_ets_templated = os.path.join( + directory_up_two_levels + "/tests/output_ets" + ) if "\\" in self.directory_ets_templated: - self.directory_ets_templated = self.directory_ets_templated.replace("\\", "/") + self.directory_ets_templated = self.directory_ets_templated.replace( + "\\", "/" + ) if not os.path.isdir(self.directory_ets_templated): os.mkdir(self.directory_ets_templated) @@ -46,20 +67,24 @@ def __init__(self, thermal_junction_properties_geojson, system_parameters_geojso # here comes the Jinja2 function: Environment() # it loads all the "*.mot" files into an environment by Jinja2 self.template_env = Environment( - loader=FileSystemLoader(searchpath=os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates')) + loader=FileSystemLoader( + searchpath=os.path.join( + os.path.dirname(os.path.abspath(__file__)), "templates" + ) + ) ) def check_ets_thermal_junction(self): """check if ETS info are in thermal-junction-geojson file""" - with open(self.thermal_junction_properties_geojson, 'r') as f: + with open(self.thermal_junction_properties_geojson, "r") as f: data = json.load(f) ets_general = False for key, value in data.items(): - if key == 'definitions': + if key == "definitions": # three levels down to get the ETS signal junctions = data["definitions"]["ThermalJunctionType"]["enum"] - if 'ETS' in junctions: + if "ETS" in junctions: ets_general = True else: pass @@ -68,15 +93,15 @@ def check_ets_thermal_junction(self): def check_ets_system_parameters(self): """check detailed parameters of ETS""" - with open(self.system_parameters_geojson, 'r') as f: + with open(self.system_parameters_geojson, "r") as f: data = json.load(f) ets_parameters = False # four levels down to get the ets model description - ets_overall = data["definitions"]["building_def"]["properties"]["ets"] + # ets_overall = data["definitions"]["building_def"]["properties"]["ets"] # three levels down to get the parameters ets_parameters = data["definitions"]["ets_parameters"]["properties"] - #print ("est_parameters are: ", type(ets_parameters) ) + # print ("est_parameters are: ", type(ets_parameters) ) return ets_parameters def check_ets_from_building_modelica(self): @@ -87,39 +112,70 @@ def check_ets_from_building_modelica(self): def to_modelica(self): """convert ETS json to modelica""" - # Here come the Jinja2 function: get_template(), which will read into the templated_ets model. - # CoolingIndirect.mot was manually created as a starting point, by adding stuff following Jinja2. + # Here come the Jinja2 function: get_template(), which reads into templated ets model. + # CoolingIndirect.mot was manually created as a starting point, + # by adding stuff following Jinja2 syntax. # it has all the necessary parameters which need to be changed through templating. - ets_template = self.template_env.get_template('CoolingIndirect.mot') + ets_template = self.template_env.get_template("CoolingIndirect.mot") - # TODO: Seems like the ets_data below should allow defaults from the system parameters JSON file, correct? + # TODO: Seems like the ets_data below should allow defaults from + # the system parameters JSON file, correct? # ets model parameters are from the schema.json file, default values only. ets_data = self.check_ets_system_parameters() # Here comes the Jina2 function: render() - file_data = ets_template.render( - ets_data=ets_data - ) + file_data = ets_template.render(ets_data=ets_data) # write templated ETS back to modelica file , to the tests folder for Dymola test - if os.path.exists(os.path.join(self.directory_ets_templated, 'ets_cooling_indirect_templated.mo')): - os.remove(os.path.join(self.directory_ets_templated, 'ets_cooling_indirect_templated.mo')) - with open(os.path.join(self.directory_ets_templated, 'ets_cooling_indirect_templated.mo'), 'w') as f: + if os.path.exists( + os.path.join( + self.directory_ets_templated, "ets_cooling_indirect_templated.mo" + ) + ): + os.remove( + os.path.join( + self.directory_ets_templated, "ets_cooling_indirect_templated.mo" + ) + ) + with open( + os.path.join( + self.directory_ets_templated, "ets_cooling_indirect_templated.mo" + ), + "w", + ) as f: f.write(file_data) # write templated ETS back to building-modelica folder for Dymola test - if os.path.exists(os.path.join(self.directory_modelica_building, 'ets_cooling_indirect_templated.mo')): - os.remove(os.path.join(self.directory_modelica_building, 'ets_cooling_indirect_templated.mo')) - with open(os.path.join(self.directory_modelica_building, 'ets_cooling_indirect_templated.mo'), 'w') as f: + if os.path.exists( + os.path.join( + self.directory_modelica_building, "ets_cooling_indirect_templated.mo" + ) + ): + os.remove( + os.path.join( + self.directory_modelica_building, + "ets_cooling_indirect_templated.mo", + ) + ) + with open( + os.path.join( + self.directory_modelica_building, "ets_cooling_indirect_templated.mo" + ), + "w", + ) as f: f.write(file_data) return file_data def templated_ets_openloops_dymola(self): """after we creating the templated ets, we need to test it in Dymola under open loops. - Here we refactor the example file: CoolingIndirectOpenLoops, to test our templated ets model. + Here we refactor the example file: CoolingIndirectOpenLoops, + to test our templated ets model. """ - file = open(self.directory_modelica_building + "/Examples/CoolingIndirectOpenLoops.mo", "r") + file = open( + self.directory_modelica_building + "/Examples/CoolingIndirectOpenLoops.mo", + "r", + ) cooling_indirect_filename = "/Examples/CoolingIndirectOpenLoops_Templated.mo" # if the modelica example file is existed, delete it first @@ -127,22 +183,30 @@ def templated_ets_openloops_dymola(self): os.remove(self.directory_modelica_building + cooling_indirect_filename) # create the modelica example file for Dymola test - # TODO: Replace this with the ModelicaFile Class -- extend ModelicaFile class if does not support. - # Theoretically it is doable using extend clause from Modelica. But we need to change the original ETS model first, in order to extend. + # TODO: Replace this with the ModelicaFile Class -- + # extend ModelicaFile class if does not support. + # Theoretically it is doable using extend clause from Modelica. + # But we need to change the original ETS model first, in order to extend. # This is Michael Wetter suggested approach. - # if so, we don't need to template modelica models, but we need to connect the modelica components + # if so, we don't need to template modelica models, + # but we need to connect the modelica components repl_dict = {} from_str = "model CoolingIndirectOpenLoops" to_str = "model CoolingIndirectOpenLoops_Templated\n" repl_dict[from_str] = to_str - from_str = "Buildings.Applications.DHC.EnergyTransferStations.CoolingIndirect coo(" - to_str = "Buildings.Applications.DHC.EnergyTransferStations.ets_cooling_indirect_templated coo(" + from_str = ( + "Buildings.Applications.DHC.EnergyTransferStations.CoolingIndirect coo(" + ) + to_str = "Buildings.Applications.DHC.EnergyTransferStations." \ + "ets_cooling_indirect_templated coo(" repl_dict[from_str] = to_str from_str = "end CoolingIndirectOpenLoops;" to_str = "end CoolingIndirectOpenLoops_Templated;" repl_dict[from_str] = to_str - with open(self.directory_modelica_building + cooling_indirect_filename, "w") as examplefile: + with open( + self.directory_modelica_building + cooling_indirect_filename, "w" + ) as examplefile: for f in file: fx = f for from_str, to_str in repl_dict.items(): @@ -157,19 +221,3 @@ def templated_ets_openloops_dymola(self): def connect(self): """connect ETS-modelica to building-modelica (specifically TEASER modelica). This function will be modified in future""" - pass - -######################################################################################################################## -################################# For Local Debugging Purpose Only ################################## -######################################################################################################################## - -thermal_junction_properties_geojson = "C:/Users/YLI3/Yanfei_Projects/UrbanOPT/geojson-modelica-translator/geojson_modelica_translator/geojson/data/schemas/thermal_junction_properties.json" -system_parameters_geojson = "C:/Users/YLI3/Yanfei_Projects/UrbanOPT/geojson-modelica-translator/geojson_modelica_translator/system_parameters/schema.json" -ets_from_building_modelica = "C:/Users/YLI3/Yanfei_Projects/UrbanOPT/geojson-modelica-translator/geojson_modelica_translator/modelica/buildingslibrary/Buildings/Applications/DHC/EnergyTransferStations/CoolingIndirect.mo" -#print ( "current folder: ", os.getcwd(), "\n") -ets = ETSTemplate(thermal_junction_properties_geojson, system_parameters_geojson, ets_from_building_modelica ) -ets.check_ets_thermal_junction() -ets.check_ets_system_parameters() -ets.check_ets_from_building_modelica() -ets.to_modelica() -ets.templated_ets_openloops_dymola() diff --git a/geojson_modelica_translator/model_connectors/spawn.py b/geojson_modelica_translator/model_connectors/spawn.py index d8d055074..fb7483e28 100644 --- a/geojson_modelica_translator/model_connectors/spawn.py +++ b/geojson_modelica_translator/model_connectors/spawn.py @@ -32,7 +32,9 @@ import shutil from jinja2 import FileSystemLoader, Environment -from geojson_modelica_translator.model_connectors.base import Base as model_connector_base +from geojson_modelica_translator.model_connectors.base import ( + Base as model_connector_base, +) from geojson_modelica_translator.modelica.input_parser import PackageParser from geojson_modelica_translator.utils import ModelicaPath @@ -42,7 +44,11 @@ def __init__(self, system_parameters): super().__init__(system_parameters) self.template_env = Environment( - loader=FileSystemLoader(searchpath=os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates')) + loader=FileSystemLoader( + searchpath=os.path.join( + os.path.dirname(os.path.abspath(__file__)), "templates" + ) + ) ) def add_building(self, urbanopt_building, mapper=None): @@ -55,16 +61,28 @@ def add_building(self, urbanopt_building, mapper=None): # TODO: Need to convert units, these should exist on the urbanopt_building object # TODO: Abstract out the GeoJSON functionality if mapper is None: - self.buildings.append({ - "area": urbanopt_building.feature.properties['floor_area'] * 0.092936, # ft2 -> m2 - "building_id": urbanopt_building.feature.properties['id'], - "building_type": urbanopt_building.feature.properties['building_type'], - "floor_height": urbanopt_building.feature.properties['height'] * 0.3048, # ft -> m - "num_stories": urbanopt_building.feature.properties['number_of_stories_above_ground'], - "num_stories_below_grade": urbanopt_building.feature.properties['number_of_stories'] - - urbanopt_building.feature.properties['number_of_stories_above_ground'], - "year_built": urbanopt_building.feature.properties['year_built'] - }) + self.buildings.append( + { + "area": urbanopt_building.feature.properties["floor_area"] + * 0.092936, # ft2 -> m2 + "building_id": urbanopt_building.feature.properties["id"], + "building_type": urbanopt_building.feature.properties[ + "building_type" + ], + "floor_height": urbanopt_building.feature.properties["height"] + * 0.3048, # ft -> m + "num_stories": urbanopt_building.feature.properties[ + "number_of_stories_above_ground" + ], + "num_stories_below_grade": urbanopt_building.feature.properties[ + "number_of_stories" + ] + - urbanopt_building.feature.properties[ + "number_of_stories_above_ground" + ], + "year_built": urbanopt_building.feature.properties["year_built"], + } + ) def lookup_building_type(self, building_type): """ @@ -73,11 +91,11 @@ def lookup_building_type(self, building_type): :param building_type: :return: """ - if 'office' in building_type.lower(): - return 'office' + if "office" in building_type.lower(): + return "office" else: # TODO: define these mappings 'office', 'institute', 'institute4', institute8' - return 'office' + return "office" def to_modelica(self, project_name, root_building_dir): """ @@ -87,9 +105,9 @@ def to_modelica(self, project_name, root_building_dir): :param root_building_dir: str, root directory where building model will be exported """ curdir = os.getcwd() - loads_root_path = os.path.join(root_building_dir, project_name, 'Loads') - spawn_fmu_template = self.template_env.get_template('spawn_fmu.mot') - spawn_mos_template = self.template_env.get_template('RunSpawnBuilding.mot') + loads_root_path = os.path.join(root_building_dir, project_name, "Loads") + spawn_fmu_template = self.template_env.get_template("spawn_fmu.mot") + spawn_mos_template = self.template_env.get_template("RunSpawnBuilding.mot") building_names = [] try: for building in self.buildings: @@ -98,21 +116,25 @@ def to_modelica(self, project_name, root_building_dir): # Path for building data building_names.append(f"B{building['building_id']}") - b_modelica_path = ModelicaPath(f"B{building['building_id']}", loads_root_path, True) + b_modelica_path = ModelicaPath( + f"B{building['building_id']}", loads_root_path, True + ) # grab the data from the system_parameter file for this building id # TODO: create method in system_parameter class to make this easier and respect the defaults idf_filename = self.system_parameters.get_param_by_building_id( - building['building_id'], 'load_model_parameters.spawn.idf_filename' + building["building_id"], "load_model_parameters.spawn.idf_filename" ) epw_filename = self.system_parameters.get_param_by_building_id( - building['building_id'], 'load_model_parameters.spawn.epw_filename' + building["building_id"], "load_model_parameters.spawn.epw_filename" ) mos_weather_filename = self.system_parameters.get_param_by_building_id( - building['building_id'], 'load_model_parameters.spawn.mos_weather_filename' + building["building_id"], + "load_model_parameters.spawn.mos_weather_filename", ) thermal_zones = self.system_parameters.get_param_by_building_id( - building['building_id'], 'load_model_parameters.spawn.thermal_zone_names' + building["building_id"], + "load_model_parameters.spawn.thermal_zone_names", ) # construct th dict to pass into the template @@ -121,60 +143,76 @@ def to_modelica(self, project_name, root_building_dir): "idf": { "idf_filename": idf_filename, "filename": os.path.basename(idf_filename), - "path": os.path.dirname(idf_filename) + "path": os.path.dirname(idf_filename), }, "epw": { "epw_filename": epw_filename, "filename": os.path.basename(epw_filename), - "path": os.path.dirname(epw_filename) + "path": os.path.dirname(epw_filename), }, "mos_weather": { "mos_weather_filename": mos_weather_filename, "filename": os.path.basename(mos_weather_filename), - "path": os.path.dirname(mos_weather_filename) + "path": os.path.dirname(mos_weather_filename), }, - "thermal_zones": [] + "thermal_zones": [], } for tz in thermal_zones: # TODO: method for creating nice zone names for modelica - template_data['thermal_zones'].append({ - "modelica_object_name": f"zn{tz}", - "spawn_object_name": tz - }) + template_data["thermal_zones"].append( + {"modelica_object_name": f"zn{tz}", "spawn_object_name": tz} + ) # copy over the resource files for this building # TODO: move some of this over to a validation step - if os.path.exists(template_data['idf']['idf_filename']): + if os.path.exists(template_data["idf"]["idf_filename"]): shutil.copy( - template_data['idf']['idf_filename'], - os.path.join(b_modelica_path.resources_dir, template_data['idf']['filename']) + template_data["idf"]["idf_filename"], + os.path.join( + b_modelica_path.resources_dir, + template_data["idf"]["filename"], + ), ) else: - raise Exception(f"Missing IDF file for Spawn: {template_data['idf']['idf_filename']}") + raise Exception( + f"Missing IDF file for Spawn: {template_data['idf']['idf_filename']}" + ) - if os.path.exists(template_data['epw']['epw_filename']): + if os.path.exists(template_data["epw"]["epw_filename"]): shutil.copy( - template_data['epw']['epw_filename'], - os.path.join(b_modelica_path.resources_dir, template_data['epw']['filename']) + template_data["epw"]["epw_filename"], + os.path.join( + b_modelica_path.resources_dir, + template_data["epw"]["filename"], + ), ) else: raise Exception(f"Missing EPW file for Spawn: {template_data['epw']['epw_filename']}") - if os.path.exists(template_data['mos_weather']['mos_weather_filename']): + if os.path.exists(template_data["mos_weather"]["mos_weather_filename"]): shutil.copy( - template_data['mos_weather']['mos_weather_filename'], - os.path.join(b_modelica_path.resources_dir, template_data['mos_weather']['filename']) + template_data["mos_weather"]["mos_weather_filename"], + os.path.join( + b_modelica_path.resources_dir, + template_data["mos_weather"]["filename"], + ), ) else: raise Exception( f"Missing MOS weather file for Spawn: {template_data['mos_weather']['mos_weather_filename']}") + file_data = spawn_fmu_template.render( project_name=project_name, model_name=f"B{building['building_id']}", data=template_data, ) - with open(os.path.join(os.path.join(b_modelica_path.files_dir, 'building.mo')), 'w') as f: + with open( + os.path.join( + os.path.join(b_modelica_path.files_dir, "building.mo") + ), + "w", + ) as f: f.write(file_data) file_data = spawn_mos_template.render( @@ -182,7 +220,14 @@ def to_modelica(self, project_name, root_building_dir): model_name=f"B{building['building_id']}", data=template_data, ) - with open(os.path.join(os.path.join(b_modelica_path.resources_dir, 'RunSpawnBuilding.mos')), 'w') as f: + with open( + os.path.join( + os.path.join( + b_modelica_path.resources_dir, "RunSpawnBuilding.mos" + ) + ), + "w", + ) as f: f.write(file_data) finally: os.chdir(curdir) @@ -203,22 +248,24 @@ def post_process(self, project_name, root_building_dir, building_names): :param building_names: list, names of the buildings that need to be cleaned up after export :return: None """ - loads_root_path = os.path.join(root_building_dir, project_name, 'Loads') + loads_root_path = os.path.join(root_building_dir, project_name, "Loads") for b in building_names: b_modelica_path = os.path.join(loads_root_path, b) new_package = PackageParser.new_from_template( - b_modelica_path, b, ['building'], within=f"{project_name}.Loads" + b_modelica_path, b, ["building"], within=f"{project_name}.Loads" ) new_package.save() # now create the Loads level package. This (for now) will create the package without considering any existing # files in the Loads directory. package = PackageParser.new_from_template( - loads_root_path, 'Loads', building_names, within=f'{project_name}' + loads_root_path, "Loads", building_names, within=f"{project_name}" ) package.save() # now create the Package level package. This really needs to happen at the GeoJSON to modelica stage, but # do it here for now for testing. - pp = PackageParser.new_from_template(os.path.join(root_building_dir, project_name), project_name, ['Loads']) + pp = PackageParser.new_from_template( + os.path.join(root_building_dir, project_name), project_name, ["Loads"] + ) pp.save() diff --git a/geojson_modelica_translator/model_connectors/teaser.py b/geojson_modelica_translator/model_connectors/teaser.py index 97d47316d..1e95c5e48 100644 --- a/geojson_modelica_translator/model_connectors/teaser.py +++ b/geojson_modelica_translator/model_connectors/teaser.py @@ -33,7 +33,9 @@ import shutil from teaser.project import Project -from geojson_modelica_translator.model_connectors.base import Base as model_connector_base +from geojson_modelica_translator.model_connectors.base import ( + Base as model_connector_base, +) from geojson_modelica_translator.modelica.input_parser import InputParser, PackageParser from geojson_modelica_translator.utils import ModelicaPath, copytree @@ -51,23 +53,35 @@ def add_building(self, urbanopt_building, mapper=None): # TODO: Need to convert units, these should exist on the urbanopt_building object # TODO: Abstract out the GeoJSON functionality if mapper is None: - self.buildings.append({ - "area": urbanopt_building.feature.properties['floor_area'] * 0.092936, # ft2 -> m2 - "building_id": urbanopt_building.feature.properties['id'], - "building_type": urbanopt_building.feature.properties['building_type'], - "floor_height": urbanopt_building.feature.properties['height'] * 0.3048, # ft -> m - "num_stories": urbanopt_building.feature.properties['number_of_stories_above_ground'], - "num_stories_below_grade": urbanopt_building.feature.properties['number_of_stories'] - - urbanopt_building.feature.properties['number_of_stories_above_ground'], - "year_built": urbanopt_building.feature.properties['year_built'] - }) + self.buildings.append( + { + "area": urbanopt_building.feature.properties["floor_area"] + * 0.092936, # ft2 -> m2 + "building_id": urbanopt_building.feature.properties["id"], + "building_type": urbanopt_building.feature.properties[ + "building_type" + ], + "floor_height": urbanopt_building.feature.properties["height"] + * 0.3048, # ft -> m + "num_stories": urbanopt_building.feature.properties[ + "number_of_stories_above_ground" + ], + "num_stories_below_grade": urbanopt_building.feature.properties[ + "number_of_stories" + ] + - urbanopt_building.feature.properties[ + "number_of_stories_above_ground" + ], + "year_built": urbanopt_building.feature.properties["year_built"], + } + ) def lookup_building_type(self, building_type): - if 'office' in building_type.lower(): - return 'office' + if "office" in building_type.lower(): + return "office" else: # TODO: define these mappings 'office', 'institute', 'institute4', institute8' - return 'office' + return "office" def to_modelica(self, project_name, root_building_dir, keep_original_models=False): """ @@ -85,39 +99,50 @@ def to_modelica(self, project_name, root_building_dir, keep_original_models=Fals try: prj = Project(load_data=True) for building in self.buildings: - building_name = building['building_id'] + building_name = building["building_id"] prj.add_non_residential( - method='bmvbs', - usage=self.lookup_building_type(building['building_type']), + method="bmvbs", + usage=self.lookup_building_type(building["building_type"]), name=building_name, - year_of_construction=building['year_built'], - number_of_floors=building['num_stories'], - height_of_floors=building['floor_height'], - net_leased_area=building['area'], + year_of_construction=building["year_built"], + number_of_floors=building["num_stories"], + height_of_floors=building["floor_height"], + net_leased_area=building["area"], office_layout=1, window_layout=1, with_ahu=False, - construction_type="heavy" + construction_type="heavy", ) building_names.append(building_name) - prj.used_library_calc = 'IBPSA' + prj.used_library_calc = "IBPSA" prj.number_of_elements_calc = self.system_parameters.get_param( - 'buildings.default.load_model_parameters.rc.order', default=2) + "buildings.default.load_model_parameters.rc.order", default=2 + ) prj.merge_windows_calc = False # calculate the properties of all the buildings and export to the Buildings library prj.calc_all_buildings() prj.export_ibpsa( - library="Buildings", - path=os.path.join(curdir, root_building_dir) + library="Buildings", path=os.path.join(curdir, root_building_dir) ) finally: os.chdir(curdir) - self.post_process(project_name, root_building_dir, building_names, keep_original_models=keep_original_models) + self.post_process( + project_name, + root_building_dir, + building_names, + keep_original_models=keep_original_models, + ) - def post_process(self, project_name, root_building_dir, building_names, keep_original_models=False): + def post_process( + self, + project_name, + root_building_dir, + building_names, + keep_original_models=False, + ): """ Cleanup the export of the TEASER files into a format suitable for the district-based analysis. This includes the following: @@ -139,64 +164,76 @@ def post_process(self, project_name, root_building_dir, building_names, keep_ori string_replace_list = [] # create a new modelica based path for the buildings # TODO: make this work at the toplevel, somehow. - b_modelica_path = ModelicaPath(f'B{b}', root_building_dir, True) + b_modelica_path = ModelicaPath(f"B{b}", root_building_dir, True) # copy over the entire model to the new location - copytree(os.path.join(root_building_dir, f'Project/B{b}/B{b}_Models'), b_modelica_path.files_dir) + copytree( + os.path.join(root_building_dir, f"Project/B{b}/B{b}_Models"), + b_modelica_path.files_dir, + ) # read in the package to apply the changes as they other files are processed # TODO: these should be linked, so a rename method should act across the model and the package.order - package = PackageParser(os.path.join(root_building_dir, f'B{b}')) + package = PackageParser(os.path.join(root_building_dir, f"B{b}")) # move the internal gains files to a new resources folder - mat_files = glob.glob(os.path.join(root_building_dir, f'B{b}/*.txt')) + mat_files = glob.glob(os.path.join(root_building_dir, f"B{b}/*.txt")) for f in mat_files: - new_file_name = os.path.basename(f).replace(f'B{b}', '') - os.rename(f, f'{b_modelica_path.resources_dir}/{new_file_name}') + new_file_name = os.path.basename(f).replace(f"B{b}", "") + os.rename(f, f"{b_modelica_path.resources_dir}/{new_file_name}") string_replace_list.append( ( - f'Project/B{b}/B{b}_Models/{os.path.basename(f)}', - f'Loads/{b_modelica_path.resources_relative_dir}/{new_file_name}' + f"Project/B{b}/B{b}_Models/{os.path.basename(f)}", + f"Loads/{b_modelica_path.resources_relative_dir}/{new_file_name}", ) ) # process each of the building models - mo_files = glob.glob(os.path.join(root_building_dir, f'B{b}/*.mo')) + mo_files = glob.glob(os.path.join(root_building_dir, f"B{b}/*.mo")) for f in mo_files: # ignore the package.mo file - if os.path.basename(f) == 'package.mo': + if os.path.basename(f) == "package.mo": continue mofile = InputParser(f) # previous paths and replace with the new one. # Make sure to update the names of any resources as well. - mofile.replace_within_string(f'{project_name}.Loads.B{b}') + mofile.replace_within_string(f"{project_name}.Loads.B{b}") # remove ReaderTMY3 - mofile.remove_object('ReaderTMY3') + mofile.remove_object("ReaderTMY3") # updating path to internal loads for s in string_replace_list: mofile.replace_model_string( - 'Modelica.Blocks.Sources.CombiTimeTable', 'internalGains', s[0], s[1] + "Modelica.Blocks.Sources.CombiTimeTable", + "internalGains", + s[0], + s[1], ) # add heat port data = [ - 'annotation (Placement(transformation(extent={{-10,90},{10,110}}), iconTransformation(extent={{-10,90},{10,110}})));' # noqa + "annotation (Placement(transformation(extent={{-10,90},{10,110}}), iconTransformation(extent={{-10,90},{10,110}})));" # noqa ] - mofile.add_model_object('Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a', 'port_a', data) + mofile.add_model_object( + "Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a", + "port_a", + data, + ) # add TAir output # TODO: read in the object by name -- parse the parenthetic content instance = 'TAir(\n quantity="ThermodynamicTemperature", unit="K", displayUnit="degC")' data = [ '"Room air temperature"', - 'annotation (Placement(transformation(extent={{100,-10},{120,10}})));' + "annotation (Placement(transformation(extent={{100,-10},{120,10}})));", ] - mofile.add_model_object('Buildings.Controls.OBC.CDL.Interfaces.RealOutput', instance, data) + mofile.add_model_object( + "Buildings.Controls.OBC.CDL.Interfaces.RealOutput", instance, data + ) # All existing weaDat.weaBus connections need to be updated to simply weaBus mofile.replace_connect_string('weaDat.weaBus', None, 'weaBus', None, True) @@ -204,49 +241,75 @@ def post_process(self, project_name, root_building_dir, building_names, keep_ori mofile.remove_connect_string('weaBus', 'weaBus') # add new port connections - if self.system_parameters.get_param('buildings.default.load_model_parameters.rc.order', default=2) == 1: # noqa - data = 'annotation (Line(points={{0,100},{96,100},{96,20},{92,20}}, color={191,0,0}))' - mofile.add_connect('port_a', 'thermalZoneOneElement.intGainsConv', data) + if ( + self.system_parameters.get_param( + "buildings.default.load_model_parameters.rc.order", default=2 + ) + == 1 + ): # noqa + data = "annotation (Line(points={{0,100},{96,100},{96,20},{92,20}}, color={191,0,0}))" + mofile.add_connect( + "port_a", "thermalZoneOneElement.intGainsConv", data + ) - data = 'annotation (Line(points={{93,32},{98,32},{98,0},{110,0}}, color={0,0,127}))' - mofile.add_connect('thermalZoneOneElement.TAir', 'TAir', data) - elif self.system_parameters.get_param('buildings.default.load_model_parameters.rc.order', default=2) == 2: # noqa - data = 'annotation (Line(points={{0,100},{96,100},{96,20},{92,20}}, color={191,0,0}))' - mofile.add_connect('port_a', 'thermalZoneTwoElements.intGainsConv', data) + data = "annotation (Line(points={{93,32},{98,32},{98,0},{110,0}}, color={0,0,127}))" + mofile.add_connect("thermalZoneOneElement.TAir", "TAir", data) + elif ( + self.system_parameters.get_param( + "buildings.default.load_model_parameters.rc.order", default=2 + ) + == 2 + ): # noqa + data = "annotation (Line(points={{0,100},{96,100},{96,20},{92,20}}, color={191,0,0}))" + mofile.add_connect( + "port_a", "thermalZoneTwoElements.intGainsConv", data + ) - data = 'annotation (Line(points={{93,32},{98,32},{98,0},{110,0}}, color={0,0,127}))' - mofile.add_connect('thermalZoneTwoElements.TAir', 'TAir', data) - elif self.system_parameters.get_param('buildings.default.load_model_parameters.rc.order', default=2) == 4: # noqa - data = 'annotation (Line(points={{0,100},{96,100},{96,20},{92,20}}, color={191,0,0}))' - mofile.add_connect('port_a', 'thermalZoneFourElements.intGainsConv', data) + data = "annotation (Line(points={{93,32},{98,32},{98,0},{110,0}}, color={0,0,127}))" + mofile.add_connect("thermalZoneTwoElements.TAir", "TAir", data) + elif ( + self.system_parameters.get_param( + "buildings.default.load_model_parameters.rc.order", default=2 + ) + == 4 + ): # noqa + data = "annotation (Line(points={{0,100},{96,100},{96,20},{92,20}}, color={191,0,0}))" + mofile.add_connect( + "port_a", "thermalZoneFourElements.intGainsConv", data + ) - data = 'annotation (Line(points={{93,32},{98,32},{98,0},{110,0}}, color={0,0,127}))' - mofile.add_connect('thermalZoneFourElements.TAir', 'TAir', data) + data = "annotation (Line(points={{93,32},{98,32},{98,0},{110,0}}, color={0,0,127}))" + mofile.add_connect("thermalZoneFourElements.TAir", "TAir", data) # change the name of the modelica model to remove the building id, update in package too! - new_model_name = mofile.model['name'].split("_")[1] - package.rename_model(mofile.model['name'], new_model_name) - mofile.model['name'] = new_model_name + new_model_name = mofile.model["name"].split("_")[1] + package.rename_model(mofile.model["name"], new_model_name) + mofile.model["name"] = new_model_name # Save as the new filename (without building ID) - new_filename = os.path.join(root_building_dir, f'B{b}/{os.path.basename(f).split("_")[1]}') + new_filename = os.path.join( + root_building_dir, f'B{b}/{os.path.basename(f).split("_")[1]}' + ) mofile.save_as(new_filename) os.remove(f) # save the updated package.mo and package.order. new_package = PackageParser.new_from_template( - package.path, f'B{b}', package.order, within=f'{project_name}.Loads' + package.path, f"B{b}", package.order, within=f"{project_name}.Loads" ) new_package.save() # remaining clean up tasks across the entire exported project if not keep_original_models: - shutil.rmtree(os.path.join(root_building_dir, 'Project')) + shutil.rmtree(os.path.join(root_building_dir, "Project")) # now create the Loads level package. This (for now) will create the package without considering any existing # files in the Loads directory. # add in the silly 'B' before the building names package = PackageParser.new_from_template( - root_building_dir, 'Loads', ['B' + b for b in building_names], within=f'{project_name}' + root_building_dir, + "Loads", + ["B" + b for b in building_names], + within=f"{project_name}", ) package.save() diff --git a/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot b/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot index 40a65d7ca..d6a8614de 100644 --- a/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot +++ b/geojson_modelica_translator/model_connectors/templates/CoolingIndirect.mot @@ -340,23 +340,23 @@ annotation (defaultComponentName="coo", coordinateSystem(preserveAspectRatio=false, extent={{-100,-100},{100,160}})), Documentation(info="

    -Indirect cooling energy transfer station (ETS) model that controls -the building chilled water supply temperature by modulating a -primary control valve on the district supply side. The design is -based on a typical district cooling ETS described in ASHRAE's +Indirect cooling energy transfer station (ETS) model that controls +the building chilled water supply temperature by modulating a +primary control valve on the district supply side. The design is +based on a typical district cooling ETS described in ASHRAE's -District Cooling Guide. -As shown in the figure below, the building pumping design (constant, -variable) is specified on the building side, not within the ETS. +District Cooling Guide. +As shown in the figure below, the building pumping design (constant, +variable) is specified on the building side, not within the ETS.

    \"DHC.ETS.CoolingIndirect\"/

    Reference

    -American Society of Heating, Refrigeration and Air-Conditioning -Engineers. (2013). Chapter 5: End User Interface. In -District Cooling Guide. 1st Edition. +American Society of Heating, Refrigeration and Air-Conditioning +Engineers. (2013). Chapter 5: End User Interface. In +District Cooling Guide. 1st Edition.

    ", revisions="
      @@ -366,4 +366,4 @@ First implementation.
    • 12/15/2020, Yanfei Li, Templating ETS model.
    "));{% endraw %} -end {{ets_data["ModelName"]}}; \ No newline at end of file +end {{ets_data["ModelName"]}}; diff --git a/geojson_modelica_translator/modelica/input_parser.py b/geojson_modelica_translator/modelica/input_parser.py index cae132bd8..ac7b8fbf4 100644 --- a/geojson_modelica_translator/modelica/input_parser.py +++ b/geojson_modelica_translator/modelica/input_parser.py @@ -52,7 +52,11 @@ class method. self.load() self.template_env = Environment( - loader=FileSystemLoader(searchpath=os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates')) + loader=FileSystemLoader( + searchpath=os.path.join( + os.path.dirname(os.path.abspath(__file__)), "templates" + ) + ) ) @classmethod @@ -68,37 +72,37 @@ def new_from_template(cls, path, name, order, within=None): """ klass = PackageParser(path) if within: - template = klass.template_env.get_template('package.mot') + template = klass.template_env.get_template("package.mot") else: - template = klass.template_env.get_template('package_base.mot') + template = klass.template_env.get_template("package_base.mot") klass.package_data = template.render(within=within, name=name, order=order) - klass.order_data = '\n'.join(order) - klass.order_data += '\n' # trailing line + klass.order_data = "\n".join(order) + klass.order_data += "\n" # trailing line return klass def load(self): """ Load the package.mo and package.mo data from the member variable path """ - filename = os.path.join(self.path, 'package.mo') + filename = os.path.join(self.path, "package.mo") if os.path.exists(filename): - with open(filename, 'r') as f: + with open(filename, "r") as f: self.package_data = f.read() - filename = os.path.join(self.path, 'package.order') + filename = os.path.join(self.path, "package.order") if os.path.exists(filename): - with open(filename, 'r') as f: + with open(filename, "r") as f: self.order_data = f.read() def save(self): """ Save the updated files to the same location """ - with open(os.path.join(os.path.join(self.path, 'package.mo')), 'w') as f: + with open(os.path.join(os.path.join(self.path, "package.mo")), "w") as f: f.write(self.package_data) - with open(os.path.join(os.path.join(self.path, 'package.order')), 'w') as f: + with open(os.path.join(os.path.join(self.path, "package.order")), "w") as f: f.write(self.order_data) @property @@ -108,7 +112,7 @@ def order(self): :return: list, list of the loaded models in the package.order file """ - return self.order_data.split('\n') + return self.order_data.split("\n") def rename_model(self, old_model, new_model): """ @@ -127,7 +131,7 @@ class InputParser(object): def __init__(self, modelica_filename): if not os.path.exists(modelica_filename): - raise Exception(f'Modelica file does not exist: {modelica_filename}') + raise Exception(f"Modelica file does not exist: {modelica_filename}") self.modelica_filename = modelica_filename self.init_vars() @@ -135,72 +139,88 @@ def __init__(self, modelica_filename): def init_vars(self): self.within = None - self.model = {'name': None, 'comment': None, 'objects': []} + self.model = {"name": None, "comment": None, "objects": []} self.connections = [] self.equations = [] def parse_mo(self): # eventually move this over to use token-based assessment of the files. Here is a list of some of the tokens # TODO: strip all spacing and reconstruct on export - tokens = ['within', 'block', 'algorithm', 'model', 'equation', 'protected', 'package', - 'extends', 'initial equation', 'end'] + tokens = [ + "within", + "block", + "algorithm", + "model", + "equation", + "protected", + "package", + "extends", + "initial equation", + "end", + ] current_block = None - obj_data = '' - connect_data = '' - with open(self.modelica_filename, 'r') as f: + obj_data = "" + connect_data = "" + with open(self.modelica_filename, "r") as f: for index, line in enumerate(f.readlines()): - if line == '\n': + if line == "\n": # Skip blank lines (for now? continue - elif line.startswith('within'): + elif line.startswith("within"): # these lines typically only have a single line, so just persist it if not self.within: # remove the line feed and the trailing semicolon - self.within = line.split(' ')[1].rstrip().replace(';', '') + self.within = line.split(" ")[1].rstrip().replace(";", "") else: raise Exception("More than one 'within' lines found") continue - elif line.startswith('model'): + elif line.startswith("model"): # get the model name and save - self.model['name'] = line.split(' ')[1].rstrip() - current_block = 'model' + self.model["name"] = line.split(" ")[1].rstrip() + current_block = "model" continue - elif line.startswith('equation'): - current_block = 'equation' + elif line.startswith("equation"): + current_block = "equation" continue - elif line.startswith('end'): - current_block = 'end' + elif line.startswith("end"): + current_block = "end" else: # check if any other tokens are triggered and throw a 'not-supported' message for t in tokens: if line.startswith(t): raise Exception( - f"Found other token '{t}' in '{self.modelica_filename}' that is not supported... cannot continue") # noqa + f"Found other token '{t}' in '{self.modelica_filename}' that is not supported... \ + cannot continue" + ) # noqa # now store data that is in between these other blocks - if current_block == 'model': + if current_block == "model": # grab the lines that are comments: - if not obj_data and line.strip().startswith('"') and line.strip().endswith('"'): - self.model['comment'] = line.rstrip() + if ( + not obj_data + and line.strip().startswith('"') + and line.strip().endswith('"') + ): + self.model["comment"] = line.rstrip() continue # determine if this is a new object or a new object (look for ';') obj_data += line - if line.endswith(';\n'): - self.model['objects'].append(obj_data) - obj_data = '' - elif current_block == 'equation': - if line.strip().startswith('connect'): + if line.endswith(";\n"): + self.model["objects"].append(obj_data) + obj_data = "" + elif current_block == "equation": + if line.strip().startswith("connect"): connect_data += line - elif connect_data and line.endswith(';\n'): + elif connect_data and line.endswith(";\n"): connect_data += line self.connections.append(connect_data) - connect_data = '' + connect_data = "" elif connect_data: connect_data += line else: self.equations.append(line) - elif current_block == 'end': + elif current_block == "end": pass else: # there is nothing to do here @@ -221,7 +241,7 @@ def save_as(self, new_filename): :param new_filename: :return: """ - with open(new_filename, 'w') as f: + with open(new_filename, "w") as f: f.write(self.serialize()) def remove_object(self, obj_name): @@ -233,7 +253,7 @@ def remove_object(self, obj_name): """ index, obj = self.find_model_object(obj_name) if index is not None: - del self.model['objects'][index] + del self.model["objects"][index] def replace_within_string(self, new_string): """ @@ -249,9 +269,9 @@ def find_model_object(self, obj_name): :param obj_name: string, name (including the instance) :return: list, index and string of object """ - for index, o in enumerate(self.model['objects']): + for index, o in enumerate(self.model["objects"]): if obj_name in o: - return index, self.model['objects'][index] + return index, self.model["objects"][index] return None, None @@ -274,9 +294,11 @@ def replace_model_string(self, model_name, model_instance, old_string, new_strin :param old_string: string, name of the old string to replace :param new_string: string, the new string """ - index, _model = self.find_model_object(f'{model_name} {model_instance}') + index, _model = self.find_model_object(f"{model_name} {model_instance}") if index is not None: - self.model['objects'][index] = self.model['objects'][index].replace(old_string, new_string) + self.model["objects"][index] = self.model["objects"][index].replace( + old_string, new_string + ) def add_model_object(self, model_name, model_instance, data): """ @@ -286,10 +308,10 @@ def add_model_object(self, model_name, model_instance, data): :param model_instance: string :param data: list of strings """ - str = f' {model_name} {model_instance}\n' + str = f" {model_name} {model_instance}\n" for d in data: - str += f' {d}\n' - self.model['objects'].append(str) + str += f" {d}\n" + self.model["objects"].append(str) def add_connect(self, a, b, annotation): """ @@ -299,7 +321,7 @@ def add_connect(self, a, b, annotation): :param b: string, port b :param annotation: string, description """ - self.connections.append(f' connect({a}, {b})\n {annotation};\n') + self.connections.append(f" connect({a}, {b})\n {annotation};\n") def find_connect(self, port_a, port_b): """ @@ -312,12 +334,12 @@ def find_connect(self, port_a, port_b): """ for index, c in enumerate(self.connections): if not port_a: - raise Exception('Unable to replace string in connect if unknown port A') + raise Exception("Unable to replace string in connect if unknown port A") if not port_b: - if f'({port_a}, ' in c: + if f"({port_a}, " in c: return index, c if port_a and port_b: - if f'({port_a}, {port_b})' in c: + if f"({port_a}, {port_b})" in c: return index, c return None, None @@ -364,13 +386,13 @@ def serialize(self): :return: string """ - str = f'within {self.within};\n' + str = f"within {self.within};\n" str += f"model {self.model['name']}\n" str += f"{self.model['comment']}\n\n" - for o in self.model['objects']: + for o in self.model["objects"]: for l in o: str += l - str += 'equation\n' + str += "equation\n" for c in self.connections: str += c for e in self.equations: diff --git a/geojson_modelica_translator/system_parameters/schema.json b/geojson_modelica_translator/system_parameters/schema.json index 4daf2c60a..8e9408ca8 100755 --- a/geojson_modelica_translator/system_parameters/schema.json +++ b/geojson_modelica_translator/system_parameters/schema.json @@ -301,4 +301,4 @@ } } } -} \ No newline at end of file +} diff --git a/geojson_modelica_translator/system_parameters/system_parameters.py b/geojson_modelica_translator/system_parameters/system_parameters.py index 882a03990..fde135a18 100644 --- a/geojson_modelica_translator/system_parameters/system_parameters.py +++ b/geojson_modelica_translator/system_parameters/system_parameters.py @@ -46,14 +46,18 @@ def __init__(self, filename=None): :param filename: string, (optional) path to file to load """ # load the schema for validation - self.schema = json.load(open(os.path.join(os.path.dirname(__file__), 'schema.json'), 'r')) + self.schema = json.load( + open(os.path.join(os.path.dirname(__file__), "schema.json"), "r") + ) self.data = {} if filename: if os.path.exists(filename): - self.data = json.load(open(filename, 'r')) + self.data = json.load(open(filename, "r")) else: - raise Exception(f"System design parameters file does not exist: {filename}") + raise Exception( + f"System design parameters file does not exist: {filename}" + ) errors = self.validate() if len(errors) != 0: @@ -90,9 +94,9 @@ def get_param(self, path, data=None, default=None): if data is None: data = self.data - paths = path.split('.') + paths = path.split(".") check_path = paths.pop(0) - if check_path == '': + if check_path == "": # no path passed return default else: @@ -103,7 +107,7 @@ def get_param(self, path, data=None, default=None): if len(paths) == 0: return value else: - return self.get_param('.'.join(paths), data=value, default=default) + return self.get_param(".".join(paths), data=value, default=default) def get_param_by_building_id(self, building_id, path, default=None): """ @@ -117,8 +121,8 @@ def get_param_by_building_id(self, building_id, path, default=None): """ # TODO: return the default value if the building ID is not defined - for b in self.data.get('buildings', {}).get('custom', {}): - if b.get('geojson_id', None) == building_id: + for b in self.data.get("buildings", {}).get("custom", {}): + if b.get("geojson_id", None) == building_id: # print(f"Building found for {building_id}") return self.get_param(path, b, default=default) diff --git a/geojson_modelica_translator/utils.py b/geojson_modelica_translator/utils.py index a6f9e3ea3..f256eba39 100644 --- a/geojson_modelica_translator/utils.py +++ b/geojson_modelica_translator/utils.py @@ -73,7 +73,9 @@ def __init__(self, name, root_dir, overwrite=False): def clear_path(self, path, overwrite=False): if os.path.exists(path): if overwrite: - raise Exception("Directory already exists and overwrite is false for %s" % path) + raise Exception( + "Directory already exists and overwrite is false for %s" % path + ) else: shutil.rmtree(path) os.makedirs(path, exist_ok=True) @@ -98,7 +100,7 @@ def resources_relative_dir(self): strings within modelica files which are relative to the package. :return: """ - return os.path.join('Resources', 'Data', self.name) + return os.path.join("Resources", "Data", self.name) @property def resources_dir(self): diff --git a/management/update_licenses.py b/management/update_licenses.py index 2eb4441a1..5be04e597 100644 --- a/management/update_licenses.py +++ b/management/update_licenses.py @@ -34,7 +34,9 @@ import os import re -PYTHON_REGEX = re.compile(r'^""".\*{100}.*:copyright.*\*{100}."""$', re.MULTILINE | re.DOTALL) +PYTHON_REGEX = re.compile( + r'^""".\*{100}.*:copyright.*\*{100}."""$', re.MULTILINE | re.DOTALL +) PYTHON_LICENSE = '''""" **************************************************************************************************** :copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. @@ -65,12 +67,15 @@ **************************************************************************************************** """''' -EXCLUDE_FILES = ["__init__.py", ] +EXCLUDE_FILES = ["__init__.py"] PATHS = [ - {"glob": 'geojson_modelica_translator/**/*.py', "license": PYTHON_LICENSE, "REGEX": PYTHON_REGEX}, - {"glob": 'management/**/*.py', "license": PYTHON_LICENSE, "REGEX": PYTHON_REGEX}, - {"glob": 'tests/**/*.py', "license": PYTHON_LICENSE, "REGEX": PYTHON_REGEX}, - + { + "glob": "geojson_modelica_translator/**/*.py", + "license": PYTHON_LICENSE, + "REGEX": PYTHON_REGEX, + }, + {"glob": "management/**/*.py", "license": PYTHON_LICENSE, "REGEX": PYTHON_REGEX}, + {"glob": "tests/**/*.py", "license": PYTHON_LICENSE, "REGEX": PYTHON_REGEX}, # single files # { "glob": 'bin/resources/**/file.py', "license": PYTHON_LICENSE, "REGEX": PYTHON_REGEX }, ] @@ -79,7 +84,7 @@ class UpdateLicenses(distutils.cmd.Command): """Custom comand for updating the GeoJSON schemas on which this project depends.""" - description = 'Update the license/copyright headers' + description = "Update the license/copyright headers" user_options = [] @@ -97,27 +102,27 @@ def check_and_update_license(self, filename): :param filename: str, path of the file to update :return: None """ - s = open(filename, 'r').read() + s = open(filename, "r").read() if PYTHON_REGEX.search(s): - print('License already exists, updating') + print("License already exists, updating") content = re.sub(PYTHON_REGEX, PYTHON_LICENSE, s) - with open(filename, 'w') as f: + with open(filename, "w") as f: f.write(content) f.close() else: - print('Adding license') - with open(filename, 'r+') as f: + print("Adding license") + with open(filename, "r+") as f: content = f.read() f.seek(0, 0) - f.write(PYTHON_LICENSE.rstrip('\r\n') + '\n\n\n' + content) + f.write(PYTHON_LICENSE.rstrip("\r\n") + "\n\n\n" + content) f.close() def run(self): for p in PATHS: - gl = glob.glob(p['glob'], recursive=True) + gl = glob.glob(p["glob"], recursive=True) for g in gl: if os.path.basename(g) in EXCLUDE_FILES: - print(f'Skipping file {g}') + print(f"Skipping file {g}") else: - print(f'Checking license in file {g}') + print(f"Checking license in file {g}") self.check_and_update_license(g) diff --git a/management/update_schemas.py b/management/update_schemas.py index 372f4c64a..4c1653801 100644 --- a/management/update_schemas.py +++ b/management/update_schemas.py @@ -38,26 +38,23 @@ class UpdateSchemas(distutils.cmd.Command): """Custom comand for updating the GeoJSON schemas on which this project depends.""" - description = 'download updated GeoJSON schemas' + description = "download updated GeoJSON schemas" - user_options = [ - ('baseurl=', 'u', 'base URL from which to download the schemas'), - ] + user_options = [("baseurl=", "u", "base URL from which to download the schemas")] def initialize_options(self): self.files_to_download = [ - 'building_properties.json', - 'district_system_properties.json', - 'electrical_connector_properties.json', - 'electrical_junction_properties.json', - 'region_properties.json', - 'site_properties.json', - 'thermal_connector_properties.json', - 'thermal_junction_properties.json', - + "building_properties.json", + "district_system_properties.json", + "electrical_connector_properties.json", + "electrical_junction_properties.json", + "region_properties.json", + "site_properties.json", + "thermal_connector_properties.json", + "thermal_junction_properties.json", ] # For now the branch is 'schema' but will need to be moved to develop after it is merged. - self.baseurl = 'https://raw.githubusercontent.com/urbanopt/urbanopt-geojson-gem/schema/lib/urbanopt/geojson/schema/' # noqa + self.baseurl = "https://raw.githubusercontent.com/urbanopt/urbanopt-geojson-gem/schema/lib/urbanopt/geojson/schema/" # noqa def finalize_options(self): if self.baseurl is None: @@ -65,10 +62,10 @@ def finalize_options(self): def run(self): for f in self.files_to_download: - self.announce('Downloading schema: %s' % str(f), level=distutils.log.INFO) - response = requests.get('%s/%s' % (self.baseurl, f)) - save_path = 'geojson_modelica_translator/geojson/data/schemas/%s' % f + self.announce("Downloading schema: %s" % str(f), level=distutils.log.INFO) + response = requests.get("%s/%s" % (self.baseurl, f)) + save_path = "geojson_modelica_translator/geojson/data/schemas/%s" % f # if os.path.exists(save_path): # os.remove(save_path) - with open(save_path, 'w') as outf: + with open(save_path, "w") as outf: json.dump(response.json(), outf, indent=2) diff --git a/setup.cfg b/setup.cfg index 45e8f5d9f..7d5927bb7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -33,4 +33,3 @@ detailed-errors=1 nologcapture=1 nocapture=1 logging-level=INFO - diff --git a/setup.py b/setup.py index b26c5631f..2121d15ad 100755 --- a/setup.py +++ b/setup.py @@ -40,64 +40,57 @@ from management.update_licenses import UpdateLicenses from management.update_schemas import UpdateSchemas -with open('README.rst') as f: +with open("README.rst") as f: readme = f.read() -with open('LICENSE') as f: +with open("LICENSE") as f: license = f.read() setup( - name='GeoJSON Modelica Translator', - version='0.1.0', - description='Package for converting GeoJSON to Modelica models for Urban Scale Analyses.', + name="GeoJSON Modelica Translator", + version="0.1.0", + description="Package for converting GeoJSON to Modelica models for Urban Scale Analyses.", long_description=readme, - author='Nicholas Long', - author_email='nicholas.long@nrel.gov', - url='https://github.com/urbanopt/geojson_modelica_translator', + author="Nicholas Long", + author_email="nicholas.long@nrel.gov", + url="https://github.com/urbanopt/geojson_modelica_translator", license=license, - packages=find_packages(exclude=('tests', 'docs')), - cmdclass={ - 'update_schemas': UpdateSchemas, - 'update_licenses': UpdateLicenses, - }, - install_requires=[ - 'geojson==2.4.1', - 'jsonschema==3.0.1', - 'requests==2.22.0', - ], + packages=find_packages(exclude=("tests", "docs")), + cmdclass={"update_schemas": UpdateSchemas, "update_licenses": UpdateLicenses}, + install_requires=["geojson==2.4.1", "jsonschema==3.0.1", "requests==2.22.0"], classifiers=[ - 'Development Status :: 4 - Beta', - 'Environment :: Console', - 'Intended Audience :: Developers', - 'Intended Audience :: Science/Research', - 'Topic :: Scientific/Engineering', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', + "Development Status :: 4 - Beta", + "Environment :: Console", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "Topic :: Scientific/Engineering", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", ], ) # install portions of the Modelica Buildings Library for grabbing files as needed (e.g. MOS files, examples, etc) -libs_to_extract = ['Buildings/Applications/DHC'] -save_path = 'geojson_modelica_translator/modelica/buildingslibrary' -tmp_save_path = 'geojson_modelica_translator/modelica/tmp_buildingslibrary' -repo_name = 'modelica-buildings' +libs_to_extract = ["Buildings/Applications/DHC"] +save_path = "geojson_modelica_translator/modelica/buildingslibrary" +tmp_save_path = "geojson_modelica_translator/modelica/tmp_buildingslibrary" +repo_name = "modelica-buildings" if os.path.exists(save_path): shutil.rmtree(save_path) if os.path.exists(tmp_save_path): shutil.rmtree(tmp_save_path) -mbl_archive_name = 'issue1442_loadCoupling' -r = get(f'https://github.com/lbl-srg/{repo_name}/archive/{mbl_archive_name}.zip') +mbl_archive_name = "issue1442_loadCoupling" +r = get(f"https://github.com/lbl-srg/{repo_name}/archive/{mbl_archive_name}.zip") with ZipFile(BytesIO(r.content)) as zip: files = zip.namelist() for file in files: # check if this needs to be extracted by looking into the libs_to_extract list for lib_to_extract in libs_to_extract: # make the path system independent when searching - if os.path.join(lib_to_extract.replace('/', os.path.sep)) in file: - print(f'extracting ... {file}') + if os.path.join(lib_to_extract.replace("/", os.path.sep)) in file: + print(f"extracting ... {file}") zip.extract(file, path=tmp_save_path) # Move the whole directory -shutil.move(os.path.join(tmp_save_path, f'{repo_name}-{mbl_archive_name}'), save_path) +shutil.move(os.path.join(tmp_save_path, f"{repo_name}-{mbl_archive_name}"), save_path) if os.path.exists(tmp_save_path): shutil.rmtree(tmp_save_path) diff --git a/tests/context.py b/tests/context.py index 0a3b3e298..d51c569bf 100644 --- a/tests/context.py +++ b/tests/context.py @@ -34,6 +34,6 @@ import os import sys -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) import geojson_modelica_translator # noqa - Do not remove this line. diff --git a/tests/geojson/test_geojson.py b/tests/geojson/test_geojson.py index 7e8b7e9f3..354d57f69 100644 --- a/tests/geojson/test_geojson.py +++ b/tests/geojson/test_geojson.py @@ -36,27 +36,28 @@ class GeoJSONTest(unittest.TestCase): - def test_load_geojson(self): - filename = os.path.abspath('tests/geojson/data/geojson_1.json') + filename = os.path.abspath("tests/geojson/data/geojson_1.json") json = UrbanOptGeoJson(filename) self.assertIsNotNone(json.data) self.assertEqual(len(json.data.features), 4) def test_missing_file(self): - fn = 'non-existent-path' + fn = "non-existent-path" with self.assertRaises(Exception) as exc: UrbanOptGeoJson(fn) - self.assertEqual(f'URBANopt GeoJSON file does not exist: {fn}', str(exc.exception)) + self.assertEqual( + f"URBANopt GeoJSON file does not exist: {fn}", str(exc.exception) + ) def test_validate(self): - filename = os.path.abspath('tests/geojson/data/geojson_1.json') + filename = os.path.abspath("tests/geojson/data/geojson_1.json") json = UrbanOptGeoJson(filename) valid, results = json.validate() self.assertFalse(valid) - self.assertEqual(len(results['building']), 3) - self.assertEqual(results['building'][0]['id'], '5a6b99ec37f4de7f94020090') + self.assertEqual(len(results["building"]), 3) + self.assertEqual(results["building"][0]["id"], "5a6b99ec37f4de7f94020090") -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/geojson/test_schemas.py b/tests/geojson/test_schemas.py index 1d077a7e4..5564c22da 100644 --- a/tests/geojson/test_schemas.py +++ b/tests/geojson/test_schemas.py @@ -37,18 +37,18 @@ class SchemasTest(unittest.TestCase): def test_load_schemas(self): s = Schemas() - data = s.retrieve('building') - self.assertEqual(data['title'], 'Building object') + data = s.retrieve("building") + self.assertEqual(data["title"], "Building object") def test_invalid_retrieve(self): s = Schemas() with self.assertRaises(Exception) as context: - s.retrieve('judicate') - self.assertEqual('Schema for judicate does not exist', str(context.exception)) + s.retrieve("judicate") + self.assertEqual("Schema for judicate does not exist", str(context.exception)) def test_validate_schema(self): s = Schemas() - s.retrieve('building') + s.retrieve("building") # verify that the schema can validate an instance with simple parameters instance = { @@ -63,17 +63,17 @@ def test_validate_schema(self): "number_of_stories_above_ground": 3, "building_status": "Proposed", "floor_area": 51177, - "year_built": 2010 + "year_built": 2010, } - res = s.validate('building', instance) + res = s.validate("building", instance) self.assertEqual(len(res), 0) # bad system_type - instance['type'] = 'MagicBuilding' - res = s.validate('building', instance) + instance["type"] = "MagicBuilding" + res = s.validate("building", instance) self.assertIn("'MagicBuilding' is not one of ['Building']", res[0]) self.assertEqual(len(res), 1) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/model_connectors/data/RefBldgSmallOfficeNew2004_Chicago.idf b/tests/model_connectors/data/RefBldgSmallOfficeNew2004_Chicago.idf index a9e959582..84b1effcf 100644 --- a/tests/model_connectors/data/RefBldgSmallOfficeNew2004_Chicago.idf +++ b/tests/model_connectors/data/RefBldgSmallOfficeNew2004_Chicago.idf @@ -4834,4 +4834,3 @@ 0, !- Nuclear High Level Emission Factor {g/MJ} , !- Nuclear High Level Emission Factor Schedule Name 0; !- Nuclear Low Level Emission Factor {m3/MJ} - diff --git a/tests/model_connectors/data/jmodelica.py b/tests/model_connectors/data/jmodelica.py index 87d468a1e..e92f0e255 100644 --- a/tests/model_connectors/data/jmodelica.py +++ b/tests/model_connectors/data/jmodelica.py @@ -19,25 +19,29 @@ if len(sys.argv) > 1: # If the argument is a file, then parse it to a model name if os.path.isfile(sys.argv[1]): - model = sys.argv[1].replace(os.path.sep, '.')[:-3] + model = sys.argv[1].replace(os.path.sep, ".")[:-3] else: model = sys.argv[1] print("*** Compiling {}".format(model)) # Increase memory -pymodelica.environ['JVM_ARGS'] = '-Xmx4096m' +pymodelica.environ["JVM_ARGS"] = "-Xmx4096m" sys.stdout.flush() ###################################################################### # Compile fmu -fmu_name = compile_fmu(model, - version="2.0", - compiler_log_level='warning', - compiler_options={"generate_html_diagnostics": False, - "nle_solver_tol_factor": 1e-2}) +fmu_name = compile_fmu( + model, + version="2.0", + compiler_log_level="warning", + compiler_options={ + "generate_html_diagnostics": False, + "nle_solver_tol_factor": 1e-2, + }, +) ###################################################################### # Load model @@ -48,10 +52,10 @@ x_nominal = mod.nominal_continuous_states opts = mod.simulate_options() # Retrieve the default options -opts['solver'] = 'CVode' -opts['ncp'] = 5000 +opts["solver"] = "CVode" +opts["ncp"] = 5000 -if opts['solver'].lower() == 'cvode': +if opts["solver"].lower() == "cvode": # Set user-specified tolerance if it is smaller than the tolerance in the .mo file rtol = 1.0e-6 x_nominal = mod.nominal_continuous_states @@ -61,14 +65,18 @@ else: atol = rtol - opts['CVode_options'] = { - 'external_event_detection': False, - 'maxh': (mod.get_default_experiment_stop_time() - mod.get_default_experiment_stop_time()) / float(opts['ncp']), - 'iter': 'Newton', - 'discr': 'BDF', - 'rtol': rtol, - 'atol': atol, - 'store_event_points': True + opts["CVode_options"] = { + "external_event_detection": False, + "maxh": ( + mod.get_default_experiment_stop_time() + - mod.get_default_experiment_stop_time() + ) + / float(opts["ncp"]), + "iter": "Newton", + "discr": "BDF", + "rtol": rtol, + "atol": atol, + "store_event_points": True, } if debug_solver: @@ -104,6 +112,7 @@ if debug_solver: # Load the debug information from pyfmi.debug import CVodeDebugInformation + debug = CVodeDebugInformation(model.replace(".", "_") + "_debug.txt") # Below are options to plot the order, error and step-size evolution. diff --git a/tests/model_connectors/data/spawn_system_params_ex2.json b/tests/model_connectors/data/spawn_system_params_ex2.json index d85811297..b2abf057e 100644 --- a/tests/model_connectors/data/spawn_system_params_ex2.json +++ b/tests/model_connectors/data/spawn_system_params_ex2.json @@ -45,5 +45,3 @@ ] } } - - diff --git a/tests/model_connectors/test_ets.py b/tests/model_connectors/test_ets.py index eaf2c7427..6c1275192 100644 --- a/tests/model_connectors/test_ets.py +++ b/tests/model_connectors/test_ets.py @@ -8,7 +8,7 @@ # TODO: do not mix upper camel case and snake case. Spell out ETS or use ETSModelConnector... class ETSModelConnectorSingleBuildingTest(unittest.TestCase): def setUp(self): # the first method/member must be setUp - base_folder = os.path.join(os.getcwd(), 'geojson_modelica_translator') + base_folder = os.path.join(os.getcwd(), "geojson_modelica_translator") dest_path = "/geojson/data/schemas/thermal_junction_properties.json" self.thermal_junction_properties_geojson = base_folder + dest_path dest_path = "/system_parameters/schema.json" @@ -19,7 +19,7 @@ def setUp(self): # the first method/member must be setUp self.ets = ETSTemplate( self.thermal_junction_properties_geojson, self.system_parameters_geojson, - self.ets_from_building_modelica + self.ets_from_building_modelica, ) self.assertIsNotNone(self.ets) @@ -42,5 +42,5 @@ def test_ets_in_dymola(self): self.assertIsNotNone(self.ets.templated_ets_openloops_dymola()) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/model_connectors/test_spawn.py b/tests/model_connectors/test_spawn.py index bcdf99e70..b7e725a8a 100644 --- a/tests/model_connectors/test_spawn.py +++ b/tests/model_connectors/test_spawn.py @@ -31,23 +31,31 @@ import os import unittest -from geojson_modelica_translator.geojson_modelica_translator import GeoJsonModelicaTranslator +from geojson_modelica_translator.geojson_modelica_translator import ( + GeoJsonModelicaTranslator, +) from geojson_modelica_translator.model_connectors.spawn import SpawnConnector -from geojson_modelica_translator.system_parameters.system_parameters import SystemParameters +from geojson_modelica_translator.system_parameters.system_parameters import ( + SystemParameters, +) from ..context import geojson_modelica_translator # noqa - Do not remove this line class SpawnModelConnectorSingleBuildingTest(unittest.TestCase): def setUp(self): - prj_dir = 'tests/model_connectors/output/spawn_single' + prj_dir = "tests/model_connectors/output/spawn_single" # load in the example geojson with a single offie building - filename = os.path.abspath('tests/model_connectors/data/spawn_geojson_ex1.json') + filename = os.path.abspath("tests/model_connectors/data/spawn_geojson_ex1.json") gj = GeoJsonModelicaTranslator.from_geojson(filename) - gj.scaffold_directory(prj_dir) # use the GeoJson translator to scaffold out the directory + gj.scaffold_directory( + prj_dir + ) # use the GeoJson translator to scaffold out the directory # load system parameter data - filename = os.path.abspath('tests/model_connectors/data/spawn_system_params_ex1.json') + filename = os.path.abspath( + "tests/model_connectors/data/spawn_system_params_ex1.json" + ) sys_params = SystemParameters(filename) # now test the spawn connector (independent of the larger geojson translator @@ -58,23 +66,30 @@ def setUp(self): def test_spawn_init(self): self.assertIsNotNone(self.spawn) - self.assertEqual(self.spawn.system_parameters.get_param('buildings.custom')[0]['load_model'], 'Spawn') + self.assertEqual( + self.spawn.system_parameters.get_param("buildings.custom")[0]["load_model"], + "Spawn", + ) def test_spawn_to_modelica(self): - self.spawn.to_modelica('spawn_single', 'tests/model_connectors/output') + self.spawn.to_modelica("spawn_single", "tests/model_connectors/output") class SpawnModelConnectorTwoBuildingTest(unittest.TestCase): def setUp(self): - prj_dir = 'tests/model_connectors/output/spawn_two_building' + prj_dir = "tests/model_connectors/output/spawn_two_building" # load in the example geojson with a single offie building - filename = os.path.abspath('tests/model_connectors/data/spawn_geojson_ex2.json') + filename = os.path.abspath("tests/model_connectors/data/spawn_geojson_ex2.json") gj = GeoJsonModelicaTranslator.from_geojson(filename) - gj.scaffold_directory(prj_dir) # use the GeoJson translator to scaffold out the directory + gj.scaffold_directory( + prj_dir + ) # use the GeoJson translator to scaffold out the directory # load system parameter data - filename = os.path.abspath('tests/model_connectors/data/spawn_system_params_ex2.json') + filename = os.path.abspath( + "tests/model_connectors/data/spawn_system_params_ex2.json" + ) sys_params = SystemParameters(filename) # now test the spawn connector (independent of the larger geojson translator @@ -84,4 +99,4 @@ def setUp(self): self.spawn.add_building(b) def test_spawn_to_modelica(self): - self.spawn.to_modelica('spawn_two_building', 'tests/model_connectors/output') + self.spawn.to_modelica("spawn_two_building", "tests/model_connectors/output") diff --git a/tests/modelica/data/package.mo b/tests/modelica/data/package.mo index 510b48b53..03228d116 100644 --- a/tests/modelica/data/package.mo +++ b/tests/modelica/data/package.mo @@ -1,5 +1,5 @@ within Project.B5a6b99ec37f4de7f94020090; package B5a6b99ec37f4de7f94020090_Models extends Modelica.Icons.Package; - + end B5a6b99ec37f4de7f94020090_Models; diff --git a/tests/modelica/test_input_parser.py b/tests/modelica/test_input_parser.py index 35a66c868..d7f3aa251 100644 --- a/tests/modelica/test_input_parser.py +++ b/tests/modelica/test_input_parser.py @@ -38,55 +38,62 @@ class InputParserTest(unittest.TestCase): def setUp(self): - self.results_path = os.path.abspath('tests/modelica/output') + self.results_path = os.path.abspath("tests/modelica/output") if not os.path.exists(self.results_path): os.mkdir(self.results_path) def test_missing_file(self): - fn = 'non-existent-path' + fn = "non-existent-path" with self.assertRaises(Exception) as exc: InputParser(fn) - self.assertEqual(f'Modelica file does not exist: {fn}', str(exc.exception)) + self.assertEqual(f"Modelica file does not exist: {fn}", str(exc.exception)) def test_roundtrip(self): - filename = os.path.abspath('tests/modelica/data/test_1.mo') - new_filename = os.path.abspath('tests/modelica/output/test_1_output_1.mo') + filename = os.path.abspath("tests/modelica/data/test_1.mo") + new_filename = os.path.abspath("tests/modelica/output/test_1_output_1.mo") f = InputParser(filename) f.save_as(new_filename) self.assertTrue(filecmp.cmp(filename, new_filename)) def test_remove_object(self): - filename = os.path.abspath('tests/modelica/data/test_1.mo') - new_filename = os.path.abspath('tests/modelica/output/test_1_output_2.mo') + filename = os.path.abspath("tests/modelica/data/test_1.mo") + new_filename = os.path.abspath("tests/modelica/output/test_1_output_2.mo") f1 = InputParser(filename) - f1.remove_object('ReaderTMY3') + f1.remove_object("ReaderTMY3") f1.save_as(new_filename) self.assertFalse(filecmp.cmp(filename, new_filename)) f1.reload() f2 = InputParser(new_filename) - self.assertGreater(len(f1.model['objects']), len(f2.model['objects'])) + self.assertGreater(len(f1.model["objects"]), len(f2.model["objects"])) # verify that it exists in f1 but not in f2 - self.assertGreaterEqual(f1.find_model_object('ReaderTMY3')[0], 0) - self.assertIsNone(f2.find_model_object('ReaderTMY3')[0]) + self.assertGreaterEqual(f1.find_model_object("ReaderTMY3")[0], 0) + self.assertIsNone(f2.find_model_object("ReaderTMY3")[0]) def test_gsub_field(self): - filename = os.path.abspath('tests/modelica/data/test_1.mo') - new_filename = os.path.abspath('tests/modelica/output/test_1_output_3.mo') + filename = os.path.abspath("tests/modelica/data/test_1.mo") + new_filename = os.path.abspath("tests/modelica/output/test_1_output_3.mo") f1 = InputParser(filename) # This example is actually updating an annotation object, not a model, but leave it here for now. - f1.replace_model_string('Modelica.Blocks.Sources.CombiTimeTable', 'internalGains', 'Internals', 'NotInternals') + f1.replace_model_string( + "Modelica.Blocks.Sources.CombiTimeTable", + "internalGains", + "Internals", + "NotInternals", + ) f1.save_as(new_filename) f2 = InputParser(new_filename) - index, model = f2.find_model_object('Modelica.Blocks.Sources.CombiTimeTable internalGains') + index, model = f2.find_model_object( + "Modelica.Blocks.Sources.CombiTimeTable internalGains" + ) self.assertFalse(filecmp.cmp(filename, new_filename)) # the 5th index is the rotation... non-ideal look up - self.assertTrue('NotInternals' in model) + self.assertTrue("NotInternals" in model) def test_rename_filename(self): - filename = os.path.abspath('tests/modelica/data/test_1.mo') - new_filename = os.path.abspath('tests/modelica/output/test_1_output_4.mo') + filename = os.path.abspath("tests/modelica/data/test_1.mo") + new_filename = os.path.abspath("tests/modelica/output/test_1_output_4.mo") f1 = InputParser(filename) # This example is actually updating an annotation object, not a model, but leave it here for now. f1.replace_model_string( @@ -99,49 +106,60 @@ def test_rename_filename(self): f1.save_as(new_filename) f2 = InputParser(new_filename) - index, model = f2.find_model_object('Modelica.Blocks.Sources.CombiTimeTable internalGains') + index, model = f2.find_model_object( + "Modelica.Blocks.Sources.CombiTimeTable internalGains" + ) self.assertFalse(filecmp.cmp(filename, new_filename)) # the 5th index is the rotation... non-ideal look up - self.assertTrue('a/new/path.mat' in model) + self.assertTrue("a/new/path.mat" in model) def test_add_model_obj(self): - filename = os.path.abspath('tests/modelica/data/test_1.mo') - new_filename = os.path.abspath('tests/modelica/output/test_1_output_5.mo') + filename = os.path.abspath("tests/modelica/data/test_1.mo") + new_filename = os.path.abspath("tests/modelica/output/test_1_output_5.mo") f1 = InputParser(filename) data = [ 'annotation (Placement(transformation(extent={{-10,90},{10,110}}), iconTransformation(extent={{-10,90},{10,110}})));' # noqa # noqa ] - f1.add_model_object('Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a', 'port_a', data) + f1.add_model_object( + "Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a", "port_a", data + ) f1.save_as(new_filename) # verify in the new file that the new model object exists f2 = InputParser(new_filename) - index, model = f2.find_model_object('Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a port_a') + index, model = f2.find_model_object( + "Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a port_a" + ) self.assertFalse(filecmp.cmp(filename, new_filename)) self.assertGreaterEqual(index, 0) def test_gsub_connect(self): - filename = os.path.abspath('tests/modelica/data/test_1.mo') - new_filename = os.path.abspath('tests/modelica/output/test_1_output_6.mo') + filename = os.path.abspath("tests/modelica/data/test_1.mo") + new_filename = os.path.abspath("tests/modelica/output/test_1_output_6.mo") f1 = InputParser(filename) - f1.add_connect('port_a', 'thermalZoneTwoElements.intGainsConv', - 'annotation (Line(points={{0,100},{96,100},{96,20},{92,20}}, color={191,0,0}))') + f1.add_connect( + "port_a", + "thermalZoneTwoElements.intGainsConv", + "annotation (Line(points={{0,100},{96,100},{96,20},{92,20}}, color={191,0,0}))", + ) f1.save_as(new_filename) # verify in the new file that the new model object exists f2 = InputParser(new_filename) self.assertFalse(filecmp.cmp(filename, new_filename)) - index, c = f2.find_connect('port_a', 'thermalZoneTwoElements.intGainsConv') + index, c = f2.find_connect("port_a", "thermalZoneTwoElements.intGainsConv") self.assertGreaterEqual(index, 0) def test_rename_connection(self): - filename = os.path.abspath('tests/modelica/data/test_1.mo') - new_filename = os.path.abspath('tests/modelica/output/test_1_output_7.mo') + filename = os.path.abspath("tests/modelica/data/test_1.mo") + new_filename = os.path.abspath("tests/modelica/output/test_1_output_7.mo") f1 = InputParser(filename) # connect(weaDat.weaBus, HDifTil[3].weaBus) - f1.replace_connect_string('eqAirTemp.TEqAir', 'prescribedTemperature.T', 'NothingOfImportance', None) - f1.replace_connect_string('weaDat.weaBus', None, 'weaBus', None, True) + f1.replace_connect_string( + "eqAirTemp.TEqAir", "prescribedTemperature.T", "NothingOfImportance", None + ) + f1.replace_connect_string("weaDat.weaBus", None, "weaBus", None, True) f1.save_as(new_filename) f2 = InputParser(new_filename) @@ -172,5 +190,5 @@ def update_connection(self): pass -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/output_ets/ets_cooling_indirect_templated.mo b/tests/output_ets/ets_cooling_indirect_templated.mo index 13c8f8a1f..e221aa185 100644 --- a/tests/output_ets/ets_cooling_indirect_templated.mo +++ b/tests/output_ets/ets_cooling_indirect_templated.mo @@ -340,23 +340,23 @@ annotation (defaultComponentName="coo", coordinateSystem(preserveAspectRatio=false, extent={{-100,-100},{100,160}})), Documentation(info="

    -Indirect cooling energy transfer station (ETS) model that controls -the building chilled water supply temperature by modulating a -primary control valve on the district supply side. The design is -based on a typical district cooling ETS described in ASHRAE's +Indirect cooling energy transfer station (ETS) model that controls +the building chilled water supply temperature by modulating a +primary control valve on the district supply side. The design is +based on a typical district cooling ETS described in ASHRAE's -District Cooling Guide. -As shown in the figure below, the building pumping design (constant, -variable) is specified on the building side, not within the ETS. +District Cooling Guide. +As shown in the figure below, the building pumping design (constant, +variable) is specified on the building side, not within the ETS.

    \"DHC.ETS.CoolingIndirect\"/

    Reference

    -American Society of Heating, Refrigeration and Air-Conditioning -Engineers. (2013). Chapter 5: End User Interface. In -District Cooling Guide. 1st Edition. +American Society of Heating, Refrigeration and Air-Conditioning +Engineers. (2013). Chapter 5: End User Interface. In +District Cooling Guide. 1st Edition.

    ", revisions="
      diff --git a/tests/system_parameters/test_system_parameters.py b/tests/system_parameters/test_system_parameters.py index 4f8180b96..f2ce58af0 100644 --- a/tests/system_parameters/test_system_parameters.py +++ b/tests/system_parameters/test_system_parameters.py @@ -31,99 +31,95 @@ import os import unittest -from geojson_modelica_translator.system_parameters.system_parameters import SystemParameters +from geojson_modelica_translator.system_parameters.system_parameters import ( + SystemParameters, +) class GeoJSONTest(unittest.TestCase): - def test_load_system_parameters_1(self): - filename = os.path.abspath('tests/system_parameters/data/system_params_1.json') + filename = os.path.abspath("tests/system_parameters/data/system_params_1.json") sdp = SystemParameters(filename) - self.assertEqual(sdp.data['buildings']['default']['load_model_parameters']['rc']['order'], 2) + self.assertEqual( + sdp.data["buildings"]["default"]["load_model_parameters"]["rc"]["order"], 2 + ) def test_load_system_parameters_2(self): - filename = os.path.abspath('tests/system_parameters/data/system_params_2.json') + filename = os.path.abspath("tests/system_parameters/data/system_params_2.json") sdp = SystemParameters(filename) self.assertIsNotNone(sdp) def test_missing_file(self): - fn = 'non-existent-path' + fn = "non-existent-path" with self.assertRaises(Exception) as exc: SystemParameters(fn) - self.assertEqual(f'System design parameters file does not exist: {fn}', str(exc.exception)) + self.assertEqual( + f"System design parameters file does not exist: {fn}", str(exc.exception) + ) def test_errors(self): data = { "buildings": { "default": { "load_model": "ROM/RC", - "load_model_parameters": { - "rc": { - "order": 6 - } - } + "load_model_parameters": {"rc": {"order": 6}}, } } } with self.assertRaises(Exception) as exc: SystemParameters.loadd(data) - self.assertRegex(str(exc.exception), 'Invalid system parameter file.*') + self.assertRegex(str(exc.exception), "Invalid system parameter file.*") sp = SystemParameters.loadd(data, validate_on_load=False) - self.assertEqual(sp.validate()[0], '6 is not one of [1, 2, 3, 4]') + self.assertEqual(sp.validate()[0], "6 is not one of [1, 2, 3, 4]") def test_get_param(self): data = { "buildings": { "default": { "load_model": "ROM/RC", - "load_model_parameters": { - "rc": { - "order": 4 - } - } + "load_model_parameters": {"rc": {"order": 4}}, } } } sp = SystemParameters.loadd(data) - value = sp.get_param('buildings.default.load_model_parameters.rc.order') + value = sp.get_param("buildings.default.load_model_parameters.rc.order") self.assertEqual(value, 4) - value = sp.get_param('buildings.default.load_model') - self.assertEqual(value, 'ROM/RC') + value = sp.get_param("buildings.default.load_model") + self.assertEqual(value, "ROM/RC") - value = sp.get_param('buildings.default') - self.assertDictEqual(value, {'load_model': 'ROM/RC', 'load_model_parameters': {"rc": {"order": 4}}}) + value = sp.get_param("buildings.default") + self.assertDictEqual( + value, + {"load_model": "ROM/RC", "load_model_parameters": {"rc": {"order": 4}}}, + ) - value = sp.get_param('') + value = sp.get_param("") self.assertIsNone(value) - value = sp.get_param('not.a.real.path') + value = sp.get_param("not.a.real.path") self.assertIsNone(value) def test_get_param_with_default(self): - data = { - "buildings": { - "default": { - "load_model": "Spawn" - } - } - } + data = {"buildings": {"default": {"load_model": "Spawn"}}} sp = SystemParameters.loadd(data) - value = sp.get_param('buildings.default.load_model_parameters.rc.order', default=2) + value = sp.get_param( + "buildings.default.load_model_parameters.rc.order", default=2 + ) self.assertEqual(value, 2) - value = sp.get_param('not.a.real.path', default=2) + value = sp.get_param("not.a.real.path", default=2) self.assertEqual(value, 2) def test_get_param_with_building_id(self): - filename = os.path.abspath('tests/system_parameters/data/system_params_1.json') + filename = os.path.abspath("tests/system_parameters/data/system_params_1.json") sdp = SystemParameters(filename) - value = sdp.get_param_by_building_id('abcd1234', 'ets.system') - self.assertEqual(value, 'Booster Heater') + value = sdp.get_param_by_building_id("abcd1234", "ets.system") + self.assertEqual(value, "Booster Heater") -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/test_translator.py b/tests/test_translator.py index 770ed093f..cd60edc6f 100644 --- a/tests/test_translator.py +++ b/tests/test_translator.py @@ -33,8 +33,12 @@ import shutil import unittest -from geojson_modelica_translator.geojson_modelica_translator import GeoJsonModelicaTranslator -from geojson_modelica_translator.system_parameters.system_parameters import SystemParameters +from geojson_modelica_translator.geojson_modelica_translator import ( + GeoJsonModelicaTranslator, +) +from geojson_modelica_translator.system_parameters.system_parameters import ( + SystemParameters, +) from .context import geojson_modelica_translator # noqa - Do not remove this line @@ -44,86 +48,126 @@ def test_init(self): self.assertIsNotNone(gj) def test_from_geojson(self): - filename = os.path.abspath('tests/geojson/data/geojson_1.json') + filename = os.path.abspath("tests/geojson/data/geojson_1.json") gj = GeoJsonModelicaTranslator.from_geojson(filename) self.assertEqual(len(gj.buildings), 3) def test_missing_geojson(self): - fn = 'non-existent-path' + fn = "non-existent-path" with self.assertRaises(Exception) as exc: GeoJsonModelicaTranslator.from_geojson(fn) - self.assertEqual(f'GeoJSON file does not exist: {fn}', str(exc.exception)) + self.assertEqual(f"GeoJSON file does not exist: {fn}", str(exc.exception)) def test_to_modelica_defaults(self): - self.results_path = os.path.abspath('tests/output/geojson_1') + self.results_path = os.path.abspath("tests/output/geojson_1") if os.path.exists(self.results_path): shutil.rmtree(self.results_path) - filename = os.path.abspath('tests/geojson/data/geojson_1.json') + filename = os.path.abspath("tests/geojson/data/geojson_1.json") gj = GeoJsonModelicaTranslator.from_geojson(filename) sys_params = SystemParameters() gj.set_system_parameters(sys_params) - gj.to_modelica('geojson_1', 'tests/output') + gj.to_modelica("geojson_1", "tests/output") # setup what we are going to check - model_names = ['Floor', 'ICT', 'Meeting', 'Office', 'package', 'Restroom', 'Storage'] - building_paths = [os.path.join(gj.loads_path.files_dir, b.dirname) for b in gj.buildings] - path_checks = [f'{os.path.sep.join(r)}.mo' for r in itertools.product(building_paths, model_names)] + model_names = [ + "Floor", + "ICT", + "Meeting", + "Office", + "package", + "Restroom", + "Storage", + ] + building_paths = [ + os.path.join(gj.loads_path.files_dir, b.dirname) for b in gj.buildings + ] + path_checks = [ + f"{os.path.sep.join(r)}.mo" + for r in itertools.product(building_paths, model_names) + ] for p in path_checks: - self.assertTrue(os.path.exists(p), f'Path not found {p}') + self.assertTrue(os.path.exists(p), f"Path not found {p}") # go through the generated buildings and ensure that the resources are created - resource_names = ['InternalGains_Floor', 'InternalGains_ICT', 'InternalGains_Meeting', 'InternalGains_Office', - 'InternalGains_Restroom', 'InternalGains_Storage'] + resource_names = [ + "InternalGains_Floor", + "InternalGains_ICT", + "InternalGains_Meeting", + "InternalGains_Office", + "InternalGains_Restroom", + "InternalGains_Storage", + ] for b in gj.buildings: for resource_name in resource_names: # TEASER 0.7.2 used .txt for schedule files - path = os.path.join(gj.loads_path.files_dir, 'Resources', 'Data', b.dirname, f'{resource_name}.txt') - self.assertTrue(os.path.exists(path), f'Path not found: {path}') + path = os.path.join( + gj.loads_path.files_dir, + "Resources", + "Data", + b.dirname, + f"{resource_name}.txt", + ) + self.assertTrue(os.path.exists(path), f"Path not found: {path}") def test_to_modelica_rc_order_4(self): - self.results_path = os.path.abspath('tests/output/rc_order_4') + self.results_path = os.path.abspath("tests/output/rc_order_4") if os.path.exists(self.results_path): shutil.rmtree(self.results_path) - filename = os.path.abspath('tests/geojson/data/geojson_1.json') + filename = os.path.abspath("tests/geojson/data/geojson_1.json") gj = GeoJsonModelicaTranslator.from_geojson(filename) sys_params = SystemParameters.loadd( - { - "buildings": { - "default": { - "load_model_parameters": { - "rc": { - "order": 4 - } - } - } - } - } + {"buildings": {"default": {"load_model_parameters": {"rc": {"order": 4}}}}} ) self.assertEqual(len(sys_params.validate()), 0) gj.set_system_parameters(sys_params) - gj.to_modelica('rc_order_4', 'tests/output') + gj.to_modelica("rc_order_4", "tests/output") # setup what we are going to check - model_names = ['Floor', 'ICT', 'Meeting', 'Office', 'package', 'Restroom', 'Storage'] - building_paths = [os.path.join(gj.loads_path.files_dir, b.dirname) for b in gj.buildings] - path_checks = [f'{os.path.sep.join(r)}.mo' for r in itertools.product(building_paths, model_names)] + model_names = [ + "Floor", + "ICT", + "Meeting", + "Office", + "package", + "Restroom", + "Storage", + ] + building_paths = [ + os.path.join(gj.loads_path.files_dir, b.dirname) for b in gj.buildings + ] + path_checks = [ + f"{os.path.sep.join(r)}.mo" + for r in itertools.product(building_paths, model_names) + ] for p in path_checks: - self.assertTrue(os.path.exists(p), f'Path not found: {p}') - - resource_names = ['InternalGains_Floor', 'InternalGains_ICT', 'InternalGains_Meeting', 'InternalGains_Office', - 'InternalGains_Restroom', 'InternalGains_Storage'] + self.assertTrue(os.path.exists(p), f"Path not found: {p}") + + resource_names = [ + "InternalGains_Floor", + "InternalGains_ICT", + "InternalGains_Meeting", + "InternalGains_Office", + "InternalGains_Restroom", + "InternalGains_Storage", + ] for b in gj.buildings: for resource_name in resource_names: # TEASER 0.7.2 used .txt for schedule files - path = os.path.join(gj.loads_path.files_dir, 'Resources', 'Data', b.dirname, f'{resource_name}.txt') - self.assertTrue(os.path.exists(path), f'Path not found: {path}') + path = os.path.join( + gj.loads_path.files_dir, + "Resources", + "Data", + b.dirname, + f"{resource_name}.txt", + ) + self.assertTrue(os.path.exists(path), f"Path not found: {path}") -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/test_utils.py b/tests/test_utils.py index 2241d1eeb..e61ecec1c 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -37,16 +37,20 @@ class ModelicaPathTest(unittest.TestCase): def test_properties(self): - mp = ModelicaPath('Loads', root_dir=None) - self.assertEqual(mp.files_dir, 'Loads') - self.assertEqual(mp.resources_dir, os.path.join('Resources', 'Data', 'Loads')) + mp = ModelicaPath("Loads", root_dir=None) + self.assertEqual(mp.files_dir, "Loads") + self.assertEqual(mp.resources_dir, os.path.join("Resources", "Data", "Loads")) def test_scaffold(self): - root_dir = os.path.abspath(os.path.join('tests', 'output', 'test_02')) - ModelicaPath('RandomContainer', root_dir) - self.assertTrue(os.path.exists(os.path.join(root_dir, 'RandomContainer'))) - self.assertTrue(os.path.exists(os.path.join(root_dir, 'Resources', 'Data', 'RandomContainer'))) + root_dir = os.path.abspath(os.path.join("tests", "output", "test_02")) + ModelicaPath("RandomContainer", root_dir) + self.assertTrue(os.path.exists(os.path.join(root_dir, "RandomContainer"))) + self.assertTrue( + os.path.exists( + os.path.join(root_dir, "Resources", "Data", "RandomContainer") + ) + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() From fff76d76bb06e9f3530be7d1080cb7ffeb0344d5 Mon Sep 17 00:00:00 2001 From: Li Date: Mon, 9 Mar 2020 11:23:46 -0600 Subject: [PATCH 37/62] increased maximum characters per line from 100 -->210 --- .pre-commit-config.yaml | 2 +- .../model_connectors/ets_template.py | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 755a0fcc8..d86cd4916 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: - id: mixed-line-ending args: ['--fix=no'] - id: flake8 - args: ['--max-line-length=100'] # default of Black + args: ['--max-line-length=210'] # default of Black - repo: https://github.com/pre-commit/mirrors-isort rev: v4.3.4 diff --git a/geojson_modelica_translator/model_connectors/ets_template.py b/geojson_modelica_translator/model_connectors/ets_template.py index e721276e6..43bf39c4c 100644 --- a/geojson_modelica_translator/model_connectors/ets_template.py +++ b/geojson_modelica_translator/model_connectors/ets_template.py @@ -113,8 +113,7 @@ def check_ets_from_building_modelica(self): def to_modelica(self): """convert ETS json to modelica""" # Here come the Jinja2 function: get_template(), which reads into templated ets model. - # CoolingIndirect.mot was manually created as a starting point, - # by adding stuff following Jinja2 syntax. + # CoolingIndirect.mot was manually created as a starting point, by adding stuff following Jinja2 syntax. # it has all the necessary parameters which need to be changed through templating. ets_template = self.template_env.get_template("CoolingIndirect.mot") @@ -188,8 +187,7 @@ def templated_ets_openloops_dymola(self): # Theoretically it is doable using extend clause from Modelica. # But we need to change the original ETS model first, in order to extend. # This is Michael Wetter suggested approach. - # if so, we don't need to template modelica models, - # but we need to connect the modelica components + # if so, we don't need to template modelica models, but we need to connect the modelica components repl_dict = {} from_str = "model CoolingIndirectOpenLoops" to_str = "model CoolingIndirectOpenLoops_Templated\n" @@ -197,8 +195,7 @@ def templated_ets_openloops_dymola(self): from_str = ( "Buildings.Applications.DHC.EnergyTransferStations.CoolingIndirect coo(" ) - to_str = "Buildings.Applications.DHC.EnergyTransferStations." \ - "ets_cooling_indirect_templated coo(" + to_str = "Buildings.Applications.DHC.EnergyTransferStations.ets_cooling_indirect_templated coo(" repl_dict[from_str] = to_str from_str = "end CoolingIndirectOpenLoops;" to_str = "end CoolingIndirectOpenLoops_Templated;" From be9d622f1511542474efe2d717448eac9432ffcc Mon Sep 17 00:00:00 2001 From: Li Date: Wed, 11 Mar 2020 17:09:29 -0600 Subject: [PATCH 38/62] put ets model locally, instead of including the whole modelica-buildings-library --- .../model_connectors/ets_template.py | 40 +- .../model_connectors/spawn.py | 8 +- .../modelica/CoolingIndirect.mo | 334 +++++++++++++++++ .../modelica/CoolingIndirectOpenLoops.mo | 179 +++++++++ .../CoolingIndirectOpenLoops_Templated.mo | 180 +++++++++ .../ets_cooling_indirect_templated.mo | 345 ++++++++++++++++++ tests/model_connectors/test_ets.py | 9 +- 7 files changed, 1067 insertions(+), 28 deletions(-) create mode 100644 geojson_modelica_translator/modelica/CoolingIndirect.mo create mode 100644 geojson_modelica_translator/modelica/CoolingIndirectOpenLoops.mo create mode 100644 geojson_modelica_translator/modelica/CoolingIndirectOpenLoops_Templated.mo create mode 100644 geojson_modelica_translator/modelica/ets_cooling_indirect_templated.mo diff --git a/geojson_modelica_translator/model_connectors/ets_template.py b/geojson_modelica_translator/model_connectors/ets_template.py index 43bf39c4c..3a38e18d6 100644 --- a/geojson_modelica_translator/model_connectors/ets_template.py +++ b/geojson_modelica_translator/model_connectors/ets_template.py @@ -39,10 +39,10 @@ def __init__( ) # get the path of modelica-buildings library - directory_up_one_levels = os.path.abspath((os.path.join(__file__, "../../"))) - dest_path = "/modelica/buildingslibrary/Buildings/Applications/DHC/EnergyTransferStations/" + # temporarily copied here to reduce repo size + directory_up_one_level = os.path.abspath(os.path.join(__file__, "../..")) self.directory_modelica_building = os.path.join( - directory_up_one_levels + dest_path + directory_up_one_level + "/modelica/CoolingIndirect.mo" ) if "\\" in self.directory_modelica_building: self.directory_modelica_building = self.directory_modelica_building.replace( @@ -126,39 +126,39 @@ def to_modelica(self): file_data = ets_template.render(ets_data=ets_data) # write templated ETS back to modelica file , to the tests folder for Dymola test - if os.path.exists( - os.path.join( + path_ets_templated = os.path.join( self.directory_ets_templated, "ets_cooling_indirect_templated.mo" ) + + if os.path.exists( + path_ets_templated ): os.remove( - os.path.join( - self.directory_ets_templated, "ets_cooling_indirect_templated.mo" - ) + path_ets_templated ) with open( - os.path.join( - self.directory_ets_templated, "ets_cooling_indirect_templated.mo" - ), + path_ets_templated, "w", ) as f: f.write(file_data) # write templated ETS back to building-modelica folder for Dymola test + path_writtenback = os.path.join(os.path.abspath(os.path.join(__file__, "../..")) + "/modelica/") + if os.path.exists( os.path.join( - self.directory_modelica_building, "ets_cooling_indirect_templated.mo" + path_writtenback, "ets_cooling_indirect_templated.mo" ) ): os.remove( os.path.join( - self.directory_modelica_building, + path_writtenback, "ets_cooling_indirect_templated.mo", ) ) with open( os.path.join( - self.directory_modelica_building, "ets_cooling_indirect_templated.mo" + path_writtenback, "ets_cooling_indirect_templated.mo" ), "w", ) as f: @@ -172,14 +172,16 @@ def templated_ets_openloops_dymola(self): to test our templated ets model. """ file = open( - self.directory_modelica_building + "/Examples/CoolingIndirectOpenLoops.mo", + os.path.join(os.getcwd(), "geojson_modelica_translator") + "/modelica/CoolingIndirectOpenLoops.mo", "r", ) - cooling_indirect_filename = "/Examples/CoolingIndirectOpenLoops_Templated.mo" + cooling_indirect_filename = "/CoolingIndirectOpenLoops_Templated.mo" # if the modelica example file is existed, delete it first - if os.path.exists(self.directory_modelica_building + cooling_indirect_filename): - os.remove(self.directory_modelica_building + cooling_indirect_filename) + path_openloops = os.path.join(os.path.abspath(os.path.join(__file__, "../..")) + "/modelica/") + + if os.path.exists(path_openloops + cooling_indirect_filename): + os.remove(path_openloops + cooling_indirect_filename) # create the modelica example file for Dymola test # TODO: Replace this with the ModelicaFile Class -- @@ -202,7 +204,7 @@ def templated_ets_openloops_dymola(self): repl_dict[from_str] = to_str with open( - self.directory_modelica_building + cooling_indirect_filename, "w" + path_openloops + cooling_indirect_filename, "w" ) as examplefile: for f in file: fx = f diff --git a/geojson_modelica_translator/model_connectors/spawn.py b/geojson_modelica_translator/model_connectors/spawn.py index fb7483e28..5394fe92d 100644 --- a/geojson_modelica_translator/model_connectors/spawn.py +++ b/geojson_modelica_translator/model_connectors/spawn.py @@ -30,13 +30,12 @@ import os import shutil -from jinja2 import FileSystemLoader, Environment -from geojson_modelica_translator.model_connectors.base import ( - Base as model_connector_base, -) +from geojson_modelica_translator.model_connectors.base import \ + Base as model_connector_base from geojson_modelica_translator.modelica.input_parser import PackageParser from geojson_modelica_translator.utils import ModelicaPath +from jinja2 import Environment, FileSystemLoader class SpawnConnector(model_connector_base): @@ -201,7 +200,6 @@ def to_modelica(self, project_name, root_building_dir): raise Exception( f"Missing MOS weather file for Spawn: {template_data['mos_weather']['mos_weather_filename']}") - file_data = spawn_fmu_template.render( project_name=project_name, model_name=f"B{building['building_id']}", diff --git a/geojson_modelica_translator/modelica/CoolingIndirect.mo b/geojson_modelica_translator/modelica/CoolingIndirect.mo new file mode 100644 index 000000000..39fbf4606 --- /dev/null +++ b/geojson_modelica_translator/modelica/CoolingIndirect.mo @@ -0,0 +1,334 @@ +within Buildings.Applications.DHC.EnergyTransferStations; +model CoolingIndirect + "Indirect cooling energy transfer station for district energy systems" + extends Buildings.Fluid.Interfaces.PartialFourPort( + redeclare final package Medium1 = Medium, + redeclare final package Medium2 = Medium); + + replaceable package Medium = + Modelica.Media.Interfaces.PartialMedium "Medium in the component"; + + // mass flow rates + parameter Modelica.SIunits.MassFlowRate m1_flow_nominal( + final min=0, + start=0.5) + "Nominal mass flow rate of primary (district) district cooling side"; + parameter Modelica.SIunits.MassFlowRate m2_flow_nominal( + final min=0, + start=0.5) + "Nominal mass flow rate of secondary (building) district cooling side"; + + // Primary supply control valve + parameter Modelica.SIunits.PressureDifference dpValve_nominal( + final min=0, + final displayUnit="Pa")=6000 + "Nominal pressure drop of fully open control valve"; + + // Heat exchanger + parameter Modelica.SIunits.PressureDifference dp1_nominal( + final min=0, + start=500, + final displayUnit="Pa") + "Nominal pressure difference on primary side" + annotation(Dialog(group="Heat exchanger")); + parameter Modelica.SIunits.PressureDifference dp2_nominal( + final min=0, + start=500, + final displayUnit="Pa") + "Nominal pressure difference on secondary side" + annotation(Dialog(group="Heat exchanger")); + parameter Boolean use_Q_flow_nominal=true + "Set to true to specify Q_flow_nominal and temperatures, or to false to specify effectiveness" + annotation(Dialog(group="Heat exchanger")); + parameter Modelica.SIunits.HeatFlowRate Q_flow_nominal( + final min=0, + start=10000) + "Nominal heat transfer" + annotation(Dialog(group="Heat exchanger")); + parameter Modelica.SIunits.Temperature T_a1_nominal( + min=0+273, + max=100+273.15, + start=5+273.15, + final displayUnit="K") + "Nominal temperature at port a1" + annotation(Dialog(group="Heat exchanger")); + parameter Modelica.SIunits.Temperature T_a2_nominal( + min=0+273, + max=100+273.15, + start=7+273.15, + final displayUnit="K") + "Nominal temperature at port a2" + annotation(Dialog(group="Heat exchanger")); + parameter Modelica.SIunits.Efficiency eta( + final min=0, + final max=1)=0.8 + "Constant effectiveness" + annotation(Dialog(group="Heat exchanger")); + + // Controller parameters + parameter Modelica.Blocks.Types.SimpleController controllerType= + Modelica.Blocks.Types.SimpleController.PI + "Type of controller" + annotation(Dialog(tab="Controller")); + parameter Real k(final min=0, final unit="1") = 1 + "Gain of controller" + annotation(Dialog(tab="Controller")); + parameter Modelica.SIunits.Time Ti( + min=Modelica.Constants.small)=120 + "Time constant of integrator block" + annotation (Dialog(tab="Controller", enable= + controllerType == Modelica.Blocks.Types.SimpleController.PI or + controllerType == Modelica.Blocks.Types.SimpleController.PID)); + parameter Modelica.SIunits.Time Td(final min=0)=0.1 + "Time constant of derivative block" + annotation (Dialog(tab="Controller", enable= + controllerType == Modelica.Blocks.Types.SimpleController.PD or + controllerType == Modelica.Blocks.Types.SimpleController.PID)); + parameter Real yMax(start=1)=1 + "Upper limit of output" + annotation(Dialog(tab="Controller")); + parameter Real yMin=0 + "Lower limit of output" + annotation(Dialog(tab="Controller")); + parameter Real wp(final min=0) = 1 + "Set-point weight for Proportional block (0..1)" + annotation(Dialog(tab="Controller")); + parameter Real wd(final min=0) = 0 + "Set-point weight for Derivative block (0..1)" + annotation(Dialog(tab="Controller", enable= + controllerType==Modelica.Blocks.Types.SimpleController.PD or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real Ni(min=100*Modelica.Constants.eps) = 0.9 + "Ni*Ti is time constant of anti-windup compensation" + annotation(Dialog(tab="Controller", enable= + controllerType==Modelica.Blocks.Types.SimpleController.PI or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real Nd(min=100*Modelica.Constants.eps) = 10 + "The higher Nd, the more ideal the derivative block" + annotation(Dialog(tab="Controller", enable= + controllerType==Modelica.Blocks.Types.SimpleController.PD or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Modelica.Blocks.Types.InitPID initType= + Modelica.Blocks.Types.InitPID.DoNotUse_InitialIntegratorState + "Type of initialization (1: no init, 2: steady state, 3: initial state, 4: initial output)" + annotation(Evaluate=true, Dialog(group="Initialization", tab="Controller")); + parameter Real xi_start=0 + "Initial or guess value value for integrator output (= integrator state)" + annotation (Dialog(group="Initialization", tab="Controller", + enable=controllerType==Modelica.Blocks.Types.SimpleController.PI or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real xd_start=0 + "Initial or guess value for state of derivative block" + annotation (Dialog(group="Initialization", tab="Controller", + enable=controllerType==Modelica.Blocks.Types.SimpleController.PD or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real yCon_start=0 + "Initial value of output from the controller" + annotation(Dialog(group="Initialization", tab="Controller", + enable=initType == Modelica.Blocks.Types.InitPID.InitialOutput)); + parameter Boolean reverseAction = true + "Set to true for throttling the water flow rate through a cooling coil controller" + annotation(Dialog(tab="Controller")); + + Modelica.Blocks.Interfaces.RealInput TSet( + final quantity="ThermodynamicTemperature", + final unit="K") + "Setpoint temperature" + annotation (Placement(transformation(extent={{-140,-20},{-100,20}}))); + + Modelica.Blocks.Interfaces.RealOutput Q_flow( + final quantity="Power", + final unit="W", + final displayUnit="kW") + "Measured power demand at the ETS" + annotation (Placement(transformation(extent={{100,140},{120,160}}))); + + Modelica.Blocks.Interfaces.RealOutput Q( + final quantity="Energy", + final unit="J", + final displayUnit="kWh") + "Measured energy consumption at the ETS" + annotation (Placement(transformation(extent={{100,100},{120,120}}))); + + Buildings.Fluid.HeatExchangers.PlateHeatExchangerEffectivenessNTU hex( + redeclare final package Medium1 = Medium, + redeclare final package Medium2 = Medium, + final m1_flow_nominal=m1_flow_nominal, + final m2_flow_nominal=m2_flow_nominal, + final dp1_nominal=dp1_nominal, + final dp2_nominal=dp2_nominal, + final configuration=Buildings.Fluid.Types.HeatExchangerConfiguration.CounterFlow, + final use_Q_flow_nominal=true, + final Q_flow_nominal=Q_flow_nominal, + final T_a1_nominal=T_a1_nominal, + final T_a2_nominal=T_a2_nominal) "Indirect cooling heat exchanger" + annotation (Placement(transformation(extent={{20,-10},{40,10}}))); + + Buildings.Controls.Continuous.LimPID con( + final controllerType=Modelica.Blocks.Types.SimpleController.PID, + final k=k, + final Td=Td, + final yMax=yMax, + final yMin=yMin, + final Ti=Ti, + final wp=wp, + final wd=wd, + final Ni=Ni, + final Nd=Nd, + final initType=Modelica.Blocks.Types.InitPID.InitialOutput, + final xi_start=xi_start, + final xd_start=xd_start, + final y_start=yCon_start, + final reverseAction=reverseAction) "Controller" + annotation (Placement(transformation(extent={{-90,-10},{-70,10}}))); + + Buildings.Fluid.Sensors.TemperatureTwoPort senTDisSup( + redeclare final package Medium = Medium, + final m_flow_nominal=m1_flow_nominal) + "District-side (primary) supply temperature sensor" + annotation (Placement(transformation(extent={{-90,50},{-70,70}}))); + + Buildings.Fluid.Sensors.TemperatureTwoPort senTDisRet( + redeclare final package Medium = Medium, + final m_flow_nominal=m1_flow_nominal) + "District-side (primary) return temperature sensor" + annotation (Placement(transformation(extent={{70,50},{90,70}}))); + + Modelica.Blocks.Continuous.Integrator int(k=1) "Integration" + annotation (Placement(transformation(extent={{60,120},{80,100}}))); + + Buildings.Fluid.Sensors.MassFlowRate senMasFlo( + redeclare package Medium = Medium) + annotation (Placement(transformation(extent={{-60,50},{-40,70}}))); + + Buildings.Fluid.Sensors.TemperatureTwoPort TBuiRet( + redeclare final package Medium = Medium, + final m_flow_nominal=m2_flow_nominal) + "Building-side (secondary) return temperature" + annotation (Placement(transformation(extent={{-70,-70},{-90,-50}}))); + + Buildings.Fluid.Actuators.Valves.TwoWayQuickOpening val( + redeclare final package Medium = Medium, + final m_flow_nominal=m1_flow_nominal, + final dpValve_nominal=dpValve_nominal, + riseTime(displayUnit="s") = 60, + y_start=0) "District-side (primary) control valve" + annotation (Placement(transformation(extent={{-30,70},{-10,50}}))); + + Modelica.Blocks.Math.Gain cp(final k=cp_default) + "Specifc heat multiplier to calculate heat flow rate" + annotation (Placement(transformation(extent={{20,100},{40,120}}))); + + Modelica.Blocks.Math.Product pro "Product" + annotation (Placement(transformation(extent={{-20,100},{0,120}}))); + + Modelica.Blocks.Math.Add dTDis(k1=-1, k2=+1) + "Temperatur difference on the district side" + annotation (Placement(transformation(extent={{-60,106},{-40,126}}))); + +protected + final parameter Medium.ThermodynamicState sta_default = Medium.setState_pTX( + T=Medium.T_default, + p=Medium.p_default, + X=Medium.X_default) "Medium state at default properties"; + final parameter Modelica.SIunits.SpecificHeatCapacity cp_default= + Medium.specificHeatCapacityCp(sta_default) + "Specific heat capacity of the fluid"; + +equation + + connect(hex.port_a2, port_a2) annotation (Line(points={{40,-6},{60,-6},{60,-60}, + {100,-60}}, color={0,127,255})); + connect(hex.port_b1, senTDisRet.port_a) annotation (Line(points={{40,6},{60,6}, + {60,60},{70,60}}, color={0,127,255})); + connect(val.port_b, hex.port_a1) annotation (Line(points={{-10,60},{0,60},{0,6}, + {20,6}}, color={0,127,255})); + connect(senMasFlo.port_b, val.port_a) + annotation (Line(points={{-40,60},{-30,60}}, color={0,127,255})); + connect(port_a1, senTDisSup.port_a) + annotation (Line(points={{-100,60},{-90,60}}, color={0,127,255})); + connect(senTDisSup.port_b, senMasFlo.port_a) + annotation (Line(points={{-70,60},{-60,60}}, color={0,127,255})); + connect(port_b2, TBuiRet.port_b) + annotation (Line(points={{-100,-60},{-90,-60}}, color={0,127,255})); + connect(senTDisRet.port_b, port_b1) + annotation (Line(points={{90,60},{100,60}}, color={0,127,255})); + connect(TSet, con.u_s) + annotation (Line(points={{-120,0},{-106,0},{-106,0},{-92,0}}, + color={0,0,127})); + connect(con.u_m, TBuiRet.T) + annotation (Line(points={{-80,-12},{-80,-49}}, color={0,0,127})); + connect(con.y, val.y) + annotation (Line(points={{-69,0},{-20,0},{-20,48}}, color={0,0,127})); + connect(TBuiRet.port_a, hex.port_b2) annotation (Line(points={{-70,-60},{0,-60}, + {0,-6},{20,-6}}, color={0,127,255})); + connect(pro.y, cp.u) + annotation (Line(points={{1,110},{18,110}}, color={0,0,127})); + connect(senMasFlo.m_flow, pro.u2) + annotation (Line(points={{-50,71},{-50,104},{-22,104}}, color={0,0,127})); + connect(senTDisSup.T, dTDis.u1) + annotation (Line(points={{-80,71},{-80,122},{-62,122}}, color={0,0,127})); + connect(senTDisRet.T, dTDis.u2) annotation (Line(points={{80,71},{80,80},{-70, + 80},{-70,110},{-62,110}}, color={0,0,127})); + connect(dTDis.y, pro.u1) + annotation (Line(points={{-39,116},{-22,116}}, color={0,0,127})); + connect(cp.y, int.u) + annotation (Line(points={{41,110},{58,110}}, color={0,0,127})); + connect(int.y, Q) + annotation (Line(points={{81,110},{110,110}}, color={0,0,127})); + connect(Q_flow, cp.y) annotation (Line(points={{110,150},{50,150},{50,110},{41, + 110}}, color={0,0,127})); + +annotation (defaultComponentName="coo", + Icon(coordinateSystem(preserveAspectRatio=false), graphics={ + Rectangle( + extent={{-100,-56},{100,-64}}, + fillColor={0,0,0}, + fillPattern=FillPattern.Solid, + pattern=LinePattern.None), + Rectangle( + extent={{-100,64},{100,56}}, + fillColor={0,0,0}, + fillPattern=FillPattern.Solid, + pattern=LinePattern.None), + Rectangle( + extent={{-80,80},{80,-80}}, + lineColor={175,175,175}, + fillColor={35,138,255}, + fillPattern=FillPattern.Solid), + Text( + extent={{-52,40},{54,-40}}, + lineColor={0,0,0}, + fillColor={35,138,255}, + fillPattern=FillPattern.Solid, + textStyle={TextStyle.Bold}, + textString="ETS")}), Diagram( + coordinateSystem(preserveAspectRatio=false, extent={{-100,-100},{100,160}})), + Documentation(info=" +

      +Indirect cooling energy transfer station (ETS) model that controls +the building chilled water supply temperature by modulating a +primary control valve on the district supply side. The design is +based on a typical district cooling ETS described in ASHRAE's + +District Cooling Guide. +As shown in the figure below, the building pumping design (constant, +variable) is specified on the building side, not within the ETS. +

      +

      +\"DHC.ETS.CoolingIndirect\"/ +

      +

      Reference

      +

      +American Society of Heating, Refrigeration and Air-Conditioning +Engineers. (2013). Chapter 5: End User Interface. In +District Cooling Guide. 1st Edition. +

      +", revisions=" +
        +
      • +November 1, 2019, by Kathryn Hinkelman:
        +First implementation.
      • +
      +")); +end CoolingIndirect; diff --git a/geojson_modelica_translator/modelica/CoolingIndirectOpenLoops.mo b/geojson_modelica_translator/modelica/CoolingIndirectOpenLoops.mo new file mode 100644 index 000000000..1527b2e08 --- /dev/null +++ b/geojson_modelica_translator/modelica/CoolingIndirectOpenLoops.mo @@ -0,0 +1,179 @@ +within Buildings.Applications.DHC.EnergyTransferStations.Examples; +model CoolingIndirectOpenLoops + "Example model for indirect cooling energy transfer station with open loops on the building and district sides" + extends Modelica.Icons.Example; + + package Medium = Buildings.Media.Water; + + parameter Modelica.SIunits.MassFlowRate m1_flow_nominal = 0.5 + "Nominal mass flow rate of primary (district) district cooling side"; + parameter Modelica.SIunits.MassFlowRate m2_flow_nominal = 0.5 + "Nominal mass flow rate of secondary (building) district cooling side"; + + Modelica.Blocks.Sources.Constant TSetCHWS(k=273.15 + 7) + "Setpoint temperature for building chilled water supply" + annotation (Placement(transformation(extent={{-120,-30},{-100,-10}}))); + Buildings.Fluid.Sources.Boundary_pT sinDis( + redeclare package Medium = Medium, + p=300000, + T=287.15, + nPorts=1) + "District-side (primary) sink" + annotation (Placement(transformation(extent={{80,40},{60,60}}))); + Buildings.Fluid.Sources.Boundary_pT souDis( + redeclare package Medium = Medium, + p(displayUnit="Pa") = 300000 + 800, + use_T_in=true, + T=278.15, + nPorts=1) + "District (primary) source" + annotation (Placement(transformation(extent={{-90,40},{-70,60}}))); + Buildings.Fluid.Sources.Boundary_pT sinBui( + redeclare package Medium = Medium, + use_T_in=false, + T=280.15, + nPorts=1) + "Building (secondary) sink (chilled water supply)" + annotation (Placement(transformation(extent={{-120,-100},{-100,-80}}))); + Buildings.Fluid.Sources.Boundary_pT souBui( + redeclare package Medium = Medium, + use_T_in=true, + T=289.15, + nPorts=1) + "Building (secondary) source (chilled water return)" + annotation (Placement(transformation(extent={{80,-100},{60,-80}}))); + Buildings.Fluid.Sensors.TemperatureTwoPort TDisSup( + redeclare package Medium = Medium, + m_flow_nominal=m1_flow_nominal, + T_start=278.15) + "District-side (primary) supply temperature sensor" + annotation (Placement(transformation(extent={{-40,40},{-20,60}}))); + Buildings.Fluid.Sensors.TemperatureTwoPort TDisRet( + redeclare package Medium = Medium, + m_flow_nominal=m1_flow_nominal, + T_start=287.15) + "District-side (primary) return temperature sensor" + annotation (Placement(transformation(extent={{20,40},{40,60}}))); + Buildings.Fluid.Sensors.TemperatureTwoPort TBuiRet( + redeclare package Medium = Medium, + m_flow_nominal=m2_flow_nominal, + T_start=289.15) + "Building-side (secondary) return temperature sensor" + annotation (Placement(transformation(extent={{40,-100},{20,-80}}))); + Buildings.Fluid.Sensors.TemperatureTwoPort TBuiSup( + redeclare package Medium = Medium, + m_flow_nominal=m2_flow_nominal, + T_start=280.15) + "Building-side (secondary) supply temperature sensor" + annotation (Placement(transformation(extent={{-50,-100},{-70,-80}}))); + Buildings.Applications.DHC.EnergyTransferStations.CoolingIndirect coo( + redeclare package Medium = Medium, + m1_flow_nominal=m1_flow_nominal, + m2_flow_nominal=m2_flow_nominal, + dp1_nominal = 500, + dp2_nominal = 500, + Q_flow_nominal=18514, + T_a1_nominal = 278.15, + T_a2_nominal = 289.15, + controllerType=Modelica.Blocks.Types.SimpleController.PI, + k=0.1, + Ti=40, + yMax=1, + yMin=0, + initType=Modelica.Blocks.Types.InitPID.InitialOutput, + yCon_start=0, + reverseAction=true) + "Indirect cooling ETS" + annotation (Placement(transformation(extent={{-10,-30},{10,-10}}))); + Buildings.Fluid.Movers.FlowControlled_m_flow pumBui( + redeclare package Medium = Medium, + m_flow_nominal=m2_flow_nominal, + inputType=Buildings.Fluid.Types.InputType.Constant, + nominalValuesDefineDefaultPressureCurve=true, + dp_nominal=6000) + "Building-side (secondary) pump" + annotation (Placement(transformation(extent={{-20,-100},{-40,-80}}))); + Modelica.Blocks.Sources.Trapezoid tra( + amplitude=1.5, + rising(displayUnit="h") = 10800, + width(displayUnit="h") = 10800, + falling(displayUnit="h") = 10800, + period(displayUnit="h") = 43200, + offset=273 + 3.5) + "District supply temperature trapezoid signal" + annotation (Placement(transformation(extent={{-120,44},{-100,64}}))); + Modelica.Blocks.Sources.RealExpression TBuiRetSig( + y=(273.15 + 16) + 2*sin(time*2*3.14/86400)) + "Sinusoidal signal for return temperature on building (secondary) side" + annotation (Placement(transformation(extent={{120,-96},{100,-76}}))); + Modelica.Blocks.Math.Add TApp(k2=-1) "Calculate approach temperature" + annotation (Placement(transformation(extent={{0,90},{20,110}}))); + Modelica.Blocks.Math.Add dTDis(k1=-1) + "Calculate change in district temperature" + annotation (Placement(transformation(extent={{60,70},{80,90}}))); + Modelica.Blocks.Math.Add dTBui(k1=-1) + "Calculate change in building temperature" + annotation (Placement(transformation(extent={{88,-50},{108,-30}}))); +equation + connect(coo.port_b2, pumBui.port_a) annotation (Line(points={{-10,-26},{-16,-26}, + {-16,-90},{-20,-90}}, color={0,127,255})); + connect(tra.y, souDis.T_in) + annotation (Line(points={{-99,54},{-92,54}}, color={0,0,127})); + connect(TBuiRetSig.y, souBui.T_in) + annotation (Line(points={{99,-86},{82,-86}}, color={0,0,127})); + connect(TSetCHWS.y, coo.TSet) + annotation (Line(points={{-99,-20},{-12,-20}}, + color={0,0,127})); + connect(souDis.ports[1], TDisSup.port_a) + annotation (Line(points={{-70,50},{-40,50}}, color={0,127,255})); + connect(TDisSup.port_b, coo.port_a1) annotation (Line(points={{-20,50},{-16,50}, + {-16,-14},{-10,-14}}, color={0,127,255})); + connect(coo.port_b1, TDisRet.port_a) annotation (Line(points={{10,-14},{16,-14}, + {16,50},{20,50}}, color={0,127,255})); + connect(TDisRet.port_b, sinDis.ports[1]) + annotation (Line(points={{40,50},{60,50}}, color={0,127,255})); + connect(TBuiSup.T, TApp.u1) + annotation (Line(points={{-60,-79},{-60,106},{-2,106}}, color={0,0,127})); + connect(TDisSup.T, TApp.u2) + annotation (Line(points={{-30,61},{-30,94},{-2,94}}, color={0,0,127})); + connect(TDisRet.T, dTDis.u2) + annotation (Line(points={{30,61},{30,74},{58,74}}, color={0,0,127})); + connect(dTDis.u1, TDisSup.T) + annotation (Line(points={{58,86},{-30,86},{-30,61}}, color={0,0,127})); + connect(TBuiRet.T, dTBui.u2) + annotation (Line(points={{30,-79},{30,-46},{86,-46}}, color={0,0,127})); + connect(TBuiSup.T, dTBui.u1) + annotation (Line(points={{-60,-79},{-60,-34},{86,-34}}, color={0,0,127})); + connect(pumBui.port_b, TBuiSup.port_a) + annotation (Line(points={{-40,-90},{-50,-90}}, color={0,127,255})); + connect(TBuiSup.port_b, sinBui.ports[1]) + annotation (Line(points={{-70,-90},{-100,-90}}, color={0,127,255})); + connect(coo.port_a2, TBuiRet.port_b) annotation (Line(points={{10,-26},{16, + -26},{16,-90},{20,-90}}, color={0,127,255})); + connect(TBuiRet.port_a, souBui.ports[1]) + annotation (Line(points={{40,-90},{60,-90}}, color={0,127,255})); + annotation (Icon(coordinateSystem(preserveAspectRatio=false, + extent={{-100,-100},{100,100}})), + Diagram(coordinateSystem(preserveAspectRatio=false, + extent={{-140,-120},{140,120}})), + __Dymola_Commands(file= + "modelica://Buildings/Resources/Scripts/Dymola/Applications/DHC/EnergyTransferStations/Examples/CoolingIndirectOpenLoops.mos" + "Simulate and plot"), + experiment( + StartTime=0, + StopTime=86400, + Tolerance=1e-06), + Documentation(info=" +

      This model provides an example for the indirect cooling energy transfer station model. +Both the district and building chilled water loops are open. The district supply temperature +is modulating, while the modulating building return temperature mimics a theoretically +variable cooling load at the building.

      +", revisions=" +
        +
      • +November 1, 2019, by Kathryn Hinkelman:
        +First implementation. +
      • +
      +")); +end CoolingIndirectOpenLoops; diff --git a/geojson_modelica_translator/modelica/CoolingIndirectOpenLoops_Templated.mo b/geojson_modelica_translator/modelica/CoolingIndirectOpenLoops_Templated.mo new file mode 100644 index 000000000..5eb407b47 --- /dev/null +++ b/geojson_modelica_translator/modelica/CoolingIndirectOpenLoops_Templated.mo @@ -0,0 +1,180 @@ +within Buildings.Applications.DHC.EnergyTransferStations.Examples; +model CoolingIndirectOpenLoops_Templated + + "Example model for indirect cooling energy transfer station with open loops on the building and district sides" + extends Modelica.Icons.Example; + + package Medium = Buildings.Media.Water; + + parameter Modelica.SIunits.MassFlowRate m1_flow_nominal = 0.5 + "Nominal mass flow rate of primary (district) district cooling side"; + parameter Modelica.SIunits.MassFlowRate m2_flow_nominal = 0.5 + "Nominal mass flow rate of secondary (building) district cooling side"; + + Modelica.Blocks.Sources.Constant TSetCHWS(k=273.15 + 7) + "Setpoint temperature for building chilled water supply" + annotation (Placement(transformation(extent={{-120,-30},{-100,-10}}))); + Buildings.Fluid.Sources.Boundary_pT sinDis( + redeclare package Medium = Medium, + p=300000, + T=287.15, + nPorts=1) + "District-side (primary) sink" + annotation (Placement(transformation(extent={{80,40},{60,60}}))); + Buildings.Fluid.Sources.Boundary_pT souDis( + redeclare package Medium = Medium, + p(displayUnit="Pa") = 300000 + 800, + use_T_in=true, + T=278.15, + nPorts=1) + "District (primary) source" + annotation (Placement(transformation(extent={{-90,40},{-70,60}}))); + Buildings.Fluid.Sources.Boundary_pT sinBui( + redeclare package Medium = Medium, + use_T_in=false, + T=280.15, + nPorts=1) + "Building (secondary) sink (chilled water supply)" + annotation (Placement(transformation(extent={{-120,-100},{-100,-80}}))); + Buildings.Fluid.Sources.Boundary_pT souBui( + redeclare package Medium = Medium, + use_T_in=true, + T=289.15, + nPorts=1) + "Building (secondary) source (chilled water return)" + annotation (Placement(transformation(extent={{80,-100},{60,-80}}))); + Buildings.Fluid.Sensors.TemperatureTwoPort TDisSup( + redeclare package Medium = Medium, + m_flow_nominal=m1_flow_nominal, + T_start=278.15) + "District-side (primary) supply temperature sensor" + annotation (Placement(transformation(extent={{-40,40},{-20,60}}))); + Buildings.Fluid.Sensors.TemperatureTwoPort TDisRet( + redeclare package Medium = Medium, + m_flow_nominal=m1_flow_nominal, + T_start=287.15) + "District-side (primary) return temperature sensor" + annotation (Placement(transformation(extent={{20,40},{40,60}}))); + Buildings.Fluid.Sensors.TemperatureTwoPort TBuiRet( + redeclare package Medium = Medium, + m_flow_nominal=m2_flow_nominal, + T_start=289.15) + "Building-side (secondary) return temperature sensor" + annotation (Placement(transformation(extent={{40,-100},{20,-80}}))); + Buildings.Fluid.Sensors.TemperatureTwoPort TBuiSup( + redeclare package Medium = Medium, + m_flow_nominal=m2_flow_nominal, + T_start=280.15) + "Building-side (secondary) supply temperature sensor" + annotation (Placement(transformation(extent={{-50,-100},{-70,-80}}))); + Buildings.Applications.DHC.EnergyTransferStations.ets_cooling_indirect_templated coo( + redeclare package Medium = Medium, + m1_flow_nominal=m1_flow_nominal, + m2_flow_nominal=m2_flow_nominal, + dp1_nominal = 500, + dp2_nominal = 500, + Q_flow_nominal=18514, + T_a1_nominal = 278.15, + T_a2_nominal = 289.15, + controllerType=Modelica.Blocks.Types.SimpleController.PI, + k=0.1, + Ti=40, + yMax=1, + yMin=0, + initType=Modelica.Blocks.Types.InitPID.InitialOutput, + yCon_start=0, + reverseAction=true) + "Indirect cooling ETS" + annotation (Placement(transformation(extent={{-10,-30},{10,-10}}))); + Buildings.Fluid.Movers.FlowControlled_m_flow pumBui( + redeclare package Medium = Medium, + m_flow_nominal=m2_flow_nominal, + inputType=Buildings.Fluid.Types.InputType.Constant, + nominalValuesDefineDefaultPressureCurve=true, + dp_nominal=6000) + "Building-side (secondary) pump" + annotation (Placement(transformation(extent={{-20,-100},{-40,-80}}))); + Modelica.Blocks.Sources.Trapezoid tra( + amplitude=1.5, + rising(displayUnit="h") = 10800, + width(displayUnit="h") = 10800, + falling(displayUnit="h") = 10800, + period(displayUnit="h") = 43200, + offset=273 + 3.5) + "District supply temperature trapezoid signal" + annotation (Placement(transformation(extent={{-120,44},{-100,64}}))); + Modelica.Blocks.Sources.RealExpression TBuiRetSig( + y=(273.15 + 16) + 2*sin(time*2*3.14/86400)) + "Sinusoidal signal for return temperature on building (secondary) side" + annotation (Placement(transformation(extent={{120,-96},{100,-76}}))); + Modelica.Blocks.Math.Add TApp(k2=-1) "Calculate approach temperature" + annotation (Placement(transformation(extent={{0,90},{20,110}}))); + Modelica.Blocks.Math.Add dTDis(k1=-1) + "Calculate change in district temperature" + annotation (Placement(transformation(extent={{60,70},{80,90}}))); + Modelica.Blocks.Math.Add dTBui(k1=-1) + "Calculate change in building temperature" + annotation (Placement(transformation(extent={{88,-50},{108,-30}}))); +equation + connect(coo.port_b2, pumBui.port_a) annotation (Line(points={{-10,-26},{-16,-26}, + {-16,-90},{-20,-90}}, color={0,127,255})); + connect(tra.y, souDis.T_in) + annotation (Line(points={{-99,54},{-92,54}}, color={0,0,127})); + connect(TBuiRetSig.y, souBui.T_in) + annotation (Line(points={{99,-86},{82,-86}}, color={0,0,127})); + connect(TSetCHWS.y, coo.TSet) + annotation (Line(points={{-99,-20},{-12,-20}}, + color={0,0,127})); + connect(souDis.ports[1], TDisSup.port_a) + annotation (Line(points={{-70,50},{-40,50}}, color={0,127,255})); + connect(TDisSup.port_b, coo.port_a1) annotation (Line(points={{-20,50},{-16,50}, + {-16,-14},{-10,-14}}, color={0,127,255})); + connect(coo.port_b1, TDisRet.port_a) annotation (Line(points={{10,-14},{16,-14}, + {16,50},{20,50}}, color={0,127,255})); + connect(TDisRet.port_b, sinDis.ports[1]) + annotation (Line(points={{40,50},{60,50}}, color={0,127,255})); + connect(TBuiSup.T, TApp.u1) + annotation (Line(points={{-60,-79},{-60,106},{-2,106}}, color={0,0,127})); + connect(TDisSup.T, TApp.u2) + annotation (Line(points={{-30,61},{-30,94},{-2,94}}, color={0,0,127})); + connect(TDisRet.T, dTDis.u2) + annotation (Line(points={{30,61},{30,74},{58,74}}, color={0,0,127})); + connect(dTDis.u1, TDisSup.T) + annotation (Line(points={{58,86},{-30,86},{-30,61}}, color={0,0,127})); + connect(TBuiRet.T, dTBui.u2) + annotation (Line(points={{30,-79},{30,-46},{86,-46}}, color={0,0,127})); + connect(TBuiSup.T, dTBui.u1) + annotation (Line(points={{-60,-79},{-60,-34},{86,-34}}, color={0,0,127})); + connect(pumBui.port_b, TBuiSup.port_a) + annotation (Line(points={{-40,-90},{-50,-90}}, color={0,127,255})); + connect(TBuiSup.port_b, sinBui.ports[1]) + annotation (Line(points={{-70,-90},{-100,-90}}, color={0,127,255})); + connect(coo.port_a2, TBuiRet.port_b) annotation (Line(points={{10,-26},{16, + -26},{16,-90},{20,-90}}, color={0,127,255})); + connect(TBuiRet.port_a, souBui.ports[1]) + annotation (Line(points={{40,-90},{60,-90}}, color={0,127,255})); + annotation (Icon(coordinateSystem(preserveAspectRatio=false, + extent={{-100,-100},{100,100}})), + Diagram(coordinateSystem(preserveAspectRatio=false, + extent={{-140,-120},{140,120}})), + __Dymola_Commands(file= + "modelica://Buildings/Resources/Scripts/Dymola/Applications/DHC/EnergyTransferStations/Examples/CoolingIndirectOpenLoops.mos" + "Simulate and plot"), + experiment( + StartTime=0, + StopTime=86400, + Tolerance=1e-06), + Documentation(info=" +

      This model provides an example for the indirect cooling energy transfer station model. +Both the district and building chilled water loops are open. The district supply temperature +is modulating, while the modulating building return temperature mimics a theoretically +variable cooling load at the building.

      +", revisions=" +
        +
      • +November 1, 2019, by Kathryn Hinkelman:
        +First implementation. +
      • +
      +")); +end CoolingIndirectOpenLoops_Templated; diff --git a/geojson_modelica_translator/modelica/ets_cooling_indirect_templated.mo b/geojson_modelica_translator/modelica/ets_cooling_indirect_templated.mo new file mode 100644 index 000000000..afffe8cae --- /dev/null +++ b/geojson_modelica_translator/modelica/ets_cooling_indirect_templated.mo @@ -0,0 +1,345 @@ + +within Buildings.Applications.DHC.EnergyTransferStations; + +model ets_cooling_indirect_templated + + "Indirect cooling energy transfer station for district energy systems" + extends Buildings.Fluid.Interfaces.PartialFourPort( + redeclare final package Medium1 = Medium, + redeclare final package Medium2 = Medium); + + replaceable package Medium = + Modelica.Media.Interfaces.PartialMedium "Medium in the component"; + + // mass flow rates + parameter Modelica.SIunits.MassFlowRate m1_flow_nominal( + final min=0, + start=0.666) + "Nominal mass flow rate of primary (district) district cooling side"; + + parameter Modelica.SIunits.MassFlowRate m2_flow_nominal( + final min=0, + start=0.5) + "Nominal mass flow rate of secondary (building) district cooling side"; + + // Primary supply control valve + parameter Modelica.SIunits.PressureDifference dpValve_nominal( + final min=0, + final displayUnit="Pa")=888 + "Nominal pressure drop of fully open control valve"; + + // Heat exchanger + parameter Modelica.SIunits.PressureDifference dp1_nominal( + final min=0, + start=999, + final displayUnit="Pa") + "Nominal pressure difference on primary side" + annotation(Dialog(group="Heat exchanger")); + + parameter Modelica.SIunits.PressureDifference dp2_nominal( + final min=0, + start=999, + final displayUnit="Pa") + "Nominal pressure difference on secondary side" + annotation(Dialog(group="Heat exchanger")); + + parameter Boolean use_Q_flow_nominal=true + "Set to true to specify Q_flow_nominal and temperatures, or to false to specify effectiveness" + annotation(Dialog(group="Heat exchanger")); + + parameter Modelica.SIunits.HeatFlowRate Q_flow_nominal( + final min=0, + start=8000) + "Nominal heat transfer" + annotation(Dialog(group="Heat exchanger")); + + parameter Modelica.SIunits.Temperature T_a1_nominal( + min=0+273, + max=100+273.15, + start=5+273.15, + final displayUnit="K") + "Nominal temperature at port a1" + annotation(Dialog(group="Heat exchanger")); + + parameter Modelica.SIunits.Temperature T_a2_nominal( + min=0+273, + max=100+273.15, + start=7+273.15, + final displayUnit="K") + "Nominal temperature at port a2" + annotation(Dialog(group="Heat exchanger")); + + parameter Modelica.SIunits.Efficiency eta( + final min=0, + final max=1)=0.666 + "Constant effectiveness" + annotation(Dialog(group="Heat exchanger")); + + // Controller parameters + parameter Modelica.Blocks.Types.SimpleController controllerType= + Modelica.Blocks.Types.SimpleController.PI + "Type of controller" + annotation(Dialog(tab="Controller")); + parameter Real k(final min=0, final unit="1") = 1 + "Gain of controller" + annotation(Dialog(tab="Controller")); + parameter Modelica.SIunits.Time Ti( + min=Modelica.Constants.small)=120 + "Time constant of integrator block" + annotation (Dialog(tab="Controller", enable= + controllerType == Modelica.Blocks.Types.SimpleController.PI or + controllerType == Modelica.Blocks.Types.SimpleController.PID)); + parameter Modelica.SIunits.Time Td(final min=0)=0.1 + "Time constant of derivative block" + annotation (Dialog(tab="Controller", enable= + controllerType == Modelica.Blocks.Types.SimpleController.PD or + controllerType == Modelica.Blocks.Types.SimpleController.PID)); + parameter Real yMax(start=1)=1 + "Upper limit of output" + annotation(Dialog(tab="Controller")); + parameter Real yMin=0 + "Lower limit of output" + annotation(Dialog(tab="Controller")); + parameter Real wp(final min=0) = 1 + "Set-point weight for Proportional block (0..1)" + annotation(Dialog(tab="Controller")); + parameter Real wd(final min=0) = 0 + "Set-point weight for Derivative block (0..1)" + annotation(Dialog(tab="Controller", enable= + controllerType==Modelica.Blocks.Types.SimpleController.PD or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real Ni(min=100*Modelica.Constants.eps) = 0.9 + "Ni*Ti is time constant of anti-windup compensation" + annotation(Dialog(tab="Controller", enable= + controllerType==Modelica.Blocks.Types.SimpleController.PI or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real Nd(min=100*Modelica.Constants.eps) = 10 + "The higher Nd, the more ideal the derivative block" + annotation(Dialog(tab="Controller", enable= + controllerType==Modelica.Blocks.Types.SimpleController.PD or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Modelica.Blocks.Types.InitPID initType= + Modelica.Blocks.Types.InitPID.DoNotUse_InitialIntegratorState + "Type of initialization (1: no init, 2: steady state, 3: initial state, 4: initial output)" + annotation(Evaluate=true, Dialog(group="Initialization", tab="Controller")); + parameter Real xi_start=0 + "Initial or guess value value for integrator output (= integrator state)" + annotation (Dialog(group="Initialization", tab="Controller", + enable=controllerType==Modelica.Blocks.Types.SimpleController.PI or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real xd_start=0 + "Initial or guess value for state of derivative block" + annotation (Dialog(group="Initialization", tab="Controller", + enable=controllerType==Modelica.Blocks.Types.SimpleController.PD or + controllerType==Modelica.Blocks.Types.SimpleController.PID)); + parameter Real yCon_start=0 + "Initial value of output from the controller" + annotation(Dialog(group="Initialization", tab="Controller", + enable=initType == Modelica.Blocks.Types.InitPID.InitialOutput)); + parameter Boolean reverseAction = true + "Set to true for throttling the water flow rate through a cooling coil controller" + annotation(Dialog(tab="Controller")); + + Modelica.Blocks.Interfaces.RealInput TSet( + final quantity="ThermodynamicTemperature", + final unit="K") + "Setpoint temperature" + annotation (Placement(transformation(extent={{-140,-20},{-100,20}}))); + + Modelica.Blocks.Interfaces.RealOutput Q_flow( + final quantity="Power", + final unit="W", + final displayUnit="kW") + "Measured power demand at the ETS" + annotation (Placement(transformation(extent={{100,140},{120,160}}))); + + Modelica.Blocks.Interfaces.RealOutput Q( + final quantity="Energy", + final unit="J", + final displayUnit="kWh") + "Measured energy consumption at the ETS" + annotation (Placement(transformation(extent={{100,100},{120,120}}))); + + Buildings.Fluid.HeatExchangers.PlateHeatExchangerEffectivenessNTU hex( + redeclare final package Medium1 = Medium, + redeclare final package Medium2 = Medium, + final m1_flow_nominal=m1_flow_nominal, + final m2_flow_nominal=m2_flow_nominal, + final dp1_nominal=dp1_nominal, + final dp2_nominal=dp2_nominal, + final configuration=Buildings.Fluid.Types.HeatExchangerConfiguration.CounterFlow, + final use_Q_flow_nominal=true, + final Q_flow_nominal=Q_flow_nominal, + final T_a1_nominal=T_a1_nominal, + final T_a2_nominal=T_a2_nominal) "Indirect cooling heat exchanger" + annotation (Placement(transformation(extent={{20,-10},{40,10}}))); + + Buildings.Controls.Continuous.LimPID con( + final controllerType=Modelica.Blocks.Types.SimpleController.PID, + final k=k, + final Td=Td, + final yMax=yMax, + final yMin=yMin, + final Ti=Ti, + final wp=wp, + final wd=wd, + final Ni=Ni, + final Nd=Nd, + final initType=Modelica.Blocks.Types.InitPID.InitialOutput, + final xi_start=xi_start, + final xd_start=xd_start, + final y_start=yCon_start, + final reverseAction=reverseAction) "Controller" + annotation (Placement(transformation(extent={{-90,-10},{-70,10}}))); + + Buildings.Fluid.Sensors.TemperatureTwoPort senTDisSup( + redeclare final package Medium = Medium, + final m_flow_nominal=m1_flow_nominal) + "District-side (primary) supply temperature sensor" + annotation (Placement(transformation(extent={{-90,50},{-70,70}}))); + + Buildings.Fluid.Sensors.TemperatureTwoPort senTDisRet( + redeclare final package Medium = Medium, + final m_flow_nominal=m1_flow_nominal) + "District-side (primary) return temperature sensor" + annotation (Placement(transformation(extent={{70,50},{90,70}}))); + + Modelica.Blocks.Continuous.Integrator int(k=1) "Integration" + annotation (Placement(transformation(extent={{60,120},{80,100}}))); + + Buildings.Fluid.Sensors.MassFlowRate senMasFlo( + redeclare package Medium = Medium) + annotation (Placement(transformation(extent={{-60,50},{-40,70}}))); + + Buildings.Fluid.Sensors.TemperatureTwoPort TBuiRet( + redeclare final package Medium = Medium, + final m_flow_nominal=m2_flow_nominal) + "Building-side (secondary) return temperature" + annotation (Placement(transformation(extent={{-70,-70},{-90,-50}}))); + + Buildings.Fluid.Actuators.Valves.TwoWayQuickOpening val( + redeclare final package Medium = Medium, + final m_flow_nominal=m1_flow_nominal, + final dpValve_nominal=dpValve_nominal, + riseTime(displayUnit="s") = 60, + y_start=0) "District-side (primary) control valve" + annotation (Placement(transformation(extent={{-30,70},{-10,50}}))); + + Modelica.Blocks.Math.Gain cp(final k=cp_default) + "Specifc heat multiplier to calculate heat flow rate" + annotation (Placement(transformation(extent={{20,100},{40,120}}))); + + Modelica.Blocks.Math.Product pro "Product" + annotation (Placement(transformation(extent={{-20,100},{0,120}}))); + + Modelica.Blocks.Math.Add dTDis(k1=-1, k2=+1) + "Temperatur difference on the district side" + annotation (Placement(transformation(extent={{-60,106},{-40,126}}))); + +protected + final parameter Medium.ThermodynamicState sta_default = Medium.setState_pTX( + T=Medium.T_default, + p=Medium.p_default, + X=Medium.X_default) "Medium state at default properties"; + final parameter Modelica.SIunits.SpecificHeatCapacity cp_default= + Medium.specificHeatCapacityCp(sta_default) + "Specific heat capacity of the fluid"; + +equation + + connect(hex.port_a2, port_a2) annotation (Line(points={{40,-6},{60,-6},{60,-60}, + {100,-60}}, color={0,127,255})); + connect(hex.port_b1, senTDisRet.port_a) annotation (Line(points={{40,6},{60,6}, + {60,60},{70,60}}, color={0,127,255})); + connect(val.port_b, hex.port_a1) annotation (Line(points={{-10,60},{0,60},{0,6}, + {20,6}}, color={0,127,255})); + connect(senMasFlo.port_b, val.port_a) + annotation (Line(points={{-40,60},{-30,60}}, color={0,127,255})); + connect(port_a1, senTDisSup.port_a) + annotation (Line(points={{-100,60},{-90,60}}, color={0,127,255})); + connect(senTDisSup.port_b, senMasFlo.port_a) + annotation (Line(points={{-70,60},{-60,60}}, color={0,127,255})); + connect(port_b2, TBuiRet.port_b) + annotation (Line(points={{-100,-60},{-90,-60}}, color={0,127,255})); + connect(senTDisRet.port_b, port_b1) + annotation (Line(points={{90,60},{100,60}}, color={0,127,255})); + connect(TSet, con.u_s) + annotation (Line(points={{-120,0},{-106,0},{-106,0},{-92,0}}, + color={0,0,127})); + connect(con.u_m, TBuiRet.T) + annotation (Line(points={{-80,-12},{-80,-49}}, color={0,0,127})); + connect(con.y, val.y) + annotation (Line(points={{-69,0},{-20,0},{-20,48}}, color={0,0,127})); + connect(TBuiRet.port_a, hex.port_b2) annotation (Line(points={{-70,-60},{0,-60}, + {0,-6},{20,-6}}, color={0,127,255})); + connect(pro.y, cp.u) + annotation (Line(points={{1,110},{18,110}}, color={0,0,127})); + connect(senMasFlo.m_flow, pro.u2) + annotation (Line(points={{-50,71},{-50,104},{-22,104}}, color={0,0,127})); + connect(senTDisSup.T, dTDis.u1) + annotation (Line(points={{-80,71},{-80,122},{-62,122}}, color={0,0,127})); + connect(senTDisRet.T, dTDis.u2) annotation (Line(points={{80,71},{80,80},{-70, + 80},{-70,110},{-62,110}}, color={0,0,127})); + connect(dTDis.y, pro.u1) + annotation (Line(points={{-39,116},{-22,116}}, color={0,0,127})); + connect(cp.y, int.u) + annotation (Line(points={{41,110},{58,110}}, color={0,0,127})); + connect(int.y, Q) + annotation (Line(points={{81,110},{110,110}}, color={0,0,127})); + connect(Q_flow, cp.y) annotation (Line(points={{110,150},{50,150},{50,110},{41, + 110}}, color={0,0,127})); + +annotation (defaultComponentName="coo", + Icon(coordinateSystem(preserveAspectRatio=false), graphics={ + Rectangle( + extent={{-100,-56},{100,-64}}, + fillColor={0,0,0}, + fillPattern=FillPattern.Solid, + pattern=LinePattern.None), + Rectangle( + extent={{-100,64},{100,56}}, + fillColor={0,0,0}, + fillPattern=FillPattern.Solid, + pattern=LinePattern.None), + Rectangle( + extent={{-80,80},{80,-80}}, + lineColor={175,175,175}, + fillColor={35,138,255}, + fillPattern=FillPattern.Solid), + Text( + extent={{-52,40},{54,-40}}, + lineColor={0,0,0}, + fillColor={35,138,255}, + fillPattern=FillPattern.Solid, + textStyle={TextStyle.Bold}, + textString="ETS")}), Diagram( + coordinateSystem(preserveAspectRatio=false, extent={{-100,-100},{100,160}})), + Documentation(info=" +

      +Indirect cooling energy transfer station (ETS) model that controls +the building chilled water supply temperature by modulating a +primary control valve on the district supply side. The design is +based on a typical district cooling ETS described in ASHRAE's + +District Cooling Guide. +As shown in the figure below, the building pumping design (constant, +variable) is specified on the building side, not within the ETS. +

      +

      +\"DHC.ETS.CoolingIndirect\"/ +

      +

      Reference

      +

      +American Society of Heating, Refrigeration and Air-Conditioning +Engineers. (2013). Chapter 5: End User Interface. In +District Cooling Guide. 1st Edition. +

      +", revisions=" +
        +
      • +November 1, 2019, by Kathryn Hinkelman:
        +First implementation.
      • +
      • 12/15/2020, Yanfei Li, Templating ETS model.
      • +
      +")); +end ets_cooling_indirect_templated; diff --git a/tests/model_connectors/test_ets.py b/tests/model_connectors/test_ets.py index 6c1275192..771a2ac8c 100644 --- a/tests/model_connectors/test_ets.py +++ b/tests/model_connectors/test_ets.py @@ -1,11 +1,12 @@ import os import unittest -from geojson_modelica_translator.model_connectors.ets_template import ETSTemplate -from ..context import geojson_modelica_translator # noqa - Do not remove this line - +from geojson_modelica_translator.model_connectors.ets_template import \ + ETSTemplate # TODO: do not mix upper camel case and snake case. Spell out ETS or use ETSModelConnector... + + class ETSModelConnectorSingleBuildingTest(unittest.TestCase): def setUp(self): # the first method/member must be setUp base_folder = os.path.join(os.getcwd(), "geojson_modelica_translator") @@ -13,7 +14,7 @@ def setUp(self): # the first method/member must be setUp self.thermal_junction_properties_geojson = base_folder + dest_path dest_path = "/system_parameters/schema.json" self.system_parameters_geojson = base_folder + dest_path - dest_path = "/modelica/buildingslibrary/Buildings/Applications/DHC/EnergyTransferStations/CoolingIndirect.mo" + dest_path = "/modelica/CoolingIndirect.mo" self.ets_from_building_modelica = base_folder + dest_path self.ets = ETSTemplate( From 39a67fce986be456a7b800555e374b4a0d419f94 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Tue, 17 Mar 2020 17:35:47 -0600 Subject: [PATCH 39/62] code cleanup --- AUTHORS.rst | 1 + README.rst | 18 +++- .../model_connectors/ets_template.py | 83 +++++------------- .../model_connectors/spawn.py | 52 +++--------- .../model_connectors/teaser.py | 85 +++++-------------- .../system_parameters/system_parameters.py | 4 +- geojson_modelica_translator/utils.py | 4 +- management/update_licenses.py | 10 +-- tests/model_connectors/test_ets.py | 5 +- 9 files changed, 77 insertions(+), 185 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 05fe77e17..228762ef8 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -2,3 +2,4 @@ Contributors ============ * Nicholas Long +* Yanfei Li diff --git a/README.rst b/README.rst index 7aed1c393..9826b4347 100644 --- a/README.rst +++ b/README.rst @@ -25,11 +25,28 @@ currently you need python3 and pip3 to install/build the packages. .. code-block:: bash pip install -r requirements.txt + # or pip install . python setup.py build python setup.py test The py.test tests should all pass assuming the libraries are installed correctly on your development computer. Also, there will be a set of Modelica models that are created and persisted into the `tests/output` folder. +Developers +********** + +This project used `pre-commit `_ to ensure code consistency. To enable pre-commit, run the following from the command line. + +.. code-block:: bash + + pip install pre-commit + pre-commit install + +To run pre-commit against the files without calling git commit, then run the following. This is useful when cleaning up the repo before committing. + +.. code-block:: bash + + pre-commit run --all-files + Modules ******* @@ -142,7 +159,6 @@ Todos * handle weather in Teaser * Create a Script directory in the modelica_path class -* EBNF parsing * Validate remaining schema objects * AHU example * runnable example diff --git a/geojson_modelica_translator/model_connectors/ets_template.py b/geojson_modelica_translator/model_connectors/ets_template.py index 3a38e18d6..62d68eef0 100644 --- a/geojson_modelica_translator/model_connectors/ets_template.py +++ b/geojson_modelica_translator/model_connectors/ets_template.py @@ -3,40 +3,28 @@ from jinja2 import Environment, FileSystemLoader -# TODO: Class name should be upper camel case, not a mix of camel and snake case. - class ETSTemplate: """This class will template the ETS modelica model.""" - def __init__( - self, - thermal_junction_properties_geojson, - system_parameters_geojson, - ets_from_building_modelica, - ): - super().__init__() + def __init__(self, thermal_junction_properties_geojson, system_parameters_geojson, ets_from_building_modelica): """ thermal_junction_properties_geojson contains the ETS at brief and at higher level; system_parameters_geojson contains the ETS with details ; ets_from_building_modelica contains the modelica model of ETS ; """ + super().__init__() + self.thermal_junction_properties_geojson = thermal_junction_properties_geojson - self.thermal_junction_properties_geojson = self.thermal_junction_properties_geojson.replace( - "\\", "/" - ) + self.thermal_junction_properties_geojson = self.thermal_junction_properties_geojson.replace("\\", "/") self.system_parameters_geojson = system_parameters_geojson if "\\" in self.system_parameters_geojson: - self.system_parameters_geojson = self.system_parameters_geojson.replace( - "\\", "/" - ) + self.system_parameters_geojson = self.system_parameters_geojson.replace("\\", "/") self.ets_from_building_modelica = ets_from_building_modelica if "\\" in self.ets_from_building_modelica: - self.ets_from_building_modelica = self.ets_from_building_modelica.replace( - "\\", "/" - ) + self.ets_from_building_modelica = self.ets_from_building_modelica.replace("\\", "/") # get the path of modelica-buildings library # temporarily copied here to reduce repo size @@ -45,9 +33,7 @@ def __init__( directory_up_one_level + "/modelica/CoolingIndirect.mo" ) if "\\" in self.directory_modelica_building: - self.directory_modelica_building = self.directory_modelica_building.replace( - "\\", "/" - ) + self.directory_modelica_building = self.directory_modelica_building.replace("\\", "/") # go up two levels of directory, to get the path of tests folder for ets directory_up_two_levels = os.path.abspath(os.path.join(__file__, "../../..")) @@ -55,9 +41,7 @@ def __init__( directory_up_two_levels + "/tests/output_ets" ) if "\\" in self.directory_ets_templated: - self.directory_ets_templated = self.directory_ets_templated.replace( - "\\", "/" - ) + self.directory_ets_templated = self.directory_ets_templated.replace("\\", "/") if not os.path.isdir(self.directory_ets_templated): os.mkdir(self.directory_ets_templated) @@ -68,9 +52,7 @@ def __init__( # it loads all the "*.mot" files into an environment by Jinja2 self.template_env = Environment( loader=FileSystemLoader( - searchpath=os.path.join( - os.path.dirname(os.path.abspath(__file__)), "templates" - ) + searchpath=os.path.join(os.path.dirname(os.path.abspath(__file__)), "templates") ) ) @@ -126,42 +108,19 @@ def to_modelica(self): file_data = ets_template.render(ets_data=ets_data) # write templated ETS back to modelica file , to the tests folder for Dymola test - path_ets_templated = os.path.join( - self.directory_ets_templated, "ets_cooling_indirect_templated.mo" - ) + path_ets_templated = os.path.join(self.directory_ets_templated, "ets_cooling_indirect_templated.mo") - if os.path.exists( - path_ets_templated - ): - os.remove( - path_ets_templated - ) - with open( - path_ets_templated, - "w", - ) as f: + if os.path.exists(path_ets_templated): + os.remove(path_ets_templated) + with open(path_ets_templated, "w") as f: f.write(file_data) # write templated ETS back to building-modelica folder for Dymola test path_writtenback = os.path.join(os.path.abspath(os.path.join(__file__, "../..")) + "/modelica/") - if os.path.exists( - os.path.join( - path_writtenback, "ets_cooling_indirect_templated.mo" - ) - ): - os.remove( - os.path.join( - path_writtenback, - "ets_cooling_indirect_templated.mo", - ) - ) - with open( - os.path.join( - path_writtenback, "ets_cooling_indirect_templated.mo" - ), - "w", - ) as f: + if os.path.exists(os.path.join(path_writtenback, "ets_cooling_indirect_templated.mo")): + os.remove(os.path.join(path_writtenback, "ets_cooling_indirect_templated.mo")) + with open(os.path.join(path_writtenback, "ets_cooling_indirect_templated.mo"), "w") as f: f.write(file_data) return file_data @@ -172,8 +131,7 @@ def templated_ets_openloops_dymola(self): to test our templated ets model. """ file = open( - os.path.join(os.getcwd(), "geojson_modelica_translator") + "/modelica/CoolingIndirectOpenLoops.mo", - "r", + os.path.join(os.getcwd(), "geojson_modelica_translator") + "/modelica/CoolingIndirectOpenLoops.mo", "r", ) cooling_indirect_filename = "/CoolingIndirectOpenLoops_Templated.mo" @@ -203,16 +161,13 @@ def templated_ets_openloops_dymola(self): to_str = "end CoolingIndirectOpenLoops_Templated;" repl_dict[from_str] = to_str - with open( - path_openloops + cooling_indirect_filename, "w" - ) as examplefile: + with open(path_openloops + cooling_indirect_filename, "w") as examplefile: for f in file: fx = f for from_str, to_str in repl_dict.items(): # TODO: f.string() causes errors, check code if fx.strip() == from_str.strip(): fx = f.replace(from_str, to_str) - examplefile.write(fx) return examplefile @@ -220,3 +175,5 @@ def templated_ets_openloops_dymola(self): def connect(self): """connect ETS-modelica to building-modelica (specifically TEASER modelica). This function will be modified in future""" + pass + diff --git a/geojson_modelica_translator/model_connectors/spawn.py b/geojson_modelica_translator/model_connectors/spawn.py index 5394fe92d..0a468514f 100644 --- a/geojson_modelica_translator/model_connectors/spawn.py +++ b/geojson_modelica_translator/model_connectors/spawn.py @@ -62,23 +62,13 @@ def add_building(self, urbanopt_building, mapper=None): if mapper is None: self.buildings.append( { - "area": urbanopt_building.feature.properties["floor_area"] - * 0.092936, # ft2 -> m2 + "area": urbanopt_building.feature.properties["floor_area"] * 0.092936, # ft2 -> m2 "building_id": urbanopt_building.feature.properties["id"], - "building_type": urbanopt_building.feature.properties[ - "building_type" - ], - "floor_height": urbanopt_building.feature.properties["height"] - * 0.3048, # ft -> m - "num_stories": urbanopt_building.feature.properties[ - "number_of_stories_above_ground" - ], - "num_stories_below_grade": urbanopt_building.feature.properties[ - "number_of_stories" - ] - - urbanopt_building.feature.properties[ - "number_of_stories_above_ground" - ], + "building_type": urbanopt_building.feature.properties["building_type"], + "floor_height": urbanopt_building.feature.properties["height"] * 0.3048, # ft -> m + "num_stories": urbanopt_building.feature.properties["number_of_stories_above_ground"], + "num_stories_below_grade": urbanopt_building.feature.properties["number_of_stories"] + - urbanopt_building.feature.properties["number_of_stories_above_ground"], "year_built": urbanopt_building.feature.properties["year_built"], } ) @@ -178,23 +168,15 @@ def to_modelica(self, project_name, root_building_dir): ) if os.path.exists(template_data["epw"]["epw_filename"]): - shutil.copy( - template_data["epw"]["epw_filename"], - os.path.join( - b_modelica_path.resources_dir, - template_data["epw"]["filename"], - ), - ) + shutil.copy(template_data["epw"]["epw_filename"], + os.path.join(b_modelica_path.resources_dir, template_data["epw"]["filename"])) else: raise Exception(f"Missing EPW file for Spawn: {template_data['epw']['epw_filename']}") if os.path.exists(template_data["mos_weather"]["mos_weather_filename"]): shutil.copy( template_data["mos_weather"]["mos_weather_filename"], - os.path.join( - b_modelica_path.resources_dir, - template_data["mos_weather"]["filename"], - ), + os.path.join(b_modelica_path.resources_dir, template_data["mos_weather"]["filename"]) ) else: raise Exception( @@ -205,12 +187,7 @@ def to_modelica(self, project_name, root_building_dir): model_name=f"B{building['building_id']}", data=template_data, ) - with open( - os.path.join( - os.path.join(b_modelica_path.files_dir, "building.mo") - ), - "w", - ) as f: + with open(os.path.join(os.path.join(b_modelica_path.files_dir, "building.mo")), "w") as f: f.write(file_data) file_data = spawn_mos_template.render( @@ -218,14 +195,7 @@ def to_modelica(self, project_name, root_building_dir): model_name=f"B{building['building_id']}", data=template_data, ) - with open( - os.path.join( - os.path.join( - b_modelica_path.resources_dir, "RunSpawnBuilding.mos" - ) - ), - "w", - ) as f: + with open(os.path.join(os.path.join(b_modelica_path.resources_dir, "RunSpawnBuilding.mos")), "w") as f: f.write(file_data) finally: os.chdir(curdir) diff --git a/geojson_modelica_translator/model_connectors/teaser.py b/geojson_modelica_translator/model_connectors/teaser.py index 1e95c5e48..1a1775b2f 100644 --- a/geojson_modelica_translator/model_connectors/teaser.py +++ b/geojson_modelica_translator/model_connectors/teaser.py @@ -55,23 +55,13 @@ def add_building(self, urbanopt_building, mapper=None): if mapper is None: self.buildings.append( { - "area": urbanopt_building.feature.properties["floor_area"] - * 0.092936, # ft2 -> m2 + "area": urbanopt_building.feature.properties["floor_area"] * 0.092936, # ft2 -> m2 "building_id": urbanopt_building.feature.properties["id"], - "building_type": urbanopt_building.feature.properties[ - "building_type" - ], - "floor_height": urbanopt_building.feature.properties["height"] - * 0.3048, # ft -> m - "num_stories": urbanopt_building.feature.properties[ - "number_of_stories_above_ground" - ], - "num_stories_below_grade": urbanopt_building.feature.properties[ - "number_of_stories" - ] - - urbanopt_building.feature.properties[ - "number_of_stories_above_ground" - ], + "building_type": urbanopt_building.feature.properties["building_type"], + "floor_height": urbanopt_building.feature.properties["height"] * 0.3048, # ft -> m + "num_stories": urbanopt_building.feature.properties["number_of_stories_above_ground"], + "num_stories_below_grade": urbanopt_building.feature.properties["number_of_stories"] + - urbanopt_building.feature.properties["number_of_stories_above_ground"], "year_built": urbanopt_building.feature.properties["year_built"], } ) @@ -137,11 +127,11 @@ def to_modelica(self, project_name, root_building_dir, keep_original_models=Fals ) def post_process( - self, - project_name, - root_building_dir, - building_names, - keep_original_models=False, + self, + project_name, + root_building_dir, + building_names, + keep_original_models=False, ): """ Cleanup the export of the TEASER files into a format suitable for the district-based analysis. This includes @@ -191,7 +181,6 @@ def post_process( # process each of the building models mo_files = glob.glob(os.path.join(root_building_dir, f"B{b}/*.mo")) for f in mo_files: - # ignore the package.mo file if os.path.basename(f) == "package.mo": continue @@ -207,21 +196,14 @@ def post_process( # updating path to internal loads for s in string_replace_list: - mofile.replace_model_string( - "Modelica.Blocks.Sources.CombiTimeTable", - "internalGains", - s[0], - s[1], - ) + mofile.replace_model_string("Modelica.Blocks.Sources.CombiTimeTable", "internalGains", s[0], s[1]) # add heat port data = [ "annotation (Placement(transformation(extent={{-10,90},{10,110}}), iconTransformation(extent={{-10,90},{10,110}})));" # noqa ] mofile.add_model_object( - "Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a", - "port_a", - data, + "Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a", "port_a", data, ) # add TAir output @@ -241,42 +223,24 @@ def post_process( mofile.remove_connect_string('weaBus', 'weaBus') # add new port connections - if ( - self.system_parameters.get_param( - "buildings.default.load_model_parameters.rc.order", default=2 - ) - == 1 - ): # noqa + if self.system_parameters.get_param( + "buildings.default.load_model_parameters.rc.order", default=2) == 1: data = "annotation (Line(points={{0,100},{96,100},{96,20},{92,20}}, color={191,0,0}))" - mofile.add_connect( - "port_a", "thermalZoneOneElement.intGainsConv", data - ) + mofile.add_connect("port_a", "thermalZoneOneElement.intGainsConv", data) data = "annotation (Line(points={{93,32},{98,32},{98,0},{110,0}}, color={0,0,127}))" mofile.add_connect("thermalZoneOneElement.TAir", "TAir", data) - elif ( - self.system_parameters.get_param( - "buildings.default.load_model_parameters.rc.order", default=2 - ) - == 2 - ): # noqa + elif self.system_parameters.get_param( + "buildings.default.load_model_parameters.rc.order", default=2) == 2: data = "annotation (Line(points={{0,100},{96,100},{96,20},{92,20}}, color={191,0,0}))" - mofile.add_connect( - "port_a", "thermalZoneTwoElements.intGainsConv", data - ) + mofile.add_connect("port_a", "thermalZoneTwoElements.intGainsConv", data) data = "annotation (Line(points={{93,32},{98,32},{98,0},{110,0}}, color={0,0,127}))" mofile.add_connect("thermalZoneTwoElements.TAir", "TAir", data) - elif ( - self.system_parameters.get_param( - "buildings.default.load_model_parameters.rc.order", default=2 - ) - == 4 - ): # noqa + elif self.system_parameters.get_param( + "buildings.default.load_model_parameters.rc.order", default=2) == 4: data = "annotation (Line(points={{0,100},{96,100},{96,20},{92,20}}, color={191,0,0}))" - mofile.add_connect( - "port_a", "thermalZoneFourElements.intGainsConv", data - ) + mofile.add_connect("port_a", "thermalZoneFourElements.intGainsConv", data) data = "annotation (Line(points={{93,32},{98,32},{98,0},{110,0}}, color={0,0,127}))" mofile.add_connect("thermalZoneFourElements.TAir", "TAir", data) @@ -307,9 +271,6 @@ def post_process( # files in the Loads directory. # add in the silly 'B' before the building names package = PackageParser.new_from_template( - root_building_dir, - "Loads", - ["B" + b for b in building_names], - within=f"{project_name}", + root_building_dir, "Loads", ["B" + b for b in building_names], within=f"{project_name}", ) package.save() diff --git a/geojson_modelica_translator/system_parameters/system_parameters.py b/geojson_modelica_translator/system_parameters/system_parameters.py index fde135a18..e04d76865 100644 --- a/geojson_modelica_translator/system_parameters/system_parameters.py +++ b/geojson_modelica_translator/system_parameters/system_parameters.py @@ -46,9 +46,7 @@ def __init__(self, filename=None): :param filename: string, (optional) path to file to load """ # load the schema for validation - self.schema = json.load( - open(os.path.join(os.path.dirname(__file__), "schema.json"), "r") - ) + self.schema = json.load(open(os.path.join(os.path.dirname(__file__), "schema.json"), "r")) self.data = {} if filename: diff --git a/geojson_modelica_translator/utils.py b/geojson_modelica_translator/utils.py index f256eba39..e1984ef05 100644 --- a/geojson_modelica_translator/utils.py +++ b/geojson_modelica_translator/utils.py @@ -73,9 +73,7 @@ def __init__(self, name, root_dir, overwrite=False): def clear_path(self, path, overwrite=False): if os.path.exists(path): if overwrite: - raise Exception( - "Directory already exists and overwrite is false for %s" % path - ) + raise Exception("Directory already exists and overwrite is false for %s" % path) else: shutil.rmtree(path) os.makedirs(path, exist_ok=True) diff --git a/management/update_licenses.py b/management/update_licenses.py index 5be04e597..7e3a082d0 100644 --- a/management/update_licenses.py +++ b/management/update_licenses.py @@ -34,9 +34,7 @@ import os import re -PYTHON_REGEX = re.compile( - r'^""".\*{100}.*:copyright.*\*{100}."""$', re.MULTILINE | re.DOTALL -) +PYTHON_REGEX = re.compile(r'^""".\*{100}.*:copyright.*\*{100}."""$', re.MULTILINE | re.DOTALL) PYTHON_LICENSE = '''""" **************************************************************************************************** :copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. @@ -69,11 +67,7 @@ EXCLUDE_FILES = ["__init__.py"] PATHS = [ - { - "glob": "geojson_modelica_translator/**/*.py", - "license": PYTHON_LICENSE, - "REGEX": PYTHON_REGEX, - }, + {"glob": "geojson_modelica_translator/**/*.py", "license": PYTHON_LICENSE, "REGEX": PYTHON_REGEX, }, {"glob": "management/**/*.py", "license": PYTHON_LICENSE, "REGEX": PYTHON_REGEX}, {"glob": "tests/**/*.py", "license": PYTHON_LICENSE, "REGEX": PYTHON_REGEX}, # single files diff --git a/tests/model_connectors/test_ets.py b/tests/model_connectors/test_ets.py index 771a2ac8c..a3a05f2b4 100644 --- a/tests/model_connectors/test_ets.py +++ b/tests/model_connectors/test_ets.py @@ -1,10 +1,7 @@ import os import unittest -from geojson_modelica_translator.model_connectors.ets_template import \ - ETSTemplate - -# TODO: do not mix upper camel case and snake case. Spell out ETS or use ETSModelConnector... +from geojson_modelica_translator.model_connectors.ets_template import ETSTemplate class ETSModelConnectorSingleBuildingTest(unittest.TestCase): From ace9c518e313467b320635c29ee501867284b986 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Tue, 17 Mar 2020 21:03:16 -0600 Subject: [PATCH 40/62] pre-commit cleanup --- .pre-commit-config.yaml | 7 ++- geojson_modelica_translator/cli.py | 1 - .../geojson/urbanopt_geojson.py | 2 +- .../geojson_modelica_translator.py | 4 +- .../model_connectors/ets_template.py | 31 ++++++++++++- .../model_connectors/spawn.py | 4 +- .../model_connectors/teaser.py | 12 ++--- .../modelica/input_parser.py | 2 +- .../system_parameters/schema.json | 2 +- setup.py | 5 ++- tests/context.py | 4 +- tests/geojson/test_geojson.py | 5 ++- tests/geojson/test_schemas.py | 1 - tests/model_connectors/data/jmodelica.py | 3 +- tests/model_connectors/test_ets.py | 34 +++++++++++++- tests/model_connectors/test_spawn.py | 5 +-- tests/modelica/test_input_parser.py | 5 +-- .../ets_cooling_indirect_templated.mo | 44 +++++++++---------- .../test_system_parameters.py | 2 +- tests/test_translator.py | 5 +-- tests/test_utils.py | 1 - 21 files changed, 120 insertions(+), 59 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d86cd4916..8c111ba72 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,14 +17,13 @@ repos: - id: check-yaml - id: debug-statements - id: end-of-file-fixer - - id: requirements-txt-fixer +# - id: requirements-txt-fixer - id: mixed-line-ending - args: ['--fix=no'] - id: flake8 - args: ['--max-line-length=210'] # default of Black + args: ['--max-line-length=140'] # default of Black - repo: https://github.com/pre-commit/mirrors-isort rev: v4.3.4 hooks: - id: isort - + args: ['-m 3'] # vertical hanging diff --git a/geojson_modelica_translator/cli.py b/geojson_modelica_translator/cli.py index b463cb638..f363b05dd 100644 --- a/geojson_modelica_translator/cli.py +++ b/geojson_modelica_translator/cli.py @@ -32,7 +32,6 @@ import logging import sys - # Work in progress diff --git a/geojson_modelica_translator/geojson/urbanopt_geojson.py b/geojson_modelica_translator/geojson/urbanopt_geojson.py index e2bb6c69d..446697c10 100644 --- a/geojson_modelica_translator/geojson/urbanopt_geojson.py +++ b/geojson_modelica_translator/geojson/urbanopt_geojson.py @@ -28,11 +28,11 @@ **************************************************************************************************** """ -import geojson import logging import os from collections import defaultdict +import geojson from geojson_modelica_translator.geojson.schemas import Schemas _log = logging.getLogger(__name__) diff --git a/geojson_modelica_translator/geojson_modelica_translator.py b/geojson_modelica_translator/geojson_modelica_translator.py index e2ef35477..6d66705a3 100644 --- a/geojson_modelica_translator/geojson_modelica_translator.py +++ b/geojson_modelica_translator/geojson_modelica_translator.py @@ -32,7 +32,9 @@ import os import shutil -from geojson_modelica_translator.geojson.urbanopt_geojson import UrbanOptGeoJson +from geojson_modelica_translator.geojson.urbanopt_geojson import ( + UrbanOptGeoJson +) from geojson_modelica_translator.modelica.input_parser import PackageParser from geojson_modelica_translator.utils import ModelicaPath diff --git a/geojson_modelica_translator/model_connectors/ets_template.py b/geojson_modelica_translator/model_connectors/ets_template.py index 62d68eef0..d09c2871e 100644 --- a/geojson_modelica_translator/model_connectors/ets_template.py +++ b/geojson_modelica_translator/model_connectors/ets_template.py @@ -1,3 +1,33 @@ +""" +**************************************************************************************************** +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted +provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions +and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this list of conditions +and the following disclaimer in the documentation and/or other materials provided with the +distribution. + +Neither the name of the copyright holder nor the names of its contributors may be used to endorse +or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**************************************************************************************************** +""" + import json import os @@ -176,4 +206,3 @@ def connect(self): """connect ETS-modelica to building-modelica (specifically TEASER modelica). This function will be modified in future""" pass - diff --git a/geojson_modelica_translator/model_connectors/spawn.py b/geojson_modelica_translator/model_connectors/spawn.py index 0a468514f..8c065ad5f 100644 --- a/geojson_modelica_translator/model_connectors/spawn.py +++ b/geojson_modelica_translator/model_connectors/spawn.py @@ -67,8 +67,8 @@ def add_building(self, urbanopt_building, mapper=None): "building_type": urbanopt_building.feature.properties["building_type"], "floor_height": urbanopt_building.feature.properties["height"] * 0.3048, # ft -> m "num_stories": urbanopt_building.feature.properties["number_of_stories_above_ground"], - "num_stories_below_grade": urbanopt_building.feature.properties["number_of_stories"] - - urbanopt_building.feature.properties["number_of_stories_above_ground"], + "num_stories_below_grade": urbanopt_building.feature.properties["number_of_stories"] - + urbanopt_building.feature.properties["number_of_stories_above_ground"], "year_built": urbanopt_building.feature.properties["year_built"], } ) diff --git a/geojson_modelica_translator/model_connectors/teaser.py b/geojson_modelica_translator/model_connectors/teaser.py index 1a1775b2f..0f5a5542e 100644 --- a/geojson_modelica_translator/model_connectors/teaser.py +++ b/geojson_modelica_translator/model_connectors/teaser.py @@ -31,13 +31,15 @@ import glob import os import shutil -from teaser.project import Project -from geojson_modelica_translator.model_connectors.base import ( - Base as model_connector_base, +from geojson_modelica_translator.model_connectors.base import \ + Base as model_connector_base +from geojson_modelica_translator.modelica.input_parser import ( + InputParser, + PackageParser ) -from geojson_modelica_translator.modelica.input_parser import InputParser, PackageParser from geojson_modelica_translator.utils import ModelicaPath, copytree +from teaser.project import Project class TeaserConnector(model_connector_base): @@ -61,7 +63,7 @@ def add_building(self, urbanopt_building, mapper=None): "floor_height": urbanopt_building.feature.properties["height"] * 0.3048, # ft -> m "num_stories": urbanopt_building.feature.properties["number_of_stories_above_ground"], "num_stories_below_grade": urbanopt_building.feature.properties["number_of_stories"] - - urbanopt_building.feature.properties["number_of_stories_above_ground"], + - urbanopt_building.feature.properties["number_of_stories_above_ground"], "year_built": urbanopt_building.feature.properties["year_built"], } ) diff --git a/geojson_modelica_translator/modelica/input_parser.py b/geojson_modelica_translator/modelica/input_parser.py index ac7b8fbf4..7df9aedb7 100644 --- a/geojson_modelica_translator/modelica/input_parser.py +++ b/geojson_modelica_translator/modelica/input_parser.py @@ -30,7 +30,7 @@ import os -from jinja2 import FileSystemLoader, Environment +from jinja2 import Environment, FileSystemLoader class PackageParser(object): diff --git a/geojson_modelica_translator/system_parameters/schema.json b/geojson_modelica_translator/system_parameters/schema.json index 8e9408ca8..1fd8d29f3 100755 --- a/geojson_modelica_translator/system_parameters/schema.json +++ b/geojson_modelica_translator/system_parameters/schema.json @@ -301,4 +301,4 @@ } } } -} +} diff --git a/setup.py b/setup.py index 2121d15ad..55fffab97 100755 --- a/setup.py +++ b/setup.py @@ -33,12 +33,13 @@ import os import shutil from io import BytesIO -from requests import get -from setuptools import setup, find_packages from zipfile import ZipFile +from setuptools import find_packages, setup + from management.update_licenses import UpdateLicenses from management.update_schemas import UpdateSchemas +from requests import get with open("README.rst") as f: readme = f.read() diff --git a/tests/context.py b/tests/context.py index d51c569bf..be3b48f91 100644 --- a/tests/context.py +++ b/tests/context.py @@ -34,6 +34,6 @@ import os import sys -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) - import geojson_modelica_translator # noqa - Do not remove this line. + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) diff --git a/tests/geojson/test_geojson.py b/tests/geojson/test_geojson.py index 354d57f69..31677680e 100644 --- a/tests/geojson/test_geojson.py +++ b/tests/geojson/test_geojson.py @@ -31,8 +31,9 @@ import os import unittest -from geojson_modelica_translator.geojson.urbanopt_geojson import UrbanOptGeoJson -from ..context import geojson_modelica_translator # noqa - Do not remove this line +from geojson_modelica_translator.geojson.urbanopt_geojson import ( + UrbanOptGeoJson +) class GeoJSONTest(unittest.TestCase): diff --git a/tests/geojson/test_schemas.py b/tests/geojson/test_schemas.py index 5564c22da..5266fc6ab 100644 --- a/tests/geojson/test_schemas.py +++ b/tests/geojson/test_schemas.py @@ -31,7 +31,6 @@ import unittest from geojson_modelica_translator.geojson.schemas import Schemas -from ..context import geojson_modelica_translator # noqa - Do not remove this line class SchemasTest(unittest.TestCase): diff --git a/tests/model_connectors/data/jmodelica.py b/tests/model_connectors/data/jmodelica.py index e92f0e255..7066c43bb 100644 --- a/tests/model_connectors/data/jmodelica.py +++ b/tests/model_connectors/data/jmodelica.py @@ -5,9 +5,10 @@ # Import the function for compilation of models and the load_fmu method import os -import pymodelica import shutil import sys + +import pymodelica from pyfmi import load_fmu from pymodelica import compile_fmu diff --git a/tests/model_connectors/test_ets.py b/tests/model_connectors/test_ets.py index a3a05f2b4..a7f9d54fa 100644 --- a/tests/model_connectors/test_ets.py +++ b/tests/model_connectors/test_ets.py @@ -1,7 +1,39 @@ +""" +**************************************************************************************************** +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted +provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions +and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this list of conditions +and the following disclaimer in the documentation and/or other materials provided with the +distribution. + +Neither the name of the copyright holder nor the names of its contributors may be used to endorse +or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**************************************************************************************************** +""" + import os import unittest -from geojson_modelica_translator.model_connectors.ets_template import ETSTemplate +from geojson_modelica_translator.model_connectors.ets_template import ( + ETSTemplate +) class ETSModelConnectorSingleBuildingTest(unittest.TestCase): diff --git a/tests/model_connectors/test_spawn.py b/tests/model_connectors/test_spawn.py index b7e725a8a..d19557170 100644 --- a/tests/model_connectors/test_spawn.py +++ b/tests/model_connectors/test_spawn.py @@ -32,13 +32,12 @@ import unittest from geojson_modelica_translator.geojson_modelica_translator import ( - GeoJsonModelicaTranslator, + GeoJsonModelicaTranslator ) from geojson_modelica_translator.model_connectors.spawn import SpawnConnector from geojson_modelica_translator.system_parameters.system_parameters import ( - SystemParameters, + SystemParameters ) -from ..context import geojson_modelica_translator # noqa - Do not remove this line class SpawnModelConnectorSingleBuildingTest(unittest.TestCase): diff --git a/tests/modelica/test_input_parser.py b/tests/modelica/test_input_parser.py index d7f3aa251..c9fd1989c 100644 --- a/tests/modelica/test_input_parser.py +++ b/tests/modelica/test_input_parser.py @@ -33,7 +33,6 @@ import unittest from geojson_modelica_translator.modelica.input_parser import InputParser -from ..context import geojson_modelica_translator # noqa - Do not remove this line class InputParserTest(unittest.TestCase): @@ -99,7 +98,7 @@ def test_rename_filename(self): f1.replace_model_string( 'Modelica.Blocks.Sources.CombiTimeTable', 'internalGains', - 'modelica://Project/B5a6b99ec37f4de7f94020090/B5a6b99ec37f4de7f94020090_Models/InternalGains_B5a6b99ec37f4de7f94020090Floor.mat', # noqa + 'modelica://Project/B5a6b99ec37f4de7f94020090/B5a6b99ec37f4de7f94020090_Models/InternalGains_B5a6b99ec37f4de7f94020090Floor.mat', # noqa # noqa 'modelica://a/new/path.mat' ) @@ -118,7 +117,7 @@ def test_add_model_obj(self): new_filename = os.path.abspath("tests/modelica/output/test_1_output_5.mo") f1 = InputParser(filename) data = [ - 'annotation (Placement(transformation(extent={{-10,90},{10,110}}), iconTransformation(extent={{-10,90},{10,110}})));' # noqa + 'annotation (Placement(transformation(extent={{-10,90},{10,110}}), iconTransformation(extent={{-10,90},{10,110}})));' # noqa # noqa ] f1.add_model_object( diff --git a/tests/output_ets/ets_cooling_indirect_templated.mo b/tests/output_ets/ets_cooling_indirect_templated.mo index e221aa185..a0fc03a3f 100644 --- a/tests/output_ets/ets_cooling_indirect_templated.mo +++ b/tests/output_ets/ets_cooling_indirect_templated.mo @@ -13,30 +13,30 @@ model ets_cooling_indirect_templated Modelica.Media.Interfaces.PartialMedium "Medium in the component"; - + // mass flow rates parameter Modelica.SIunits.MassFlowRate m1_flow_nominal( final min=0, start=0.666) "Nominal mass flow rate of primary (district) district cooling side"; - - + + parameter Modelica.SIunits.MassFlowRate m2_flow_nominal( final min=0, start=0.5) "Nominal mass flow rate of secondary (building) district cooling side"; - - + + // Primary supply control valve parameter Modelica.SIunits.PressureDifference dpValve_nominal( final min=0, final displayUnit="Pa")=888 "Nominal pressure drop of fully open control valve"; - - + + // Heat exchanger parameter Modelica.SIunits.PressureDifference dp1_nominal( final min=0, @@ -44,33 +44,33 @@ model ets_cooling_indirect_templated final displayUnit="Pa") "Nominal pressure difference on primary side" annotation(Dialog(group="Heat exchanger")); - - + + parameter Modelica.SIunits.PressureDifference dp2_nominal( final min=0, start=999, final displayUnit="Pa") "Nominal pressure difference on secondary side" annotation(Dialog(group="Heat exchanger")); - - + + parameter Boolean use_Q_flow_nominal=true "Set to true to specify Q_flow_nominal and temperatures, or to false to specify effectiveness" annotation(Dialog(group="Heat exchanger")); - - + + parameter Modelica.SIunits.HeatFlowRate Q_flow_nominal( final min=0, start=8000) "Nominal heat transfer" annotation(Dialog(group="Heat exchanger")); - - + + parameter Modelica.SIunits.Temperature T_a1_nominal( min=0+273, max=100+273.15, @@ -78,9 +78,9 @@ model ets_cooling_indirect_templated final displayUnit="K") "Nominal temperature at port a1" annotation(Dialog(group="Heat exchanger")); - - + + parameter Modelica.SIunits.Temperature T_a2_nominal( min=0+273, max=100+273.15, @@ -88,17 +88,17 @@ model ets_cooling_indirect_templated final displayUnit="K") "Nominal temperature at port a2" annotation(Dialog(group="Heat exchanger")); - - + + parameter Modelica.SIunits.Efficiency eta( final min=0, final max=1)=0.666 "Constant effectiveness" annotation(Dialog(group="Heat exchanger")); - - + + // Controller parameters parameter Modelica.Blocks.Types.SimpleController controllerType= Modelica.Blocks.Types.SimpleController.PI @@ -366,4 +366,4 @@ First implementation.
    • 12/15/2020, Yanfei Li, Templating ETS model.
    ")); -end ets_cooling_indirect_templated; \ No newline at end of file +end ets_cooling_indirect_templated; diff --git a/tests/system_parameters/test_system_parameters.py b/tests/system_parameters/test_system_parameters.py index f2ce58af0..047798ba4 100644 --- a/tests/system_parameters/test_system_parameters.py +++ b/tests/system_parameters/test_system_parameters.py @@ -32,7 +32,7 @@ import unittest from geojson_modelica_translator.system_parameters.system_parameters import ( - SystemParameters, + SystemParameters ) diff --git a/tests/test_translator.py b/tests/test_translator.py index cd60edc6f..112423624 100644 --- a/tests/test_translator.py +++ b/tests/test_translator.py @@ -34,12 +34,11 @@ import unittest from geojson_modelica_translator.geojson_modelica_translator import ( - GeoJsonModelicaTranslator, + GeoJsonModelicaTranslator ) from geojson_modelica_translator.system_parameters.system_parameters import ( - SystemParameters, + SystemParameters ) -from .context import geojson_modelica_translator # noqa - Do not remove this line class GeoJSONTranslatorTest(unittest.TestCase): diff --git a/tests/test_utils.py b/tests/test_utils.py index e61ecec1c..c88f2e9de 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -32,7 +32,6 @@ import unittest from geojson_modelica_translator.utils import ModelicaPath -from .context import geojson_modelica_translator # noqa - Do not remove this line class ModelicaPathTest(unittest.TestCase): From ea179d9a8469879a8a2db41a628051034da61688 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Tue, 17 Mar 2020 21:22:14 -0600 Subject: [PATCH 41/62] do not recurse modelica-buildings --- setup.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.cfg b/setup.cfg index 7d5927bb7..1937e4b34 100644 --- a/setup.cfg +++ b/setup.cfg @@ -8,6 +8,7 @@ addopts = norecursedirs = dist build + modelica-buildings .tox src testpaths = tests @@ -20,6 +21,7 @@ exclude = .tox .eggs build + modelica-buildings dist docs/conf.py From 2fa13ea5e50eb1f462c44dfa08933edf5e544e39 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Tue, 17 Mar 2020 21:28:07 -0600 Subject: [PATCH 42/62] do not run models as they need to be updated --- .../ets_cooling_indirect_templated.mo | 24 +++++++++++++++++++ tests/model_connectors/test_spawn.py | 22 ++++++++--------- tests/test_translator.py | 11 +++++++++ 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/geojson_modelica_translator/modelica/ets_cooling_indirect_templated.mo b/geojson_modelica_translator/modelica/ets_cooling_indirect_templated.mo index afffe8cae..a0fc03a3f 100644 --- a/geojson_modelica_translator/modelica/ets_cooling_indirect_templated.mo +++ b/geojson_modelica_translator/modelica/ets_cooling_indirect_templated.mo @@ -3,6 +3,7 @@ within Buildings.Applications.DHC.EnergyTransferStations; model ets_cooling_indirect_templated + "Indirect cooling energy transfer station for district energy systems" extends Buildings.Fluid.Interfaces.PartialFourPort( redeclare final package Medium1 = Medium, @@ -11,23 +12,31 @@ model ets_cooling_indirect_templated replaceable package Medium = Modelica.Media.Interfaces.PartialMedium "Medium in the component"; + + // mass flow rates parameter Modelica.SIunits.MassFlowRate m1_flow_nominal( final min=0, start=0.666) "Nominal mass flow rate of primary (district) district cooling side"; + + parameter Modelica.SIunits.MassFlowRate m2_flow_nominal( final min=0, start=0.5) "Nominal mass flow rate of secondary (building) district cooling side"; + + // Primary supply control valve parameter Modelica.SIunits.PressureDifference dpValve_nominal( final min=0, final displayUnit="Pa")=888 "Nominal pressure drop of fully open control valve"; + + // Heat exchanger parameter Modelica.SIunits.PressureDifference dp1_nominal( final min=0, @@ -36,6 +45,8 @@ model ets_cooling_indirect_templated "Nominal pressure difference on primary side" annotation(Dialog(group="Heat exchanger")); + + parameter Modelica.SIunits.PressureDifference dp2_nominal( final min=0, start=999, @@ -43,16 +54,23 @@ model ets_cooling_indirect_templated "Nominal pressure difference on secondary side" annotation(Dialog(group="Heat exchanger")); + + parameter Boolean use_Q_flow_nominal=true "Set to true to specify Q_flow_nominal and temperatures, or to false to specify effectiveness" annotation(Dialog(group="Heat exchanger")); + + parameter Modelica.SIunits.HeatFlowRate Q_flow_nominal( final min=0, start=8000) "Nominal heat transfer" annotation(Dialog(group="Heat exchanger")); + + + parameter Modelica.SIunits.Temperature T_a1_nominal( min=0+273, max=100+273.15, @@ -61,6 +79,8 @@ model ets_cooling_indirect_templated "Nominal temperature at port a1" annotation(Dialog(group="Heat exchanger")); + + parameter Modelica.SIunits.Temperature T_a2_nominal( min=0+273, max=100+273.15, @@ -69,12 +89,16 @@ model ets_cooling_indirect_templated "Nominal temperature at port a2" annotation(Dialog(group="Heat exchanger")); + + parameter Modelica.SIunits.Efficiency eta( final min=0, final max=1)=0.666 "Constant effectiveness" annotation(Dialog(group="Heat exchanger")); + + // Controller parameters parameter Modelica.Blocks.Types.SimpleController controllerType= Modelica.Blocks.Types.SimpleController.PI diff --git a/tests/model_connectors/test_spawn.py b/tests/model_connectors/test_spawn.py index e3ac4c02a..a5068bfc2 100644 --- a/tests/model_connectors/test_spawn.py +++ b/tests/model_connectors/test_spawn.py @@ -35,7 +35,7 @@ GeoJsonModelicaTranslator ) from geojson_modelica_translator.model_connectors.spawn import SpawnConnector -from geojson_modelica_translator.modelica.modelica_runner import ModelicaRunner +# from geojson_modelica_translator.modelica.modelica_runner import ModelicaRunner from geojson_modelica_translator.system_parameters.system_parameters import ( SystemParameters ) @@ -98,13 +98,13 @@ def setUp(self): for b in gj.buildings: self.spawn.add_building(b) - def test_spawn_to_modelica_and_run(self): - self.spawn.to_modelica('spawn_two_building', 'tests/model_connectors/output') - - # make sure the model can run using the ModelicaRunner class - mr = ModelicaRunner() - file_to_run = os.path.abspath( - 'tests/model_connectors/output/spawn_two_building/Loads/B5a6b99ec37f4de7f94020090/building.mo' - ) - exitcode = mr.run_in_docker(file_to_run) - self.assertEqual(0, exitcode) + # def test_spawn_to_modelica_and_run(self): + # self.spawn.to_modelica('spawn_two_building', 'tests/model_connectors/output') + # + # # make sure the model can run using the ModelicaRunner class + # mr = ModelicaRunner() + # file_to_run = os.path.abspath( + # 'tests/model_connectors/output/spawn_two_building/Loads/B5a6b99ec37f4de7f94020090/building.mo' + # ) + # exitcode = mr.run_in_docker(file_to_run) + # self.assertEqual(0, exitcode) diff --git a/tests/test_translator.py b/tests/test_translator.py index 112423624..84af6af3a 100644 --- a/tests/test_translator.py +++ b/tests/test_translator.py @@ -33,6 +33,9 @@ import shutil import unittest +# from geojson_modelica_translator.modelica.modelica_runner import ( +# ModelicaRunner +# ) from geojson_modelica_translator.geojson_modelica_translator import ( GeoJsonModelicaTranslator ) @@ -167,6 +170,14 @@ def test_to_modelica_rc_order_4(self): ) self.assertTrue(os.path.exists(path), f"Path not found: {path}") + # # make sure the model can run using the ModelicaRunner class + # mr = ModelicaRunner() + # file_to_run = os.path.abspath( + # f'{self.results_path}/Loads/B5a6b99ec37f4de7f94020090/Office.mo' + # ) + # exitcode = mr.run_in_docker(file_to_run) + # self.assertEqual(0, exitcode) + if __name__ == "__main__": unittest.main() From e7afaba6bb8740c49b7c490a22236f04877a16b5 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Mon, 23 Mar 2020 20:26:13 -0600 Subject: [PATCH 43/62] add in new spawn templates --- .gitignore | 1 + .../model_connectors/spawn.py | 29 +- .../templates/RunSpawnBuilding.mot | 135 ++++++- .../templates/spawn_building.mot | 217 ++++++++++ .../templates/spawn_coupling.mot | 135 +++++++ tests/model_connectors/test_spawn.py | 22 +- .../ets_cooling_indirect_templated.mo | 369 ------------------ 7 files changed, 510 insertions(+), 398 deletions(-) create mode 100644 geojson_modelica_translator/model_connectors/templates/spawn_building.mot create mode 100644 geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot delete mode 100644 tests/output_ets/ets_cooling_indirect_templated.mo diff --git a/.gitignore b/.gitignore index 03956b627..f1540efb4 100644 --- a/.gitignore +++ b/.gitignore @@ -94,6 +94,7 @@ ENV/ # Test outputs tests/output +tests/output_ets tests/modelica/output tests/model_connectors/output /geojson_modelica_translator/modelica/buildingslibrary/ diff --git a/geojson_modelica_translator/model_connectors/spawn.py b/geojson_modelica_translator/model_connectors/spawn.py index 8c065ad5f..e01f9f050 100644 --- a/geojson_modelica_translator/model_connectors/spawn.py +++ b/geojson_modelica_translator/model_connectors/spawn.py @@ -73,19 +73,6 @@ def add_building(self, urbanopt_building, mapper=None): } ) - def lookup_building_type(self, building_type): - """ - This needs to be updated to decide which building to reference if no IDF is included. - - :param building_type: - :return: - """ - if "office" in building_type.lower(): - return "office" - else: - # TODO: define these mappings 'office', 'institute', 'institute4', institute8' - return "office" - def to_modelica(self, project_name, root_building_dir): """ Create spawn models based on the data in the buildings and geojsons @@ -95,7 +82,8 @@ def to_modelica(self, project_name, root_building_dir): """ curdir = os.getcwd() loads_root_path = os.path.join(root_building_dir, project_name, "Loads") - spawn_fmu_template = self.template_env.get_template("spawn_fmu.mot") + spawn_coupling_template = self.template_env.get_template("spawn_coupling.mot") + spawn_building_template = self.template_env.get_template("spawn_building.mot") spawn_mos_template = self.template_env.get_template("RunSpawnBuilding.mot") building_names = [] try: @@ -182,7 +170,7 @@ def to_modelica(self, project_name, root_building_dir): raise Exception( f"Missing MOS weather file for Spawn: {template_data['mos_weather']['mos_weather_filename']}") - file_data = spawn_fmu_template.render( + file_data = spawn_building_template.render( project_name=project_name, model_name=f"B{building['building_id']}", data=template_data, @@ -197,6 +185,15 @@ def to_modelica(self, project_name, root_building_dir): ) with open(os.path.join(os.path.join(b_modelica_path.resources_dir, "RunSpawnBuilding.mos")), "w") as f: f.write(file_data) + + file_data = spawn_coupling_template.render( + project_name=project_name, + model_name=f"B{building['building_id']}", + data=template_data, + ) + with open(os.path.join(os.path.join(b_modelica_path.files_dir, "coupling.mo")), "w") as f: + f.write(file_data) + finally: os.chdir(curdir) @@ -220,7 +217,7 @@ def post_process(self, project_name, root_building_dir, building_names): for b in building_names: b_modelica_path = os.path.join(loads_root_path, b) new_package = PackageParser.new_from_template( - b_modelica_path, b, ["building"], within=f"{project_name}.Loads" + b_modelica_path, b, ["coupling", "building"], within=f"{project_name}.Loads" ) new_package.save() diff --git a/geojson_modelica_translator/model_connectors/templates/RunSpawnBuilding.mot b/geojson_modelica_translator/model_connectors/templates/RunSpawnBuilding.mot index 4b30f98d0..d10d679f2 100644 --- a/geojson_modelica_translator/model_connectors/templates/RunSpawnBuilding.mot +++ b/geojson_modelica_translator/model_connectors/templates/RunSpawnBuilding.mot @@ -1,2 +1,133 @@ -simulateModel("{{project_name}}.Loads.{{model_name}}",stopTime=604800, method="Cvode", tolerance=1e-06, resultFile="{{model_name}}"); -createPlot(id=1, position={55, 50, 1783, 995}, y={% raw %}{{% endraw %}{% for zone in data['thermal_zones']%}"{{zone['modelica_object_name']}}.TAir", {% endfor %} {% raw %} "weaDat.weaBus.TDryBul", "datRea.y[1]", "datRea.y[2]", "datRea.y[3]", "datRea.y[4]", "datRea.y[5]", "datRea.y[6]", "datRea.y[7]"}, range={0.0, 620000.0, -30.0, 25.0}, grid=true, colors={{28,108,200}, {238,46,47}, {0,140,72}, {217,67,180}, {0,0,0}, {162,29,33}, {244,125,35}, {102,44,145}, {28,108,200}, {238,46,47}, {0,140,72}, {217,67,180}, {0,0,0}, {162,29,33}}, patterns={LinePattern.Solid, LinePattern.Solid, LinePattern.Solid, LinePattern.Solid, LinePattern.Solid, LinePattern.Solid, LinePattern.Solid, LinePattern.Solid, LinePattern.Dash, LinePattern.Dash, LinePattern.Dash, LinePattern.Dash, LinePattern.Dash, LinePattern.Dash});{% endraw %} +old_hidden_avoid_double_computation=Hidden.AvoidDoubleComputation; +Hidden.AvoidDoubleComputation=true; +simulateModel("spawn_two_building.Loads.B5a6b99ec37f4de7f94020090.Examples.CouplingSpawnZ6", + method="cvode", + tolerance=1e-6, + numberOfIntervals=500, + stopTime=604800.0, + resultFile="CouplingSpawnZ6"); +Hidden.AvoidDoubleComputation=old_hidden_avoid_double_computation; +createPlot( + id=1, + position={10, 20, 670, 900}, + y={ + "bui.maxTSet[1].y", + "bui.minTSet[1].y", + "bui.znAttic.TAir", + "bui.znCore_ZN.TAir", + "bui.znPerimeter_ZN_1.TAir", + "bui.znPerimeter_ZN_2.TAir", + "bui.znPerimeter_ZN_3.TAir", + "bui.znPerimeter_ZN_4.TAir" + }, + autoscale=true, + grid=true +); +createPlot( + id=1, + y={ + "bui.terUni[1].QActHea_flow", + "bui.terUni[2].QActHea_flow", + "bui.terUni[3].QActHea_flow", + "bui.terUni[4].QActHea_flow", + "bui.terUni[5].QActHea_flow", + "bui.terUni[6].QActHea_flow" + }, + grid=true, + subPlot=2 +); +createPlot( + id=1, + y={ + "bui.terUni[1].QActCoo_flow", + "bui.terUni[2].QActCoo_flow", + "bui.terUni[3].QActCoo_flow", + "bui.terUni[4].QActCoo_flow", + "bui.terUni[5].QActCoo_flow", + "bui.terUni[6].QActCoo_flow" + }, + grid=true, + subPlot=3 +); +createPlot( + id=2, + position={700, 20, 670, 900}, + y={ + "supHeaWat.T_in", + "bui.terUni[1].T_aHeaWat_nominal", + "bui.terUni[2].T_aHeaWat_nominal", + "bui.terUni[3].T_aHeaWat_nominal", + "bui.terUni[4].T_aHeaWat_nominal", + "bui.terUni[5].T_aHeaWat_nominal", + "bui.terUni[6].T_aHeaWat_nominal" + }, + autoscale=true, + grid=true +); +createPlot( + id=2, + y={ + "bui.disFloHea.mAct_flow[1].y", + "bui.disFloHea.mAct_flow[2].y", + "bui.disFloHea.mAct_flow[3].y", + "bui.disFloHea.mAct_flow[4].y", + "bui.disFloHea.mAct_flow[5].y", + "bui.disFloHea.mAct_flow[6].y" + }, + grid=true, + subPlot=2 +); +createPlot( + id=2, + y={ + "bui.terUni[1].QActHea_flow", + "bui.terUni[2].QActHea_flow", + "bui.terUni[3].QActHea_flow", + "bui.terUni[4].QActHea_flow", + "bui.terUni[5].QActHea_flow", + "bui.terUni[6].QActHea_flow" + }, + grid=true, + subPlot=3 +); +createPlot( + id=3, + position={1400, 20, 670, 900}, + y={ + "supChiWat.T_in", + "bui.terUni[1].T_aChiWat_nominal", + "bui.terUni[2].T_aChiWat_nominal", + "bui.terUni[3].T_aChiWat_nominal", + "bui.terUni[4].T_aChiWat_nominal", + "bui.terUni[5].T_aChiWat_nominal", + "bui.terUni[6].T_aChiWat_nominal" + }, + autoscale=true, + grid=true +); +createPlot( + id=3, + y={ + "bui.disFloCoo.mAct_flow[1].y", + "bui.disFloCoo.mAct_flow[2].y", + "bui.disFloCoo.mAct_flow[3].y", + "bui.disFloCoo.mAct_flow[4].y", + "bui.disFloCoo.mAct_flow[5].y", + "bui.disFloCoo.mAct_flow[6].y" + }, + grid=true, + subPlot=2 +); +createPlot( + id=3, + y={ + "bui.terUni[1].QActCoo_flow", + "bui.terUni[2].QActCoo_flow", + "bui.terUni[3].QActCoo_flow", + "bui.terUni[4].QActCoo_flow", + "bui.terUni[5].QActCoo_flow", + "bui.terUni[6].QActCoo_flow" + }, + grid=true, + subPlot=3 +); diff --git a/geojson_modelica_translator/model_connectors/templates/spawn_building.mot b/geojson_modelica_translator/model_connectors/templates/spawn_building.mot new file mode 100644 index 000000000..94a067043 --- /dev/null +++ b/geojson_modelica_translator/model_connectors/templates/spawn_building.mot @@ -0,0 +1,217 @@ +within {{project_name}}.Loads.{{model_name}}; +model BuildingSpawnZ6 + "n-zone EnergyPlus building model based on URBANopt GeoJSON export, with distribution pumps" + +extends Buildings.Applications.DHC.Loads.BaseClasses.PartialBuilding( + redeclare package Medium = MediumW, + have_eleHea=false, + have_eleCoo=false, + have_pum=true, + have_weaBus=false); + + replaceable package MediumW = Buildings.Media.Water + "Source side medium"; + replaceable package MediumA = Buildings.Media.Air + "Load side medium"; + + parameter Integer nZon + "Number of thermal zones" + parameter String idfPat= + "modelica://{{project_name}}/Loads/{{data['load_resources_path']}}/{{data['idf']['filename']}}" + "Path of the IDF file"; + parameter String weaPat= + "modelica://{{project_name}}/Loads/{{data['load_resources_path']}}/{{data['epw']['filename']}}" + "Path of the weather file"; + // TODO: Minimum and Maximum TSet: make a function of the outdoor air temperature, type of building,occupancy schedule or woking/idle days? + {% raw %} + Buildings.Controls.OBC.CDL.Continuous.Sources.Constant minTSet[nZon]( + k=fill(20+273.15, nZon)) + "Minimum temperature setpoint" + annotation (Placement(transformation(extent={{-220,60},{-200,80}}))); + Buildings.Controls.OBC.CDL.Continuous.Sources.Constant maxTSet[nZon]( + k=fill(24+273.15, nZon)) + "Maximum temperature setpoint" + annotation (Placement(transformation(extent={{-220,20},{-200,40}}))); + Modelica.Blocks.Sources.Constant qConGai_flow(k=0) "Convective heat gain" + annotation (Placement(transformation(extent={{-66,128},{-46,148}}))); + Modelica.Blocks.Sources.Constant qRadGai_flow(k=0) "Radiative heat gain" + annotation (Placement(transformation(extent={{-66,168},{-46,188}}))); + Modelica.Blocks.Routing.Multiplex3 multiplex3_1 + annotation (Placement(transformation(extent={{-20,128},{0,148}}))); + Modelica.Blocks.Sources.Constant qLatGai_flow(k=0) "Latent heat gain" + annotation (Placement(transformation(extent={{-66,88},{-46,108}}))); + {% endraw %} + + // TODO: apply a dynamic layout + {% for zone in data['thermal_zones'] %} + Buildings.ThermalZones.EnergyPlus.ThermalZone {{zone['modelica_object_name']}}( + redeclare package Medium = MediumA, + zoneName="{{zone['spawn_object_name']}}") "Thermal zone" + {% raw %} annotation (Placement(transformation(extent={{40,-20},{80,20}}))); {% endraw %} + {% endfor %} + + {% raw %} + inner Buildings.ThermalZones.EnergyPlus.Building building( + idfName=Modelica.Utilities.Files.loadResource(idfPat), + weaName=Modelica.Utilities.Files.loadResource(weaPat), + showWeatherData=false) + "Building outer component" + annotation (Placement(transformation(extent={{40,60},{60,80}}))); + + Buildings.Controls.OBC.CDL.Continuous.MultiSum mulSum(nin=6) + annotation (Placement(transformation(extent={{260,110},{280,130}}))); + Buildings.Controls.OBC.CDL.Continuous.MultiSum mulSum3(nin=2) + annotation (Placement(transformation(extent={{260,70},{280,90}}))); + + Buildings.Applications.DHC.Loads.Examples.BaseClasses.FanCoil4Pipe terUni[nZon]( + redeclare package Medium1 = MediumW, + redeclare package Medium2 = MediumA, + each facSca=facSca, + QHea_flow_nominal={50000,10000,10000,10000,10000,10000}, + QCoo_flow_nominal={-10000,-10000,-10000,-10000,-10000,-10000}, + each T_aLoaHea_nominal=293.15, + each T_aLoaCoo_nominal=297.15, + each T_bHeaWat_nominal=308.15, + each T_bChiWat_nominal=285.15, + each T_aHeaWat_nominal=313.15, + each T_aChiWat_nominal=280.15, + each mLoaHea_flow_nominal=5, + each mLoaCoo_flow_nominal=5) "Terminal unit" + annotation (Placement(transformation(extent={{-140,-2},{-120,20}}))); + + Buildings.Applications.DHC.Loads.BaseClasses.FlowDistribution disFloHea( + redeclare package Medium = MediumW, + m_flow_nominal=sum(terUni.mHeaWat_flow_nominal .* terUni.facSca), + dp_nominal=100000, + nPorts_a1=nZon, + nPorts_b1=nZon) + "Heating water distribution system" + annotation (Placement(transformation(extent={{-236,-188},{-216,-168}}))); + Buildings.Applications.DHC.Loads.BaseClasses.FlowDistribution disFloCoo( + redeclare package Medium = MediumW, + m_flow_nominal=sum(terUni.mChiWat_flow_nominal .* terUni.facSca), + typDis=Buildings.Applications.DHC.Loads.Types.DistributionType.ChilledWater, + dp_nominal=100000, + nPorts_a1=nZon, + nPorts_b1=nZon) + "Chilled water distribution system" + annotation (Placement(transformation(extent={{-160,-230},{-140,-210}}))); + {% endraw %} + +equation +{% raw %} + connect(qRadGai_flow.y,multiplex3_1.u1[1]) annotation (Line( + points={{-45,178},{-26,178},{-26,145},{-22,145}}, + color={0,0,127}, + smooth=Smooth.None)); + connect(qConGai_flow.y,multiplex3_1.u2[1]) annotation (Line( + points={{-45,138},{-22,138}}, + smooth=Smooth.None)); + connect(qLatGai_flow.y, multiplex3_1.u3[1]) annotation (Line( + points={{-22,131},{-26,131},{-26,98},{-45,98}}, + color={0,0,127})); + connect(disFloHea.port_a,ports_a[1]) annotation (Line( + points={{-300,0},{-280,0},{-280,-178},{-236,-178}}, + color={0,127,255})); + connect(disFloHea.port_b, ports_b[1]) annotation (Line( + points={{-216,-178},{260,-178},{260,0},{300,0}}, + color={0,127,255})); + connect(disFloCoo.port_a, ports_a[2]) annotation (Line( + points={{-300,0},{-280,0},{-280,-220},{-160,-220}}, + color={0,127,255})); + connect(disFloCoo.port_b, ports_b[2]) annotation (Line( + points={{-140,-220},{280,-220},{280,0},{300,0}}, + color={0,127,255})); + connect(mulSum.y, PFan) annotation (Line( + points={{282,120},{302,120},{302,120},{320,120}}, + color={0,0,127})); + connect(PPum, mulSum3.y) annotation (Line( + points={{320,80},{302,80},{302,80},{282,80}}, + color={0,0,127})); + connect(disFloHea.PPum, mulSum3.u[1]) annotation (Line( + points={{-215,-186},{220.5,-186},{220.5,81},{258,81}}, + color={0,0,127})); + connect(disFloCoo.PPum, mulSum3.u[2]) annotation (Line( + points={{-139,-228},{224,-228},{224,79},{258,79}}, + color={0,0,127})); + connect(disFloHea.QActTot_flow, QHea_flow) annotation (Line( + points={{-215,-184},{-2,-184},{-2,-182},{212,-182},{212,280},{320,280}}, + color={0,0,127})); + connect(disFloCoo.QActTot_flow, QCoo_flow) annotation (Line( + points={{-139,-226},{28,-226},{28,-224},{216,-224},{216,240},{320,240}}, + color={0,0,127})); +{% endraw %} + +{% for zone in data['thermal_zones'] %} + connect(multiplex3_1.y, {{zone['modelica_object_name']}}.qGai_flow) + {% raw %}annotation (Line(points={{1,138},{20,138},{20,30},{22,30}}, color={0,0,127}));{% endraw %} + for i in 1:nZon loop + connect ({{zone['modelica_object_name']}}.ports[1], terUni[i].port_aLoa) + {% raw %}annotation (Line(points={{-160,-41.6667},{-172,-41.6667},{-180,-41.6667},{-180,-19.2},{62,-19.2}},{% endraw %} + color={0,127,255})); + connect ({{zone['modelica_object_name']}}.ports[2], terUni[i].port_bLoa) + {% raw %}annotation (Line(points={{-140,18.1667},{-20,18.1667},{-20,-39.2},{46,-39.2}},{% endraw %} + color={0,127,255})); + connect ({{zone['modelica_object_name']}}.TAir, terUni[i].TSen) + {% raw %}annotation (Line(points={{65,33.8},{72,33.8},{72,34},{80,34},{80,160},{-152,160},{-152,10.8333},{-140.833,10.8333}},{% endraw %} + color={0,0,127})); + connect(terUni[i].PFan, mulSum.u[i]) + {% raw %}annotation (Line(points={{-119.167,9},{-100,9},{-100,220},{220,220},{220,118.333},{258,118.333}},{% endraw %} + color={0,0,127})); + connect(disFloCoo.ports_a1[i], terUni[i].port_bChiWat) + {% raw %}annotation (Line(points={{-140,-214},{-38,-214},{-38,1.66667},{-120,1.66667}},{% endraw %} + color={0,127,255})); + connect(disFloCoo.ports_b1[i], terUni[i].port_aChiWat) + {% raw %}annotation (Line(points={{-160,-214},{-260,-214},{-260,1.66667},{-140,1.66667}},{% endraw %} + color={0,127,255})); + connect(disFloHea.ports_a1[i], terUni[i].port_bHeaWat) + {% raw %}annotation (Line(points={{-220,-164},{-40,-164},{-40,-0.166667},{-120,-0.166667}},{% endraw %} + color={0,127,255})); + connect(disFloHea.ports_b1[i], terUni[i].port_aHeaWat) + {% raw %}annotation (Line(points={{-240,-164},{-260,-164},{-260,-0.166667},{-140,-0.166667}},{% endraw %} + color={0,127,255})); + connect(terUni[i].mReqChiWat_flow, disFloCoo.mReq_flow[i]) + {% raw %}annotation (Line(points={{-119.167,3.5},{-104,3.5},{-104,-80},{-180,-80},{-180,-224},{-161,-224}},{% endraw %} + color={0,0,127})); + connect(terUni[i].mReqHeaWat_flow, disFloHea.mReq_flow[i]) + {% raw %}annotation (Line(points={{-119.167,5.33333},{-100,5.33333},{-100,-90.5},{-241,-90.5},{-241,-174}},{% endraw %} + color={0,0,127})); + connect(terUni[i].TSetHea, minTSet[i].y) + {% raw %}annotation (Line(points={{-140.833,14.5},{-160,14.5},{-160,70},{-198,70}},{% endraw %} + color={0,0,127})); + connect(terUni[i].TSetCoo, maxTSet[i].y) + {% raw %}annotation (Line(points={{-140.833,12.6667},{-164,12.6667},{-164,30},{-198,30}},{% endraw %} + color={0,0,127})); + end for; + {% endfor %} + +{% raw %} +annotation ( +Diagram(coordinateSystem(extent={{-300,-300},{300,300}})), +Icon(coordinateSystem(extent={{-100,-100},{100,100}}), graphics={ + Bitmap(extent={{-40,-24},{48,22}}, + fileName="modelica://Buildings/Resources/Images/ThermalZones/EnergyPlus/EnergyPlusLogo.png")}), +Documentation( info=" +

    + This is a simplified six-zone building model based on EnergyPlus + building envelope model. + It was generated from translating a GeoJSON model specified within URBANopt UI. + The heating and cooling loads are computed with a four-pipe + fan coil unit model derived from + + Buildings.Applications.DHC.Loads.BaseClasses.PartialTerminalUnit + and connected to the room model by means of fluid ports. +

    +", revisions=" +
    • +March 12, 2020: Nicholas Long
      +Updated implemention to handle template needed for GeoJSON to Modelica. +
    • +
    • +February 21, 2020, by Antoine Gautier:
      +First implementation. +
    • +
    +")); +{% endraw %} +end BuildingSpawnZ6; diff --git a/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot b/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot new file mode 100644 index 000000000..5f288126a --- /dev/null +++ b/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot @@ -0,0 +1,135 @@ +within {{project_name}}.Loads.{{model_name}}; +model CouplingSpawnZ6 + "FMU Template for Spawn" + + replaceable package MediumW = Buildings.Media.Water + "Source side medium"; + replaceable package MediumA = Buildings.Media.Air + "Load side medium"; + +{% raw %} + Buildings.Applications.DHC.Loads.Examples.BaseClasses.BuildingSpawnZ1 bui( + nPorts_a=2, + nPorts_b=2) + "Building" + annotation (Placement(transformation(extent={{40,-40},{60,-20}}))); + Buildings.Fluid.Sources.MassFlowSource_T supHeaWat( + use_m_flow_in=true, + redeclare package Medium = MediumW, + use_T_in=true, + nPorts=1) "Heating water supply" + annotation (Placement(transformation( + extent={{-10,-10},{10,10}}, + rotation=0, + origin={-30,0}))); + Modelica.Blocks.Sources.RealExpression THeaWatSup(y=bui.terUni.T_aHeaWat_nominal) + "Heating water supply temperature" + annotation (Placement(transformation(extent={{-80,-10},{-60,10}}))); + Modelica.Blocks.Sources.RealExpression mHeaWat_flow(y=bui.disFloHea.mReqTot_flow) + "Heating water flow rate" + annotation (Placement(transformation(extent={{-80,10},{-60,30}}))); + Buildings.Fluid.Sources.MassFlowSource_T supChiWat( + use_m_flow_in=true, + redeclare package Medium = MediumW, + use_T_in=true, + nPorts=1) "Chilled water supply" + annotation (Placement(transformation( + extent={{-10,-10},{10,10}}, + rotation=0, + origin={-30,-80}))); + Modelica.Blocks.Sources.RealExpression TChiWatSup(y=bui.terUni.T_aChiWat_nominal) + "Chilled water supply temperature" + annotation (Placement(transformation(extent={{-80,-90},{-60,-70}}))); + Modelica.Blocks.Sources.RealExpression mChiWat_flow(y=bui.disFloCoo.mReqTot_flow) + "Chilled water flow rate" + annotation (Placement(transformation(extent={{-80,-70},{-60,-50}}))); + Buildings.Fluid.Sources.Boundary_pT sinHeaWat( + redeclare package Medium = Medium1, + p=300000, + nPorts=1) "Sink for heating water" annotation (Placement(transformation( + extent={{10,-10},{-10,10}}, + rotation=0, + origin={130,0}))); + Buildings.Fluid.Sources.Boundary_pT sinChiWat( + redeclare package Medium = MediumW, + p=300000, + nPorts=1) "Sink for chilled water" annotation (Placement(transformation( + extent={{10,-10},{-10,10}}, + rotation=0, + origin={130,-80}))); +{% endraw %} +equation +{% raw %} + connect(THeaWatSup.y, supHeaWat.T_in) annotation (Line(points={{-59,0},{-50,0}, + {-50,4},{-42,4}}, color={0,0,127})); + connect(mHeaWat_flow.y, supHeaWat.m_flow_in) annotation (Line(points={{-59,20}, + {-50,20},{-50,8},{-42,8}}, color={0,0,127})); + connect(TChiWatSup.y, supChiWat.T_in) annotation (Line(points={{-59,-80},{-50, + -80},{-50,-76},{-42,-76}}, color={0,0,127})); + connect(mChiWat_flow.y, supChiWat.m_flow_in) annotation (Line(points={{-59, + -60},{-50,-60},{-50,-72},{-42,-72}}, color={0,0,127})); + connect(supHeaWat.ports[1], bui.ports_a[1]) annotation (Line(points={{-20,0}, + {0,0},{0,-50},{20,-50}}, color={0,127,255})); + connect(supChiWat.ports[1], bui.ports_a[2]) annotation (Line(points={{-20,-80}, + {0,-80},{0,-46},{20,-46}}, color={0,127,255})); + connect(bui.ports_b[1], sinHeaWat.ports[1]) annotation (Line(points={{80,-50}, + {94,-50},{94,0},{120,0}}, color={0,127,255})); + connect(bui.ports_b[2], sinChiWat.ports[1]) annotation (Line(points={{80,-46}, + {94,-46},{94,-80},{120,-80}}, color={0,127,255})); +{% endraw %} + + // TODO: determine how to handle the "lines" +{% raw %} +annotation (Diagram( + coordinateSystem(preserveAspectRatio=false, extent={{-120,-100},{160,60}}), + graphics={Text( + extent={{-46,36},{86,10}}, + lineColor={28,108,200}, + textString="")}), + __Dymola_Commands(file= + "Resources/Scripts/Dymola/SpawnTwoBuildings/CouplingSpawnZ6.mos" + "Simulate and plot"), + experiment( + StopTime=604800, + Tolerance=1e-06, + __Dymola_Algorithm="Cvode"), +Documentation (info= +" +

    +This example illustrates the use of + +Buildings.Applications.DHC.Loads.BaseClasses.PartialBuilding, + +Buildings.Applications.DHC.Loads.BaseClasses.PartialTerminalUnit +and + +Buildings.Applications.DHC.Loads.BaseClasses.FlowDistribution +in a configuration with: +

    +
      +
    • +six-zone building model based on EnergyPlus envelope model (from +GeoJSON export), +
    • +
    • +secondary pumps. +
    • +
    +

    +Simulation with Dymola requires minimum version 2020x and setting +Hidden.AvoidDoubleComputation=true, see + +Buildings.ThermalZones.EnergyPlus.UsersGuide. +

    +", +revisions= +" +
      +
    • +February 21, 2020, by Antoine Gautier:
      +First implementation. +
    • +
    +")); +{% endraw %} +end CouplingSpawnZ6; diff --git a/tests/model_connectors/test_spawn.py b/tests/model_connectors/test_spawn.py index a5068bfc2..e3ac4c02a 100644 --- a/tests/model_connectors/test_spawn.py +++ b/tests/model_connectors/test_spawn.py @@ -35,7 +35,7 @@ GeoJsonModelicaTranslator ) from geojson_modelica_translator.model_connectors.spawn import SpawnConnector -# from geojson_modelica_translator.modelica.modelica_runner import ModelicaRunner +from geojson_modelica_translator.modelica.modelica_runner import ModelicaRunner from geojson_modelica_translator.system_parameters.system_parameters import ( SystemParameters ) @@ -98,13 +98,13 @@ def setUp(self): for b in gj.buildings: self.spawn.add_building(b) - # def test_spawn_to_modelica_and_run(self): - # self.spawn.to_modelica('spawn_two_building', 'tests/model_connectors/output') - # - # # make sure the model can run using the ModelicaRunner class - # mr = ModelicaRunner() - # file_to_run = os.path.abspath( - # 'tests/model_connectors/output/spawn_two_building/Loads/B5a6b99ec37f4de7f94020090/building.mo' - # ) - # exitcode = mr.run_in_docker(file_to_run) - # self.assertEqual(0, exitcode) + def test_spawn_to_modelica_and_run(self): + self.spawn.to_modelica('spawn_two_building', 'tests/model_connectors/output') + + # make sure the model can run using the ModelicaRunner class + mr = ModelicaRunner() + file_to_run = os.path.abspath( + 'tests/model_connectors/output/spawn_two_building/Loads/B5a6b99ec37f4de7f94020090/building.mo' + ) + exitcode = mr.run_in_docker(file_to_run) + self.assertEqual(0, exitcode) diff --git a/tests/output_ets/ets_cooling_indirect_templated.mo b/tests/output_ets/ets_cooling_indirect_templated.mo deleted file mode 100644 index a0fc03a3f..000000000 --- a/tests/output_ets/ets_cooling_indirect_templated.mo +++ /dev/null @@ -1,369 +0,0 @@ - -within Buildings.Applications.DHC.EnergyTransferStations; - -model ets_cooling_indirect_templated - - - "Indirect cooling energy transfer station for district energy systems" - extends Buildings.Fluid.Interfaces.PartialFourPort( - redeclare final package Medium1 = Medium, - redeclare final package Medium2 = Medium); - - replaceable package Medium = - Modelica.Media.Interfaces.PartialMedium "Medium in the component"; - - - - // mass flow rates - parameter Modelica.SIunits.MassFlowRate m1_flow_nominal( - final min=0, - start=0.666) - "Nominal mass flow rate of primary (district) district cooling side"; - - - - parameter Modelica.SIunits.MassFlowRate m2_flow_nominal( - final min=0, - start=0.5) - "Nominal mass flow rate of secondary (building) district cooling side"; - - - - // Primary supply control valve - parameter Modelica.SIunits.PressureDifference dpValve_nominal( - final min=0, - final displayUnit="Pa")=888 - "Nominal pressure drop of fully open control valve"; - - - - // Heat exchanger - parameter Modelica.SIunits.PressureDifference dp1_nominal( - final min=0, - start=999, - final displayUnit="Pa") - "Nominal pressure difference on primary side" - annotation(Dialog(group="Heat exchanger")); - - - - parameter Modelica.SIunits.PressureDifference dp2_nominal( - final min=0, - start=999, - final displayUnit="Pa") - "Nominal pressure difference on secondary side" - annotation(Dialog(group="Heat exchanger")); - - - - parameter Boolean use_Q_flow_nominal=true - "Set to true to specify Q_flow_nominal and temperatures, or to false to specify effectiveness" - annotation(Dialog(group="Heat exchanger")); - - - - parameter Modelica.SIunits.HeatFlowRate Q_flow_nominal( - final min=0, - start=8000) - "Nominal heat transfer" - annotation(Dialog(group="Heat exchanger")); - - - - - parameter Modelica.SIunits.Temperature T_a1_nominal( - min=0+273, - max=100+273.15, - start=5+273.15, - final displayUnit="K") - "Nominal temperature at port a1" - annotation(Dialog(group="Heat exchanger")); - - - - parameter Modelica.SIunits.Temperature T_a2_nominal( - min=0+273, - max=100+273.15, - start=7+273.15, - final displayUnit="K") - "Nominal temperature at port a2" - annotation(Dialog(group="Heat exchanger")); - - - - parameter Modelica.SIunits.Efficiency eta( - final min=0, - final max=1)=0.666 - "Constant effectiveness" - annotation(Dialog(group="Heat exchanger")); - - - - // Controller parameters - parameter Modelica.Blocks.Types.SimpleController controllerType= - Modelica.Blocks.Types.SimpleController.PI - "Type of controller" - annotation(Dialog(tab="Controller")); - parameter Real k(final min=0, final unit="1") = 1 - "Gain of controller" - annotation(Dialog(tab="Controller")); - parameter Modelica.SIunits.Time Ti( - min=Modelica.Constants.small)=120 - "Time constant of integrator block" - annotation (Dialog(tab="Controller", enable= - controllerType == Modelica.Blocks.Types.SimpleController.PI or - controllerType == Modelica.Blocks.Types.SimpleController.PID)); - parameter Modelica.SIunits.Time Td(final min=0)=0.1 - "Time constant of derivative block" - annotation (Dialog(tab="Controller", enable= - controllerType == Modelica.Blocks.Types.SimpleController.PD or - controllerType == Modelica.Blocks.Types.SimpleController.PID)); - parameter Real yMax(start=1)=1 - "Upper limit of output" - annotation(Dialog(tab="Controller")); - parameter Real yMin=0 - "Lower limit of output" - annotation(Dialog(tab="Controller")); - parameter Real wp(final min=0) = 1 - "Set-point weight for Proportional block (0..1)" - annotation(Dialog(tab="Controller")); - parameter Real wd(final min=0) = 0 - "Set-point weight for Derivative block (0..1)" - annotation(Dialog(tab="Controller", enable= - controllerType==Modelica.Blocks.Types.SimpleController.PD or - controllerType==Modelica.Blocks.Types.SimpleController.PID)); - parameter Real Ni(min=100*Modelica.Constants.eps) = 0.9 - "Ni*Ti is time constant of anti-windup compensation" - annotation(Dialog(tab="Controller", enable= - controllerType==Modelica.Blocks.Types.SimpleController.PI or - controllerType==Modelica.Blocks.Types.SimpleController.PID)); - parameter Real Nd(min=100*Modelica.Constants.eps) = 10 - "The higher Nd, the more ideal the derivative block" - annotation(Dialog(tab="Controller", enable= - controllerType==Modelica.Blocks.Types.SimpleController.PD or - controllerType==Modelica.Blocks.Types.SimpleController.PID)); - parameter Modelica.Blocks.Types.InitPID initType= - Modelica.Blocks.Types.InitPID.DoNotUse_InitialIntegratorState - "Type of initialization (1: no init, 2: steady state, 3: initial state, 4: initial output)" - annotation(Evaluate=true, Dialog(group="Initialization", tab="Controller")); - parameter Real xi_start=0 - "Initial or guess value value for integrator output (= integrator state)" - annotation (Dialog(group="Initialization", tab="Controller", - enable=controllerType==Modelica.Blocks.Types.SimpleController.PI or - controllerType==Modelica.Blocks.Types.SimpleController.PID)); - parameter Real xd_start=0 - "Initial or guess value for state of derivative block" - annotation (Dialog(group="Initialization", tab="Controller", - enable=controllerType==Modelica.Blocks.Types.SimpleController.PD or - controllerType==Modelica.Blocks.Types.SimpleController.PID)); - parameter Real yCon_start=0 - "Initial value of output from the controller" - annotation(Dialog(group="Initialization", tab="Controller", - enable=initType == Modelica.Blocks.Types.InitPID.InitialOutput)); - parameter Boolean reverseAction = true - "Set to true for throttling the water flow rate through a cooling coil controller" - annotation(Dialog(tab="Controller")); - - Modelica.Blocks.Interfaces.RealInput TSet( - final quantity="ThermodynamicTemperature", - final unit="K") - "Setpoint temperature" - annotation (Placement(transformation(extent={{-140,-20},{-100,20}}))); - - Modelica.Blocks.Interfaces.RealOutput Q_flow( - final quantity="Power", - final unit="W", - final displayUnit="kW") - "Measured power demand at the ETS" - annotation (Placement(transformation(extent={{100,140},{120,160}}))); - - Modelica.Blocks.Interfaces.RealOutput Q( - final quantity="Energy", - final unit="J", - final displayUnit="kWh") - "Measured energy consumption at the ETS" - annotation (Placement(transformation(extent={{100,100},{120,120}}))); - - Buildings.Fluid.HeatExchangers.PlateHeatExchangerEffectivenessNTU hex( - redeclare final package Medium1 = Medium, - redeclare final package Medium2 = Medium, - final m1_flow_nominal=m1_flow_nominal, - final m2_flow_nominal=m2_flow_nominal, - final dp1_nominal=dp1_nominal, - final dp2_nominal=dp2_nominal, - final configuration=Buildings.Fluid.Types.HeatExchangerConfiguration.CounterFlow, - final use_Q_flow_nominal=true, - final Q_flow_nominal=Q_flow_nominal, - final T_a1_nominal=T_a1_nominal, - final T_a2_nominal=T_a2_nominal) "Indirect cooling heat exchanger" - annotation (Placement(transformation(extent={{20,-10},{40,10}}))); - - Buildings.Controls.Continuous.LimPID con( - final controllerType=Modelica.Blocks.Types.SimpleController.PID, - final k=k, - final Td=Td, - final yMax=yMax, - final yMin=yMin, - final Ti=Ti, - final wp=wp, - final wd=wd, - final Ni=Ni, - final Nd=Nd, - final initType=Modelica.Blocks.Types.InitPID.InitialOutput, - final xi_start=xi_start, - final xd_start=xd_start, - final y_start=yCon_start, - final reverseAction=reverseAction) "Controller" - annotation (Placement(transformation(extent={{-90,-10},{-70,10}}))); - - Buildings.Fluid.Sensors.TemperatureTwoPort senTDisSup( - redeclare final package Medium = Medium, - final m_flow_nominal=m1_flow_nominal) - "District-side (primary) supply temperature sensor" - annotation (Placement(transformation(extent={{-90,50},{-70,70}}))); - - Buildings.Fluid.Sensors.TemperatureTwoPort senTDisRet( - redeclare final package Medium = Medium, - final m_flow_nominal=m1_flow_nominal) - "District-side (primary) return temperature sensor" - annotation (Placement(transformation(extent={{70,50},{90,70}}))); - - Modelica.Blocks.Continuous.Integrator int(k=1) "Integration" - annotation (Placement(transformation(extent={{60,120},{80,100}}))); - - Buildings.Fluid.Sensors.MassFlowRate senMasFlo( - redeclare package Medium = Medium) - annotation (Placement(transformation(extent={{-60,50},{-40,70}}))); - - Buildings.Fluid.Sensors.TemperatureTwoPort TBuiRet( - redeclare final package Medium = Medium, - final m_flow_nominal=m2_flow_nominal) - "Building-side (secondary) return temperature" - annotation (Placement(transformation(extent={{-70,-70},{-90,-50}}))); - - Buildings.Fluid.Actuators.Valves.TwoWayQuickOpening val( - redeclare final package Medium = Medium, - final m_flow_nominal=m1_flow_nominal, - final dpValve_nominal=dpValve_nominal, - riseTime(displayUnit="s") = 60, - y_start=0) "District-side (primary) control valve" - annotation (Placement(transformation(extent={{-30,70},{-10,50}}))); - - Modelica.Blocks.Math.Gain cp(final k=cp_default) - "Specifc heat multiplier to calculate heat flow rate" - annotation (Placement(transformation(extent={{20,100},{40,120}}))); - - Modelica.Blocks.Math.Product pro "Product" - annotation (Placement(transformation(extent={{-20,100},{0,120}}))); - - Modelica.Blocks.Math.Add dTDis(k1=-1, k2=+1) - "Temperatur difference on the district side" - annotation (Placement(transformation(extent={{-60,106},{-40,126}}))); - -protected - final parameter Medium.ThermodynamicState sta_default = Medium.setState_pTX( - T=Medium.T_default, - p=Medium.p_default, - X=Medium.X_default) "Medium state at default properties"; - final parameter Modelica.SIunits.SpecificHeatCapacity cp_default= - Medium.specificHeatCapacityCp(sta_default) - "Specific heat capacity of the fluid"; - -equation - - connect(hex.port_a2, port_a2) annotation (Line(points={{40,-6},{60,-6},{60,-60}, - {100,-60}}, color={0,127,255})); - connect(hex.port_b1, senTDisRet.port_a) annotation (Line(points={{40,6},{60,6}, - {60,60},{70,60}}, color={0,127,255})); - connect(val.port_b, hex.port_a1) annotation (Line(points={{-10,60},{0,60},{0,6}, - {20,6}}, color={0,127,255})); - connect(senMasFlo.port_b, val.port_a) - annotation (Line(points={{-40,60},{-30,60}}, color={0,127,255})); - connect(port_a1, senTDisSup.port_a) - annotation (Line(points={{-100,60},{-90,60}}, color={0,127,255})); - connect(senTDisSup.port_b, senMasFlo.port_a) - annotation (Line(points={{-70,60},{-60,60}}, color={0,127,255})); - connect(port_b2, TBuiRet.port_b) - annotation (Line(points={{-100,-60},{-90,-60}}, color={0,127,255})); - connect(senTDisRet.port_b, port_b1) - annotation (Line(points={{90,60},{100,60}}, color={0,127,255})); - connect(TSet, con.u_s) - annotation (Line(points={{-120,0},{-106,0},{-106,0},{-92,0}}, - color={0,0,127})); - connect(con.u_m, TBuiRet.T) - annotation (Line(points={{-80,-12},{-80,-49}}, color={0,0,127})); - connect(con.y, val.y) - annotation (Line(points={{-69,0},{-20,0},{-20,48}}, color={0,0,127})); - connect(TBuiRet.port_a, hex.port_b2) annotation (Line(points={{-70,-60},{0,-60}, - {0,-6},{20,-6}}, color={0,127,255})); - connect(pro.y, cp.u) - annotation (Line(points={{1,110},{18,110}}, color={0,0,127})); - connect(senMasFlo.m_flow, pro.u2) - annotation (Line(points={{-50,71},{-50,104},{-22,104}}, color={0,0,127})); - connect(senTDisSup.T, dTDis.u1) - annotation (Line(points={{-80,71},{-80,122},{-62,122}}, color={0,0,127})); - connect(senTDisRet.T, dTDis.u2) annotation (Line(points={{80,71},{80,80},{-70, - 80},{-70,110},{-62,110}}, color={0,0,127})); - connect(dTDis.y, pro.u1) - annotation (Line(points={{-39,116},{-22,116}}, color={0,0,127})); - connect(cp.y, int.u) - annotation (Line(points={{41,110},{58,110}}, color={0,0,127})); - connect(int.y, Q) - annotation (Line(points={{81,110},{110,110}}, color={0,0,127})); - connect(Q_flow, cp.y) annotation (Line(points={{110,150},{50,150},{50,110},{41, - 110}}, color={0,0,127})); - -annotation (defaultComponentName="coo", - Icon(coordinateSystem(preserveAspectRatio=false), graphics={ - Rectangle( - extent={{-100,-56},{100,-64}}, - fillColor={0,0,0}, - fillPattern=FillPattern.Solid, - pattern=LinePattern.None), - Rectangle( - extent={{-100,64},{100,56}}, - fillColor={0,0,0}, - fillPattern=FillPattern.Solid, - pattern=LinePattern.None), - Rectangle( - extent={{-80,80},{80,-80}}, - lineColor={175,175,175}, - fillColor={35,138,255}, - fillPattern=FillPattern.Solid), - Text( - extent={{-52,40},{54,-40}}, - lineColor={0,0,0}, - fillColor={35,138,255}, - fillPattern=FillPattern.Solid, - textStyle={TextStyle.Bold}, - textString="ETS")}), Diagram( - coordinateSystem(preserveAspectRatio=false, extent={{-100,-100},{100,160}})), - Documentation(info=" -

    -Indirect cooling energy transfer station (ETS) model that controls -the building chilled water supply temperature by modulating a -primary control valve on the district supply side. The design is -based on a typical district cooling ETS described in ASHRAE's - -District Cooling Guide. -As shown in the figure below, the building pumping design (constant, -variable) is specified on the building side, not within the ETS. -

    -

    -\"DHC.ETS.CoolingIndirect\"/ -

    -

    Reference

    -

    -American Society of Heating, Refrigeration and Air-Conditioning -Engineers. (2013). Chapter 5: End User Interface. In -District Cooling Guide. 1st Edition. -

    -", revisions=" -
      -
    • -November 1, 2019, by Kathryn Hinkelman:
      -First implementation.
    • -
    • 12/15/2020, Yanfei Li, Templating ETS model.
    • -
    -")); -end ets_cooling_indirect_templated; From 2ad9f0c05e7cf73fbe8db273db5a26021f62d93f Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Mon, 23 Mar 2020 20:41:00 -0600 Subject: [PATCH 44/62] remove unneeded files in directory --- .gitignore | 2 + ets_cooling_indirect.mo | 355 ----------------- .../ets_cooling_indirect_2.mo | 332 ---------------- .../model_connectors/ets_template.py | 1 + .../ets_cooling_indirect_templated.mo | 369 ------------------ 5 files changed, 3 insertions(+), 1056 deletions(-) delete mode 100644 ets_cooling_indirect.mo delete mode 100644 geojson_modelica_translator/model_connectors/ets_cooling_indirect_2.mo delete mode 100644 geojson_modelica_translator/modelica/ets_cooling_indirect_templated.mo diff --git a/.gitignore b/.gitignore index f1540efb4..e502eddb3 100644 --- a/.gitignore +++ b/.gitignore @@ -98,3 +98,5 @@ tests/output_ets tests/modelica/output tests/model_connectors/output /geojson_modelica_translator/modelica/buildingslibrary/ +# TODO: this file shoud not be writtent out, but it is... fix this eventually +/geojson_modelica_translator/modelica/ets_cooling_indirect_templated.mo diff --git a/ets_cooling_indirect.mo b/ets_cooling_indirect.mo deleted file mode 100644 index eb1702f07..000000000 --- a/ets_cooling_indirect.mo +++ /dev/null @@ -1,355 +0,0 @@ - -within Buildings.Applications.DHC.EnergyTransferStations; -model CoolingIndirect - - "Indirect cooling energy transfer station for district energy systems" - extends Buildings.Fluid.Interfaces.PartialFourPort( - redeclare final package Medium1 = Medium, - redeclare final package Medium2 = Medium); - - replaceable package Medium = - Modelica.Media.Interfaces.PartialMedium "Medium in the component"; - - - - // mass flow rates - parameter Modelica.SIunits.MassFlowRate m1_flow_nominal( - final min=0, - start=0.666) - "Nominal mass flow rate of primary (district) district cooling side"; - - - - - - - - // Heat exchanger - parameter Modelica.SIunits.PressureDifference dp1_nominal( - final min=0, - start=999, - final displayUnit="Pa") - "Nominal pressure difference on primary side" - annotation(Dialog(group="Heat exchanger")); - - - - parameter Modelica.SIunits.PressureDifference dp2_nominal( - final min=0, - start=999, - final displayUnit="Pa") - "Nominal pressure difference on secondary side" - annotation(Dialog(group="Heat exchanger")); - - - - parameter Boolean use_Q_flow_nominal=true - "Set to true to specify Q_flow_nominal and temperatures, or to false to specify effectiveness" - annotation(Dialog(group="Heat exchanger")); - - - - parameter Modelica.SIunits.HeatFlowRate Q_flow_nominal( - final min=0, - start=8000) - "Nominal heat transfer" - annotation(Dialog(group="Heat exchanger")); - - - - - parameter Modelica.SIunits.Temperature T_a1_nominal( - min=0+273, - max=100+273.15, - start=5+273.15, - final displayUnit="K") - "Nominal temperature at port a1" - annotation(Dialog(group="Heat exchanger")); - - - - parameter Modelica.SIunits.Temperature T_a2_nominal( - min=0+273, - max=100+273.15, - start=7+273.15, - final displayUnit="K") - "Nominal temperature at port a2" - annotation(Dialog(group="Heat exchanger")); - - - - parameter Modelica.SIunits.Efficiency eta( - final min=0, - final max=1)=0.666 - "Constant effectiveness" - annotation(Dialog(group="Heat exchanger")); - - - - // Controller parameters - parameter Modelica.Blocks.Types.SimpleController controllerType= - Modelica.Blocks.Types.SimpleController.PI - "Type of controller" - annotation(Dialog(tab="Controller")); - parameter Real k(final min=0, final unit="1") = 1 - "Gain of controller" - annotation(Dialog(tab="Controller")); - parameter Modelica.SIunits.Time Ti( - min=Modelica.Constants.small)=120 - "Time constant of integrator block" - annotation (Dialog(tab="Controller", enable= - controllerType == Modelica.Blocks.Types.SimpleController.PI or - controllerType == Modelica.Blocks.Types.SimpleController.PID)); - parameter Modelica.SIunits.Time Td(final min=0)=0.1 - "Time constant of derivative block" - annotation (Dialog(tab="Controller", enable= - controllerType == Modelica.Blocks.Types.SimpleController.PD or - controllerType == Modelica.Blocks.Types.SimpleController.PID)); - parameter Real yMax(start=1)=1 - "Upper limit of output" - annotation(Dialog(tab="Controller")); - parameter Real yMin=0 - "Lower limit of output" - annotation(Dialog(tab="Controller")); - parameter Real wp(final min=0) = 1 - "Set-point weight for Proportional block (0..1)" - annotation(Dialog(tab="Controller")); - parameter Real wd(final min=0) = 0 - "Set-point weight for Derivative block (0..1)" - annotation(Dialog(tab="Controller", enable= - controllerType==Modelica.Blocks.Types.SimpleController.PD or - controllerType==Modelica.Blocks.Types.SimpleController.PID)); - parameter Real Ni(min=100*Modelica.Constants.eps) = 0.9 - "Ni*Ti is time constant of anti-windup compensation" - annotation(Dialog(tab="Controller", enable= - controllerType==Modelica.Blocks.Types.SimpleController.PI or - controllerType==Modelica.Blocks.Types.SimpleController.PID)); - parameter Real Nd(min=100*Modelica.Constants.eps) = 10 - "The higher Nd, the more ideal the derivative block" - annotation(Dialog(tab="Controller", enable= - controllerType==Modelica.Blocks.Types.SimpleController.PD or - controllerType==Modelica.Blocks.Types.SimpleController.PID)); - parameter Modelica.Blocks.Types.InitPID initType= - Modelica.Blocks.Types.InitPID.DoNotUse_InitialIntegratorState - "Type of initialization (1: no init, 2: steady state, 3: initial state, 4: initial output)" - annotation(Evaluate=true, Dialog(group="Initialization", tab="Controller")); - parameter Real xi_start=0 - "Initial or guess value value for integrator output (= integrator state)" - annotation (Dialog(group="Initialization", tab="Controller", - enable=controllerType==Modelica.Blocks.Types.SimpleController.PI or - controllerType==Modelica.Blocks.Types.SimpleController.PID)); - parameter Real xd_start=0 - "Initial or guess value for state of derivative block" - annotation (Dialog(group="Initialization", tab="Controller", - enable=controllerType==Modelica.Blocks.Types.SimpleController.PD or - controllerType==Modelica.Blocks.Types.SimpleController.PID)); - parameter Real yCon_start=0 - "Initial value of output from the controller" - annotation(Dialog(group="Initialization", tab="Controller", - enable=initType == Modelica.Blocks.Types.InitPID.InitialOutput)); - parameter Boolean reverseAction = true - "Set to true for throttling the water flow rate through a cooling coil controller" - annotation(Dialog(tab="Controller")); - - Modelica.Blocks.Interfaces.RealInput TSet( - final quantity="ThermodynamicTemperature", - final unit="K") - "Setpoint temperature" - annotation (Placement(transformation(extent={{-140,-20},{-100,20}}))); - - Modelica.Blocks.Interfaces.RealOutput Q_flow( - final quantity="Power", - final unit="W", - final displayUnit="kW") - "Measured power demand at the ETS" - annotation (Placement(transformation(extent={{100,140},{120,160}}))); - - Modelica.Blocks.Interfaces.RealOutput Q( - final quantity="Energy", - final unit="J", - final displayUnit="kWh") - "Measured energy consumption at the ETS" - annotation (Placement(transformation(extent={{100,100},{120,120}}))); - - Buildings.Fluid.HeatExchangers.PlateHeatExchangerEffectivenessNTU hex( - redeclare final package Medium1 = Medium, - redeclare final package Medium2 = Medium, - final m1_flow_nominal=m1_flow_nominal, - final m2_flow_nominal=m2_flow_nominal, - final dp1_nominal=dp1_nominal, - final dp2_nominal=dp2_nominal, - final configuration=Buildings.Fluid.Types.HeatExchangerConfiguration.CounterFlow, - final use_Q_flow_nominal=true, - final Q_flow_nominal=Q_flow_nominal, - final T_a1_nominal=T_a1_nominal, - final T_a2_nominal=T_a2_nominal) "Indirect cooling heat exchanger" - annotation (Placement(transformation(extent={{20,-10},{40,10}}))); - - Buildings.Controls.Continuous.LimPID con( - final controllerType=Modelica.Blocks.Types.SimpleController.PID, - final k=k, - final Td=Td, - final yMax=yMax, - final yMin=yMin, - final Ti=Ti, - final wp=wp, - final wd=wd, - final Ni=Ni, - final Nd=Nd, - final initType=Modelica.Blocks.Types.InitPID.InitialOutput, - final xi_start=xi_start, - final xd_start=xd_start, - final y_start=yCon_start, - final reverseAction=reverseAction) "Controller" - annotation (Placement(transformation(extent={{-90,-10},{-70,10}}))); - - Buildings.Fluid.Sensors.TemperatureTwoPort senTDisSup( - redeclare final package Medium = Medium, - final m_flow_nominal=m1_flow_nominal) - "District-side (primary) supply temperature sensor" - annotation (Placement(transformation(extent={{-90,50},{-70,70}}))); - - Buildings.Fluid.Sensors.TemperatureTwoPort senTDisRet( - redeclare final package Medium = Medium, - final m_flow_nominal=m1_flow_nominal) - "District-side (primary) return temperature sensor" - annotation (Placement(transformation(extent={{70,50},{90,70}}))); - - Modelica.Blocks.Continuous.Integrator int(k=1) "Integration" - annotation (Placement(transformation(extent={{60,120},{80,100}}))); - - Buildings.Fluid.Sensors.MassFlowRate senMasFlo( - redeclare package Medium = Medium) - annotation (Placement(transformation(extent={{-60,50},{-40,70}}))); - - Buildings.Fluid.Sensors.TemperatureTwoPort TBuiRet( - redeclare final package Medium = Medium, - final m_flow_nominal=m2_flow_nominal) - "Building-side (secondary) return temperature" - annotation (Placement(transformation(extent={{-70,-70},{-90,-50}}))); - - Buildings.Fluid.Actuators.Valves.TwoWayQuickOpening val( - redeclare final package Medium = Medium, - final m_flow_nominal=m1_flow_nominal, - final dpValve_nominal=dpValve_nominal, - riseTime(displayUnit="s") = 60, - y_start=0) "District-side (primary) control valve" - annotation (Placement(transformation(extent={{-30,70},{-10,50}}))); - - Modelica.Blocks.Math.Gain cp(final k=cp_default) - "Specifc heat multiplier to calculate heat flow rate" - annotation (Placement(transformation(extent={{20,100},{40,120}}))); - - Modelica.Blocks.Math.Product pro "Product" - annotation (Placement(transformation(extent={{-20,100},{0,120}}))); - - Modelica.Blocks.Math.Add dTDis(k1=-1, k2=+1) - "Temperatur difference on the district side" - annotation (Placement(transformation(extent={{-60,106},{-40,126}}))); - -protected - final parameter Medium.ThermodynamicState sta_default = Medium.setState_pTX( - T=Medium.T_default, - p=Medium.p_default, - X=Medium.X_default) "Medium state at default properties"; - final parameter Modelica.SIunits.SpecificHeatCapacity cp_default= - Medium.specificHeatCapacityCp(sta_default) - "Specific heat capacity of the fluid"; - -equation - - connect(hex.port_a2, port_a2) annotation (Line(points={{40,-6},{60,-6},{60,-60}, - {100,-60}}, color={0,127,255})); - connect(hex.port_b1, senTDisRet.port_a) annotation (Line(points={{40,6},{60,6}, - {60,60},{70,60}}, color={0,127,255})); - connect(val.port_b, hex.port_a1) annotation (Line(points={{-10,60},{0,60},{0,6}, - {20,6}}, color={0,127,255})); - connect(senMasFlo.port_b, val.port_a) - annotation (Line(points={{-40,60},{-30,60}}, color={0,127,255})); - connect(port_a1, senTDisSup.port_a) - annotation (Line(points={{-100,60},{-90,60}}, color={0,127,255})); - connect(senTDisSup.port_b, senMasFlo.port_a) - annotation (Line(points={{-70,60},{-60,60}}, color={0,127,255})); - connect(port_b2, TBuiRet.port_b) - annotation (Line(points={{-100,-60},{-90,-60}}, color={0,127,255})); - connect(senTDisRet.port_b, port_b1) - annotation (Line(points={{90,60},{100,60}}, color={0,127,255})); - connect(TSet, con.u_s) - annotation (Line(points={{-120,0},{-106,0},{-106,0},{-92,0}}, - color={0,0,127})); - connect(con.u_m, TBuiRet.T) - annotation (Line(points={{-80,-12},{-80,-49}}, color={0,0,127})); - connect(con.y, val.y) - annotation (Line(points={{-69,0},{-20,0},{-20,48}}, color={0,0,127})); - connect(TBuiRet.port_a, hex.port_b2) annotation (Line(points={{-70,-60},{0,-60}, - {0,-6},{20,-6}}, color={0,127,255})); - connect(pro.y, cp.u) - annotation (Line(points={{1,110},{18,110}}, color={0,0,127})); - connect(senMasFlo.m_flow, pro.u2) - annotation (Line(points={{-50,71},{-50,104},{-22,104}}, color={0,0,127})); - connect(senTDisSup.T, dTDis.u1) - annotation (Line(points={{-80,71},{-80,122},{-62,122}}, color={0,0,127})); - connect(senTDisRet.T, dTDis.u2) annotation (Line(points={{80,71},{80,80},{-70, - 80},{-70,110},{-62,110}}, color={0,0,127})); - connect(dTDis.y, pro.u1) - annotation (Line(points={{-39,116},{-22,116}}, color={0,0,127})); - connect(cp.y, int.u) - annotation (Line(points={{41,110},{58,110}}, color={0,0,127})); - connect(int.y, Q) - annotation (Line(points={{81,110},{110,110}}, color={0,0,127})); - connect(Q_flow, cp.y) annotation (Line(points={{110,150},{50,150},{50,110},{41, - 110}}, color={0,0,127})); - -annotation (defaultComponentName="coo", - Icon(coordinateSystem(preserveAspectRatio=false), graphics={ - Rectangle( - extent={{-100,-56},{100,-64}}, - fillColor={0,0,0}, - fillPattern=FillPattern.Solid, - pattern=LinePattern.None), - Rectangle( - extent={{-100,64},{100,56}}, - fillColor={0,0,0}, - fillPattern=FillPattern.Solid, - pattern=LinePattern.None), - Rectangle( - extent={{-80,80},{80,-80}}, - lineColor={175,175,175}, - fillColor={35,138,255}, - fillPattern=FillPattern.Solid), - Text( - extent={{-52,40},{54,-40}}, - lineColor={0,0,0}, - fillColor={35,138,255}, - fillPattern=FillPattern.Solid, - textStyle={TextStyle.Bold}, - textString="ETS")}), Diagram( - coordinateSystem(preserveAspectRatio=false, extent={{-100,-100},{100,160}})), - Documentation(info=" -

    -Indirect cooling energy transfer station (ETS) model that controls -the building chilled water supply temperature by modulating a -primary control valve on the district supply side. The design is -based on a typical district cooling ETS described in ASHRAE's - -District Cooling Guide. -As shown in the figure below, the building pumping design (constant, -variable) is specified on the building side, not within the ETS. -

    -

    -\"DHC.ETS.CoolingIndirect\"/ -

    -

    Reference

    -

    -American Society of Heating, Refrigeration and Air-Conditioning -Engineers. (2013). Chapter 5: End User Interface. In -District Cooling Guide. 1st Edition. -

    -", revisions=" -
      -
    • -November 1, 2019, by Kathryn Hinkelman:
      -First implementation.
    • -
    -")); -end CoolingIndirect; diff --git a/geojson_modelica_translator/model_connectors/ets_cooling_indirect_2.mo b/geojson_modelica_translator/model_connectors/ets_cooling_indirect_2.mo deleted file mode 100644 index 9d2b886e0..000000000 --- a/geojson_modelica_translator/model_connectors/ets_cooling_indirect_2.mo +++ /dev/null @@ -1,332 +0,0 @@ - -within Buildings.Applications.DHC.EnergyTransferStations; -model CoolingIndirect - - "Indirect cooling energy transfer station for district energy systems" - extends Buildings.Fluid.Interfaces.PartialFourPort( - redeclare final package Medium1 = Medium, - redeclare final package Medium2 = Medium); - - replaceable package Medium = - Modelica.Media.Interfaces.PartialMedium "Medium in the component"; - - // mass flow rates - parameter Modelica.SIunits.MassFlowRate m1_flow_nominal( - final min=0, - start=0.666) - "Nominal mass flow rate of primary (district) district cooling side"; - - // Heat exchanger - parameter Modelica.SIunits.PressureDifference dp1_nominal( - final min=0, - start=999, - final displayUnit="Pa") - "Nominal pressure difference on primary side" - annotation(Dialog(group="Heat exchanger")); - - parameter Modelica.SIunits.PressureDifference dp2_nominal( - final min=0, - start=999, - final displayUnit="Pa") - "Nominal pressure difference on secondary side" - annotation(Dialog(group="Heat exchanger")); - - parameter Boolean use_Q_flow_nominal=true - "Set to true to specify Q_flow_nominal and temperatures, or to false to specify effectiveness" - annotation(Dialog(group="Heat exchanger")); - - parameter Modelica.SIunits.HeatFlowRate Q_flow_nominal( - final min=0, - start=8000) - "Nominal heat transfer" - annotation(Dialog(group="Heat exchanger")); - - parameter Modelica.SIunits.Temperature T_a1_nominal( - min=0+273, - max=100+273.15, - start=5+273.15, - final displayUnit="K") - "Nominal temperature at port a1" - annotation(Dialog(group="Heat exchanger")); - - parameter Modelica.SIunits.Temperature T_a2_nominal( - min=0+273, - max=100+273.15, - start=7+273.15, - final displayUnit="K") - "Nominal temperature at port a2" - annotation(Dialog(group="Heat exchanger")); - - parameter Modelica.SIunits.Efficiency eta( - final min=0, - final max=1)=0.666 - "Constant effectiveness" - annotation(Dialog(group="Heat exchanger")); - - // Controller parameters - parameter Modelica.Blocks.Types.SimpleController controllerType= - Modelica.Blocks.Types.SimpleController.PI - "Type of controller" - annotation(Dialog(tab="Controller")); - parameter Real k(final min=0, final unit="1") = 1 - "Gain of controller" - annotation(Dialog(tab="Controller")); - parameter Modelica.SIunits.Time Ti( - min=Modelica.Constants.small)=120 - "Time constant of integrator block" - annotation (Dialog(tab="Controller", enable= - controllerType == Modelica.Blocks.Types.SimpleController.PI or - controllerType == Modelica.Blocks.Types.SimpleController.PID)); - parameter Modelica.SIunits.Time Td(final min=0)=0.1 - "Time constant of derivative block" - annotation (Dialog(tab="Controller", enable= - controllerType == Modelica.Blocks.Types.SimpleController.PD or - controllerType == Modelica.Blocks.Types.SimpleController.PID)); - parameter Real yMax(start=1)=1 - "Upper limit of output" - annotation(Dialog(tab="Controller")); - parameter Real yMin=0 - "Lower limit of output" - annotation(Dialog(tab="Controller")); - parameter Real wp(final min=0) = 1 - "Set-point weight for Proportional block (0..1)" - annotation(Dialog(tab="Controller")); - parameter Real wd(final min=0) = 0 - "Set-point weight for Derivative block (0..1)" - annotation(Dialog(tab="Controller", enable= - controllerType==Modelica.Blocks.Types.SimpleController.PD or - controllerType==Modelica.Blocks.Types.SimpleController.PID)); - parameter Real Ni(min=100*Modelica.Constants.eps) = 0.9 - "Ni*Ti is time constant of anti-windup compensation" - annotation(Dialog(tab="Controller", enable= - controllerType==Modelica.Blocks.Types.SimpleController.PI or - controllerType==Modelica.Blocks.Types.SimpleController.PID)); - parameter Real Nd(min=100*Modelica.Constants.eps) = 10 - "The higher Nd, the more ideal the derivative block" - annotation(Dialog(tab="Controller", enable= - controllerType==Modelica.Blocks.Types.SimpleController.PD or - controllerType==Modelica.Blocks.Types.SimpleController.PID)); - parameter Modelica.Blocks.Types.InitPID initType= - Modelica.Blocks.Types.InitPID.DoNotUse_InitialIntegratorState - "Type of initialization (1: no init, 2: steady state, 3: initial state, 4: initial output)" - annotation(Evaluate=true, Dialog(group="Initialization", tab="Controller")); - parameter Real xi_start=0 - "Initial or guess value value for integrator output (= integrator state)" - annotation (Dialog(group="Initialization", tab="Controller", - enable=controllerType==Modelica.Blocks.Types.SimpleController.PI or - controllerType==Modelica.Blocks.Types.SimpleController.PID)); - parameter Real xd_start=0 - "Initial or guess value for state of derivative block" - annotation (Dialog(group="Initialization", tab="Controller", - enable=controllerType==Modelica.Blocks.Types.SimpleController.PD or - controllerType==Modelica.Blocks.Types.SimpleController.PID)); - parameter Real yCon_start=0 - "Initial value of output from the controller" - annotation(Dialog(group="Initialization", tab="Controller", - enable=initType == Modelica.Blocks.Types.InitPID.InitialOutput)); - parameter Boolean reverseAction = true - "Set to true for throttling the water flow rate through a cooling coil controller" - annotation(Dialog(tab="Controller")); - - Modelica.Blocks.Interfaces.RealInput TSet( - final quantity="ThermodynamicTemperature", - final unit="K") - "Setpoint temperature" - annotation (Placement(transformation(extent={{-140,-20},{-100,20}}))); - - Modelica.Blocks.Interfaces.RealOutput Q_flow( - final quantity="Power", - final unit="W", - final displayUnit="kW") - "Measured power demand at the ETS" - annotation (Placement(transformation(extent={{100,140},{120,160}}))); - - Modelica.Blocks.Interfaces.RealOutput Q( - final quantity="Energy", - final unit="J", - final displayUnit="kWh") - "Measured energy consumption at the ETS" - annotation (Placement(transformation(extent={{100,100},{120,120}}))); - - Buildings.Fluid.HeatExchangers.PlateHeatExchangerEffectivenessNTU hex( - redeclare final package Medium1 = Medium, - redeclare final package Medium2 = Medium, - final m1_flow_nominal=m1_flow_nominal, - final m2_flow_nominal=m2_flow_nominal, - final dp1_nominal=dp1_nominal, - final dp2_nominal=dp2_nominal, - final configuration=Buildings.Fluid.Types.HeatExchangerConfiguration.CounterFlow, - final use_Q_flow_nominal=true, - final Q_flow_nominal=Q_flow_nominal, - final T_a1_nominal=T_a1_nominal, - final T_a2_nominal=T_a2_nominal) "Indirect cooling heat exchanger" - annotation (Placement(transformation(extent={{20,-10},{40,10}}))); - - Buildings.Controls.Continuous.LimPID con( - final controllerType=Modelica.Blocks.Types.SimpleController.PID, - final k=k, - final Td=Td, - final yMax=yMax, - final yMin=yMin, - final Ti=Ti, - final wp=wp, - final wd=wd, - final Ni=Ni, - final Nd=Nd, - final initType=Modelica.Blocks.Types.InitPID.InitialOutput, - final xi_start=xi_start, - final xd_start=xd_start, - final y_start=yCon_start, - final reverseAction=reverseAction) "Controller" - annotation (Placement(transformation(extent={{-90,-10},{-70,10}}))); - - Buildings.Fluid.Sensors.TemperatureTwoPort senTDisSup( - redeclare final package Medium = Medium, - final m_flow_nominal=m1_flow_nominal) - "District-side (primary) supply temperature sensor" - annotation (Placement(transformation(extent={{-90,50},{-70,70}}))); - - Buildings.Fluid.Sensors.TemperatureTwoPort senTDisRet( - redeclare final package Medium = Medium, - final m_flow_nominal=m1_flow_nominal) - "District-side (primary) return temperature sensor" - annotation (Placement(transformation(extent={{70,50},{90,70}}))); - - Modelica.Blocks.Continuous.Integrator int(k=1) "Integration" - annotation (Placement(transformation(extent={{60,120},{80,100}}))); - - Buildings.Fluid.Sensors.MassFlowRate senMasFlo( - redeclare package Medium = Medium) - annotation (Placement(transformation(extent={{-60,50},{-40,70}}))); - - Buildings.Fluid.Sensors.TemperatureTwoPort TBuiRet( - redeclare final package Medium = Medium, - final m_flow_nominal=m2_flow_nominal) - "Building-side (secondary) return temperature" - annotation (Placement(transformation(extent={{-70,-70},{-90,-50}}))); - - Buildings.Fluid.Actuators.Valves.TwoWayQuickOpening val( - redeclare final package Medium = Medium, - final m_flow_nominal=m1_flow_nominal, - final dpValve_nominal=dpValve_nominal, - riseTime(displayUnit="s") = 60, - y_start=0) "District-side (primary) control valve" - annotation (Placement(transformation(extent={{-30,70},{-10,50}}))); - - Modelica.Blocks.Math.Gain cp(final k=cp_default) - "Specifc heat multiplier to calculate heat flow rate" - annotation (Placement(transformation(extent={{20,100},{40,120}}))); - - Modelica.Blocks.Math.Product pro "Product" - annotation (Placement(transformation(extent={{-20,100},{0,120}}))); - - Modelica.Blocks.Math.Add dTDis(k1=-1, k2=+1) - "Temperatur difference on the district side" - annotation (Placement(transformation(extent={{-60,106},{-40,126}}))); - -protected - final parameter Medium.ThermodynamicState sta_default = Medium.setState_pTX( - T=Medium.T_default, - p=Medium.p_default, - X=Medium.X_default) "Medium state at default properties"; - final parameter Modelica.SIunits.SpecificHeatCapacity cp_default= - Medium.specificHeatCapacityCp(sta_default) - "Specific heat capacity of the fluid"; - -equation - - connect(hex.port_a2, port_a2) annotation (Line(points={{40,-6},{60,-6},{60,-60}, - {100,-60}}, color={0,127,255})); - connect(hex.port_b1, senTDisRet.port_a) annotation (Line(points={{40,6},{60,6}, - {60,60},{70,60}}, color={0,127,255})); - connect(val.port_b, hex.port_a1) annotation (Line(points={{-10,60},{0,60},{0,6}, - {20,6}}, color={0,127,255})); - connect(senMasFlo.port_b, val.port_a) - annotation (Line(points={{-40,60},{-30,60}}, color={0,127,255})); - connect(port_a1, senTDisSup.port_a) - annotation (Line(points={{-100,60},{-90,60}}, color={0,127,255})); - connect(senTDisSup.port_b, senMasFlo.port_a) - annotation (Line(points={{-70,60},{-60,60}}, color={0,127,255})); - connect(port_b2, TBuiRet.port_b) - annotation (Line(points={{-100,-60},{-90,-60}}, color={0,127,255})); - connect(senTDisRet.port_b, port_b1) - annotation (Line(points={{90,60},{100,60}}, color={0,127,255})); - connect(TSet, con.u_s) - annotation (Line(points={{-120,0},{-106,0},{-106,0},{-92,0}}, - color={0,0,127})); - connect(con.u_m, TBuiRet.T) - annotation (Line(points={{-80,-12},{-80,-49}}, color={0,0,127})); - connect(con.y, val.y) - annotation (Line(points={{-69,0},{-20,0},{-20,48}}, color={0,0,127})); - connect(TBuiRet.port_a, hex.port_b2) annotation (Line(points={{-70,-60},{0,-60}, - {0,-6},{20,-6}}, color={0,127,255})); - connect(pro.y, cp.u) - annotation (Line(points={{1,110},{18,110}}, color={0,0,127})); - connect(senMasFlo.m_flow, pro.u2) - annotation (Line(points={{-50,71},{-50,104},{-22,104}}, color={0,0,127})); - connect(senTDisSup.T, dTDis.u1) - annotation (Line(points={{-80,71},{-80,122},{-62,122}}, color={0,0,127})); - connect(senTDisRet.T, dTDis.u2) annotation (Line(points={{80,71},{80,80},{-70, - 80},{-70,110},{-62,110}}, color={0,0,127})); - connect(dTDis.y, pro.u1) - annotation (Line(points={{-39,116},{-22,116}}, color={0,0,127})); - connect(cp.y, int.u) - annotation (Line(points={{41,110},{58,110}}, color={0,0,127})); - connect(int.y, Q) - annotation (Line(points={{81,110},{110,110}}, color={0,0,127})); - connect(Q_flow, cp.y) annotation (Line(points={{110,150},{50,150},{50,110},{41, - 110}}, color={0,0,127})); - -annotation (defaultComponentName="coo", - Icon(coordinateSystem(preserveAspectRatio=false), graphics={ - Rectangle( - extent={{-100,-56},{100,-64}}, - fillColor={0,0,0}, - fillPattern=FillPattern.Solid, - pattern=LinePattern.None), - Rectangle( - extent={{-100,64},{100,56}}, - fillColor={0,0,0}, - fillPattern=FillPattern.Solid, - pattern=LinePattern.None), - Rectangle( - extent={{-80,80},{80,-80}}, - lineColor={175,175,175}, - fillColor={35,138,255}, - fillPattern=FillPattern.Solid), - Text( - extent={{-52,40},{54,-40}}, - lineColor={0,0,0}, - fillColor={35,138,255}, - fillPattern=FillPattern.Solid, - textStyle={TextStyle.Bold}, - textString="ETS")}), Diagram( - coordinateSystem(preserveAspectRatio=false, extent={{-100,-100},{100,160}})), - Documentation(info=" -

    -Indirect cooling energy transfer station (ETS) model that controls -the building chilled water supply temperature by modulating a -primary control valve on the district supply side. The design is -based on a typical district cooling ETS described in ASHRAE's - -District Cooling Guide. -As shown in the figure below, the building pumping design (constant, -variable) is specified on the building side, not within the ETS. -

    -

    -\"DHC.ETS.CoolingIndirect\"/ -

    -

    Reference

    -

    -American Society of Heating, Refrigeration and Air-Conditioning -Engineers. (2013). Chapter 5: End User Interface. In -District Cooling Guide. 1st Edition. -

    -", revisions=" -
      -
    • -November 1, 2019, by Kathryn Hinkelman:
      -First implementation.
    • -
    -")); -end CoolingIndirect; diff --git a/geojson_modelica_translator/model_connectors/ets_template.py b/geojson_modelica_translator/model_connectors/ets_template.py index d09c2871e..08d4883f8 100644 --- a/geojson_modelica_translator/model_connectors/ets_template.py +++ b/geojson_modelica_translator/model_connectors/ets_template.py @@ -66,6 +66,7 @@ def __init__(self, thermal_junction_properties_geojson, system_parameters_geojso self.directory_modelica_building = self.directory_modelica_building.replace("\\", "/") # go up two levels of directory, to get the path of tests folder for ets + # TODO: we shouldn't be writing to the test directory in this file, only in tests. directory_up_two_levels = os.path.abspath(os.path.join(__file__, "../../..")) self.directory_ets_templated = os.path.join( directory_up_two_levels + "/tests/output_ets" diff --git a/geojson_modelica_translator/modelica/ets_cooling_indirect_templated.mo b/geojson_modelica_translator/modelica/ets_cooling_indirect_templated.mo deleted file mode 100644 index a0fc03a3f..000000000 --- a/geojson_modelica_translator/modelica/ets_cooling_indirect_templated.mo +++ /dev/null @@ -1,369 +0,0 @@ - -within Buildings.Applications.DHC.EnergyTransferStations; - -model ets_cooling_indirect_templated - - - "Indirect cooling energy transfer station for district energy systems" - extends Buildings.Fluid.Interfaces.PartialFourPort( - redeclare final package Medium1 = Medium, - redeclare final package Medium2 = Medium); - - replaceable package Medium = - Modelica.Media.Interfaces.PartialMedium "Medium in the component"; - - - - // mass flow rates - parameter Modelica.SIunits.MassFlowRate m1_flow_nominal( - final min=0, - start=0.666) - "Nominal mass flow rate of primary (district) district cooling side"; - - - - parameter Modelica.SIunits.MassFlowRate m2_flow_nominal( - final min=0, - start=0.5) - "Nominal mass flow rate of secondary (building) district cooling side"; - - - - // Primary supply control valve - parameter Modelica.SIunits.PressureDifference dpValve_nominal( - final min=0, - final displayUnit="Pa")=888 - "Nominal pressure drop of fully open control valve"; - - - - // Heat exchanger - parameter Modelica.SIunits.PressureDifference dp1_nominal( - final min=0, - start=999, - final displayUnit="Pa") - "Nominal pressure difference on primary side" - annotation(Dialog(group="Heat exchanger")); - - - - parameter Modelica.SIunits.PressureDifference dp2_nominal( - final min=0, - start=999, - final displayUnit="Pa") - "Nominal pressure difference on secondary side" - annotation(Dialog(group="Heat exchanger")); - - - - parameter Boolean use_Q_flow_nominal=true - "Set to true to specify Q_flow_nominal and temperatures, or to false to specify effectiveness" - annotation(Dialog(group="Heat exchanger")); - - - - parameter Modelica.SIunits.HeatFlowRate Q_flow_nominal( - final min=0, - start=8000) - "Nominal heat transfer" - annotation(Dialog(group="Heat exchanger")); - - - - - parameter Modelica.SIunits.Temperature T_a1_nominal( - min=0+273, - max=100+273.15, - start=5+273.15, - final displayUnit="K") - "Nominal temperature at port a1" - annotation(Dialog(group="Heat exchanger")); - - - - parameter Modelica.SIunits.Temperature T_a2_nominal( - min=0+273, - max=100+273.15, - start=7+273.15, - final displayUnit="K") - "Nominal temperature at port a2" - annotation(Dialog(group="Heat exchanger")); - - - - parameter Modelica.SIunits.Efficiency eta( - final min=0, - final max=1)=0.666 - "Constant effectiveness" - annotation(Dialog(group="Heat exchanger")); - - - - // Controller parameters - parameter Modelica.Blocks.Types.SimpleController controllerType= - Modelica.Blocks.Types.SimpleController.PI - "Type of controller" - annotation(Dialog(tab="Controller")); - parameter Real k(final min=0, final unit="1") = 1 - "Gain of controller" - annotation(Dialog(tab="Controller")); - parameter Modelica.SIunits.Time Ti( - min=Modelica.Constants.small)=120 - "Time constant of integrator block" - annotation (Dialog(tab="Controller", enable= - controllerType == Modelica.Blocks.Types.SimpleController.PI or - controllerType == Modelica.Blocks.Types.SimpleController.PID)); - parameter Modelica.SIunits.Time Td(final min=0)=0.1 - "Time constant of derivative block" - annotation (Dialog(tab="Controller", enable= - controllerType == Modelica.Blocks.Types.SimpleController.PD or - controllerType == Modelica.Blocks.Types.SimpleController.PID)); - parameter Real yMax(start=1)=1 - "Upper limit of output" - annotation(Dialog(tab="Controller")); - parameter Real yMin=0 - "Lower limit of output" - annotation(Dialog(tab="Controller")); - parameter Real wp(final min=0) = 1 - "Set-point weight for Proportional block (0..1)" - annotation(Dialog(tab="Controller")); - parameter Real wd(final min=0) = 0 - "Set-point weight for Derivative block (0..1)" - annotation(Dialog(tab="Controller", enable= - controllerType==Modelica.Blocks.Types.SimpleController.PD or - controllerType==Modelica.Blocks.Types.SimpleController.PID)); - parameter Real Ni(min=100*Modelica.Constants.eps) = 0.9 - "Ni*Ti is time constant of anti-windup compensation" - annotation(Dialog(tab="Controller", enable= - controllerType==Modelica.Blocks.Types.SimpleController.PI or - controllerType==Modelica.Blocks.Types.SimpleController.PID)); - parameter Real Nd(min=100*Modelica.Constants.eps) = 10 - "The higher Nd, the more ideal the derivative block" - annotation(Dialog(tab="Controller", enable= - controllerType==Modelica.Blocks.Types.SimpleController.PD or - controllerType==Modelica.Blocks.Types.SimpleController.PID)); - parameter Modelica.Blocks.Types.InitPID initType= - Modelica.Blocks.Types.InitPID.DoNotUse_InitialIntegratorState - "Type of initialization (1: no init, 2: steady state, 3: initial state, 4: initial output)" - annotation(Evaluate=true, Dialog(group="Initialization", tab="Controller")); - parameter Real xi_start=0 - "Initial or guess value value for integrator output (= integrator state)" - annotation (Dialog(group="Initialization", tab="Controller", - enable=controllerType==Modelica.Blocks.Types.SimpleController.PI or - controllerType==Modelica.Blocks.Types.SimpleController.PID)); - parameter Real xd_start=0 - "Initial or guess value for state of derivative block" - annotation (Dialog(group="Initialization", tab="Controller", - enable=controllerType==Modelica.Blocks.Types.SimpleController.PD or - controllerType==Modelica.Blocks.Types.SimpleController.PID)); - parameter Real yCon_start=0 - "Initial value of output from the controller" - annotation(Dialog(group="Initialization", tab="Controller", - enable=initType == Modelica.Blocks.Types.InitPID.InitialOutput)); - parameter Boolean reverseAction = true - "Set to true for throttling the water flow rate through a cooling coil controller" - annotation(Dialog(tab="Controller")); - - Modelica.Blocks.Interfaces.RealInput TSet( - final quantity="ThermodynamicTemperature", - final unit="K") - "Setpoint temperature" - annotation (Placement(transformation(extent={{-140,-20},{-100,20}}))); - - Modelica.Blocks.Interfaces.RealOutput Q_flow( - final quantity="Power", - final unit="W", - final displayUnit="kW") - "Measured power demand at the ETS" - annotation (Placement(transformation(extent={{100,140},{120,160}}))); - - Modelica.Blocks.Interfaces.RealOutput Q( - final quantity="Energy", - final unit="J", - final displayUnit="kWh") - "Measured energy consumption at the ETS" - annotation (Placement(transformation(extent={{100,100},{120,120}}))); - - Buildings.Fluid.HeatExchangers.PlateHeatExchangerEffectivenessNTU hex( - redeclare final package Medium1 = Medium, - redeclare final package Medium2 = Medium, - final m1_flow_nominal=m1_flow_nominal, - final m2_flow_nominal=m2_flow_nominal, - final dp1_nominal=dp1_nominal, - final dp2_nominal=dp2_nominal, - final configuration=Buildings.Fluid.Types.HeatExchangerConfiguration.CounterFlow, - final use_Q_flow_nominal=true, - final Q_flow_nominal=Q_flow_nominal, - final T_a1_nominal=T_a1_nominal, - final T_a2_nominal=T_a2_nominal) "Indirect cooling heat exchanger" - annotation (Placement(transformation(extent={{20,-10},{40,10}}))); - - Buildings.Controls.Continuous.LimPID con( - final controllerType=Modelica.Blocks.Types.SimpleController.PID, - final k=k, - final Td=Td, - final yMax=yMax, - final yMin=yMin, - final Ti=Ti, - final wp=wp, - final wd=wd, - final Ni=Ni, - final Nd=Nd, - final initType=Modelica.Blocks.Types.InitPID.InitialOutput, - final xi_start=xi_start, - final xd_start=xd_start, - final y_start=yCon_start, - final reverseAction=reverseAction) "Controller" - annotation (Placement(transformation(extent={{-90,-10},{-70,10}}))); - - Buildings.Fluid.Sensors.TemperatureTwoPort senTDisSup( - redeclare final package Medium = Medium, - final m_flow_nominal=m1_flow_nominal) - "District-side (primary) supply temperature sensor" - annotation (Placement(transformation(extent={{-90,50},{-70,70}}))); - - Buildings.Fluid.Sensors.TemperatureTwoPort senTDisRet( - redeclare final package Medium = Medium, - final m_flow_nominal=m1_flow_nominal) - "District-side (primary) return temperature sensor" - annotation (Placement(transformation(extent={{70,50},{90,70}}))); - - Modelica.Blocks.Continuous.Integrator int(k=1) "Integration" - annotation (Placement(transformation(extent={{60,120},{80,100}}))); - - Buildings.Fluid.Sensors.MassFlowRate senMasFlo( - redeclare package Medium = Medium) - annotation (Placement(transformation(extent={{-60,50},{-40,70}}))); - - Buildings.Fluid.Sensors.TemperatureTwoPort TBuiRet( - redeclare final package Medium = Medium, - final m_flow_nominal=m2_flow_nominal) - "Building-side (secondary) return temperature" - annotation (Placement(transformation(extent={{-70,-70},{-90,-50}}))); - - Buildings.Fluid.Actuators.Valves.TwoWayQuickOpening val( - redeclare final package Medium = Medium, - final m_flow_nominal=m1_flow_nominal, - final dpValve_nominal=dpValve_nominal, - riseTime(displayUnit="s") = 60, - y_start=0) "District-side (primary) control valve" - annotation (Placement(transformation(extent={{-30,70},{-10,50}}))); - - Modelica.Blocks.Math.Gain cp(final k=cp_default) - "Specifc heat multiplier to calculate heat flow rate" - annotation (Placement(transformation(extent={{20,100},{40,120}}))); - - Modelica.Blocks.Math.Product pro "Product" - annotation (Placement(transformation(extent={{-20,100},{0,120}}))); - - Modelica.Blocks.Math.Add dTDis(k1=-1, k2=+1) - "Temperatur difference on the district side" - annotation (Placement(transformation(extent={{-60,106},{-40,126}}))); - -protected - final parameter Medium.ThermodynamicState sta_default = Medium.setState_pTX( - T=Medium.T_default, - p=Medium.p_default, - X=Medium.X_default) "Medium state at default properties"; - final parameter Modelica.SIunits.SpecificHeatCapacity cp_default= - Medium.specificHeatCapacityCp(sta_default) - "Specific heat capacity of the fluid"; - -equation - - connect(hex.port_a2, port_a2) annotation (Line(points={{40,-6},{60,-6},{60,-60}, - {100,-60}}, color={0,127,255})); - connect(hex.port_b1, senTDisRet.port_a) annotation (Line(points={{40,6},{60,6}, - {60,60},{70,60}}, color={0,127,255})); - connect(val.port_b, hex.port_a1) annotation (Line(points={{-10,60},{0,60},{0,6}, - {20,6}}, color={0,127,255})); - connect(senMasFlo.port_b, val.port_a) - annotation (Line(points={{-40,60},{-30,60}}, color={0,127,255})); - connect(port_a1, senTDisSup.port_a) - annotation (Line(points={{-100,60},{-90,60}}, color={0,127,255})); - connect(senTDisSup.port_b, senMasFlo.port_a) - annotation (Line(points={{-70,60},{-60,60}}, color={0,127,255})); - connect(port_b2, TBuiRet.port_b) - annotation (Line(points={{-100,-60},{-90,-60}}, color={0,127,255})); - connect(senTDisRet.port_b, port_b1) - annotation (Line(points={{90,60},{100,60}}, color={0,127,255})); - connect(TSet, con.u_s) - annotation (Line(points={{-120,0},{-106,0},{-106,0},{-92,0}}, - color={0,0,127})); - connect(con.u_m, TBuiRet.T) - annotation (Line(points={{-80,-12},{-80,-49}}, color={0,0,127})); - connect(con.y, val.y) - annotation (Line(points={{-69,0},{-20,0},{-20,48}}, color={0,0,127})); - connect(TBuiRet.port_a, hex.port_b2) annotation (Line(points={{-70,-60},{0,-60}, - {0,-6},{20,-6}}, color={0,127,255})); - connect(pro.y, cp.u) - annotation (Line(points={{1,110},{18,110}}, color={0,0,127})); - connect(senMasFlo.m_flow, pro.u2) - annotation (Line(points={{-50,71},{-50,104},{-22,104}}, color={0,0,127})); - connect(senTDisSup.T, dTDis.u1) - annotation (Line(points={{-80,71},{-80,122},{-62,122}}, color={0,0,127})); - connect(senTDisRet.T, dTDis.u2) annotation (Line(points={{80,71},{80,80},{-70, - 80},{-70,110},{-62,110}}, color={0,0,127})); - connect(dTDis.y, pro.u1) - annotation (Line(points={{-39,116},{-22,116}}, color={0,0,127})); - connect(cp.y, int.u) - annotation (Line(points={{41,110},{58,110}}, color={0,0,127})); - connect(int.y, Q) - annotation (Line(points={{81,110},{110,110}}, color={0,0,127})); - connect(Q_flow, cp.y) annotation (Line(points={{110,150},{50,150},{50,110},{41, - 110}}, color={0,0,127})); - -annotation (defaultComponentName="coo", - Icon(coordinateSystem(preserveAspectRatio=false), graphics={ - Rectangle( - extent={{-100,-56},{100,-64}}, - fillColor={0,0,0}, - fillPattern=FillPattern.Solid, - pattern=LinePattern.None), - Rectangle( - extent={{-100,64},{100,56}}, - fillColor={0,0,0}, - fillPattern=FillPattern.Solid, - pattern=LinePattern.None), - Rectangle( - extent={{-80,80},{80,-80}}, - lineColor={175,175,175}, - fillColor={35,138,255}, - fillPattern=FillPattern.Solid), - Text( - extent={{-52,40},{54,-40}}, - lineColor={0,0,0}, - fillColor={35,138,255}, - fillPattern=FillPattern.Solid, - textStyle={TextStyle.Bold}, - textString="ETS")}), Diagram( - coordinateSystem(preserveAspectRatio=false, extent={{-100,-100},{100,160}})), - Documentation(info=" -

    -Indirect cooling energy transfer station (ETS) model that controls -the building chilled water supply temperature by modulating a -primary control valve on the district supply side. The design is -based on a typical district cooling ETS described in ASHRAE's - -District Cooling Guide. -As shown in the figure below, the building pumping design (constant, -variable) is specified on the building side, not within the ETS. -

    -

    -\"DHC.ETS.CoolingIndirect\"/ -

    -

    Reference

    -

    -American Society of Heating, Refrigeration and Air-Conditioning -Engineers. (2013). Chapter 5: End User Interface. In -District Cooling Guide. 1st Edition. -

    -", revisions=" -
      -
    • -November 1, 2019, by Kathryn Hinkelman:
      -First implementation.
    • -
    • 12/15/2020, Yanfei Li, Templating ETS model.
    • -
    -")); -end ets_cooling_indirect_templated; From 81f54cf881b25c675c021b519e55c259bc2b35f9 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Tue, 24 Mar 2020 13:15:23 -0600 Subject: [PATCH 45/62] fixes to templates to enable running --- .../model_connectors/templates/spawn_building.mot | 4 ++-- .../model_connectors/templates/spawn_coupling.mot | 6 +++--- tests/model_connectors/test_spawn.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/geojson_modelica_translator/model_connectors/templates/spawn_building.mot b/geojson_modelica_translator/model_connectors/templates/spawn_building.mot index 94a067043..b7655e89c 100644 --- a/geojson_modelica_translator/model_connectors/templates/spawn_building.mot +++ b/geojson_modelica_translator/model_connectors/templates/spawn_building.mot @@ -1,5 +1,5 @@ within {{project_name}}.Loads.{{model_name}}; -model BuildingSpawnZ6 +model building "n-zone EnergyPlus building model based on URBANopt GeoJSON export, with distribution pumps" extends Buildings.Applications.DHC.Loads.BaseClasses.PartialBuilding( @@ -214,4 +214,4 @@ First implementation. ")); {% endraw %} -end BuildingSpawnZ6; +end building; diff --git a/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot b/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot index 5f288126a..5b0eefcbd 100644 --- a/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot +++ b/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot @@ -1,5 +1,5 @@ within {{project_name}}.Loads.{{model_name}}; -model CouplingSpawnZ6 +model coupling "FMU Template for Spawn" replaceable package MediumW = Buildings.Media.Water @@ -44,7 +44,7 @@ model CouplingSpawnZ6 "Chilled water flow rate" annotation (Placement(transformation(extent={{-80,-70},{-60,-50}}))); Buildings.Fluid.Sources.Boundary_pT sinHeaWat( - redeclare package Medium = Medium1, + redeclare package Medium = MediumW, p=300000, nPorts=1) "Sink for heating water" annotation (Placement(transformation( extent={{10,-10},{-10,10}}, @@ -132,4 +132,4 @@ First implementation. ")); {% endraw %} -end CouplingSpawnZ6; +end coupling; diff --git a/tests/model_connectors/test_spawn.py b/tests/model_connectors/test_spawn.py index e3ac4c02a..77aa0d42a 100644 --- a/tests/model_connectors/test_spawn.py +++ b/tests/model_connectors/test_spawn.py @@ -104,7 +104,7 @@ def test_spawn_to_modelica_and_run(self): # make sure the model can run using the ModelicaRunner class mr = ModelicaRunner() file_to_run = os.path.abspath( - 'tests/model_connectors/output/spawn_two_building/Loads/B5a6b99ec37f4de7f94020090/building.mo' + 'tests/model_connectors/output/spawn_two_building/Loads/B5a6b99ec37f4de7f94020090/coupling.mo' ) exitcode = mr.run_in_docker(file_to_run) self.assertEqual(0, exitcode) From 7dea56be7f3363739299a9f153c726f5f0b7e298 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Tue, 24 Mar 2020 17:47:17 -0600 Subject: [PATCH 46/62] refactor the scaffolding to be one level higher. Add in mos for spawn. --- .../geojson_modelica_translator.py | 54 ++++------- .../model_connectors/spawn.py | 50 +++++----- .../model_connectors/teaser.py | 56 +++++------ ...pawnBuilding.mot => RunSpawnBuilding.most} | 2 +- .../modelica/input_parser.py | 2 +- geojson_modelica_translator/scaffold.py | 95 +++++++++++++++++++ geojson_modelica_translator/utils.py | 45 +++++++-- tests/model_connectors/test_spawn.py | 46 ++++----- tests/modelica/test_input_parser.py | 2 - tests/test_scaffold.py | 49 ++++++++++ tests/test_translator.py | 8 +- tests/test_utils.py | 12 +-- 12 files changed, 278 insertions(+), 143 deletions(-) rename geojson_modelica_translator/model_connectors/templates/{RunSpawnBuilding.mot => RunSpawnBuilding.most} (97%) create mode 100644 geojson_modelica_translator/scaffold.py create mode 100644 tests/test_scaffold.py diff --git a/geojson_modelica_translator/geojson_modelica_translator.py b/geojson_modelica_translator/geojson_modelica_translator.py index 6d66705a3..62f04be9c 100644 --- a/geojson_modelica_translator/geojson_modelica_translator.py +++ b/geojson_modelica_translator/geojson_modelica_translator.py @@ -30,13 +30,12 @@ import logging import os -import shutil from geojson_modelica_translator.geojson.urbanopt_geojson import ( UrbanOptGeoJson ) from geojson_modelica_translator.modelica.input_parser import PackageParser -from geojson_modelica_translator.utils import ModelicaPath +from geojson_modelica_translator.scaffold import Scaffold _log = logging.getLogger(__name__) @@ -50,11 +49,7 @@ def __init__(self): self.buildings = [] # directory name member variables. These are set in the scaffold_directory method - self.loads_path = None - self.substations_path = None - self.plants_path = None - self.districts_path = None - + self.scaffold = None self.system_parameters = None @classmethod @@ -83,30 +78,19 @@ def set_system_parameters(self, sys_params): """ self.system_parameters = sys_params - def scaffold_directory(self, root_dir, overwrite=False): + def scaffold_directory(self, root_dir, project_name, overwrite=False): """ Scaffold out the initial directory and set various helper directories - :param root_dir: string, absolute path where to save results - :return: bool, did the directory get scaffolded + :param root_dir: string, absolute path where the project will be scaffolded. + :param project_name: string, name of the project that is being created + :return: string, path to the scaffold directory """ - # TODO: need to be careful with this. If we are mixing load models, then we need to not remove the entire path - if os.path.exists(root_dir): - if overwrite: - raise Exception( - "Directory already exists and overwrite is false for %s" % root_dir - ) - else: - shutil.rmtree(root_dir) - - self.loads_path = ModelicaPath("Loads", root_dir=root_dir) - self.substations_path = ModelicaPath("Substations", root_dir=root_dir) - self.plants_path = ModelicaPath("Plants", root_dir=root_dir) - self.districts_path = ModelicaPath("Districts", root_dir=root_dir) - - def to_modelica( - self, project_name, save_dir, model_connector_str="TeaserConnector" - ): + self.scaffold = Scaffold(root_dir, project_name) + self.scaffold.create() + return self.scaffold.project_path + + def to_modelica(self, project_name, save_dir, model_connector_str="TeaserConnector"): """ Convert the data in the GeoJSON to modelica based-objects @@ -114,12 +98,10 @@ def to_modelica( {save_dir}/{project_name} :param model_connector_str: str, which model_connector to use """ - prj_dir = f"{save_dir}/{project_name}" - self.scaffold_directory(prj_dir) + self.scaffold_directory(save_dir, project_name) - # TODO: Handle other connectors -- create map based on model_connector_str + # TODO: Create a map to load the required model_connectors import geojson_modelica_translator.model_connectors.teaser - import geojson_modelica_translator.model_connectors.spawn mc_klass = getattr(geojson_modelica_translator.model_connectors.teaser, model_connector_str) @@ -131,17 +113,17 @@ def to_modelica( model_connector.add_building(building) - _log.info(f"Translating building to model {building}") - model_connector.to_modelica( - project_name, self.loads_path.files_dir, keep_original_models=False - ) + _log.info(f"Translating building to model {building}") + model_connector.to_modelica(self.scaffold, keep_original_models=False) # add in Substations + # TODO: YL, where are the substations/ETSs? + # add in Districts # add in Plants # now add in the top level package. - pp = PackageParser.new_from_template(prj_dir, project_name, ["Loads"]) + pp = PackageParser.new_from_template(self.scaffold.project_path, project_name, ["Loads"]) pp.save() # TODO: BuildingModelClass diff --git a/geojson_modelica_translator/model_connectors/spawn.py b/geojson_modelica_translator/model_connectors/spawn.py index e01f9f050..541d2c41a 100644 --- a/geojson_modelica_translator/model_connectors/spawn.py +++ b/geojson_modelica_translator/model_connectors/spawn.py @@ -60,6 +60,8 @@ def add_building(self, urbanopt_building, mapper=None): # TODO: Need to convert units, these should exist on the urbanopt_building object # TODO: Abstract out the GeoJSON functionality if mapper is None: + number_stories = urbanopt_building.feature.properties["number_of_stories"] + number_stories_above_ground = urbanopt_building.feature.properties["number_of_stories_above_ground"] self.buildings.append( { "area": urbanopt_building.feature.properties["floor_area"] * 0.092936, # ft2 -> m2 @@ -67,24 +69,21 @@ def add_building(self, urbanopt_building, mapper=None): "building_type": urbanopt_building.feature.properties["building_type"], "floor_height": urbanopt_building.feature.properties["height"] * 0.3048, # ft -> m "num_stories": urbanopt_building.feature.properties["number_of_stories_above_ground"], - "num_stories_below_grade": urbanopt_building.feature.properties["number_of_stories"] - - urbanopt_building.feature.properties["number_of_stories_above_ground"], + "num_stories_below_grade": number_stories - number_stories_above_ground, "year_built": urbanopt_building.feature.properties["year_built"], } ) - def to_modelica(self, project_name, root_building_dir): + def to_modelica(self, scaffold): """ Create spawn models based on the data in the buildings and geojsons - :param project_name: str, Name of the overall project that will be set in the modelica file - :param root_building_dir: str, root directory where building model will be exported + :param scaffold: Scaffold object, Scaffold of the entire directory of the project. """ curdir = os.getcwd() - loads_root_path = os.path.join(root_building_dir, project_name, "Loads") spawn_coupling_template = self.template_env.get_template("spawn_coupling.mot") spawn_building_template = self.template_env.get_template("spawn_building.mot") - spawn_mos_template = self.template_env.get_template("RunSpawnBuilding.mot") + spawn_mos_template = self.template_env.get_template("RunSpawnBuilding.most") building_names = [] try: for building in self.buildings: @@ -94,7 +93,7 @@ def to_modelica(self, project_name, root_building_dir): # Path for building data building_names.append(f"B{building['building_id']}") b_modelica_path = ModelicaPath( - f"B{building['building_id']}", loads_root_path, True + f"B{building['building_id']}", scaffold.loads_path.files_dir, True ) # grab the data from the system_parameter file for this building id @@ -170,24 +169,25 @@ def to_modelica(self, project_name, root_building_dir): raise Exception( f"Missing MOS weather file for Spawn: {template_data['mos_weather']['mos_weather_filename']}") + # Run the templating file_data = spawn_building_template.render( - project_name=project_name, + project_name=scaffold.project_name, model_name=f"B{building['building_id']}", data=template_data, ) with open(os.path.join(os.path.join(b_modelica_path.files_dir, "building.mo")), "w") as f: f.write(file_data) - file_data = spawn_mos_template.render( - project_name=project_name, - model_name=f"B{building['building_id']}", - data=template_data, - ) - with open(os.path.join(os.path.join(b_modelica_path.resources_dir, "RunSpawnBuilding.mos")), "w") as f: + full_model_name = os.path.join( + scaffold.project_name, scaffold.loads_path.files_relative_dir, f"B{building['building_id']}", 'coupling').replace( + os.path.sep, '.') + + file_data = spawn_mos_template.render(full_model_name=full_model_name) + with open(os.path.join(os.path.join(b_modelica_path.scripts_dir, "RunSpawnBuilding.mos")), "w") as f: f.write(file_data) file_data = spawn_coupling_template.render( - project_name=project_name, + project_name=scaffold.project_name, model_name=f"B{building['building_id']}", data=template_data, ) @@ -198,9 +198,9 @@ def to_modelica(self, project_name, root_building_dir): os.chdir(curdir) # run post process to create the remaining project files for this building - self.post_process(project_name, root_building_dir, building_names) + self.post_process(scaffold, building_names) - def post_process(self, project_name, root_building_dir, building_names): + def post_process(self, scaffold, building_names): """ Cleanup the export of Spawn files into a format suitable for the district-based analysis. This includes the following: @@ -208,29 +208,27 @@ def post_process(self, project_name, root_building_dir, building_names): * Add a Loads project * Add a project level project - :param project_name: string, name of the project which will be used to set the package.mo file - :param root_building_dir: string, where the project will be ultimately saved + :param scaffold: Scaffold object, Scaffold of the entire directory of the project. :param building_names: list, names of the buildings that need to be cleaned up after export :return: None """ - loads_root_path = os.path.join(root_building_dir, project_name, "Loads") for b in building_names: - b_modelica_path = os.path.join(loads_root_path, b) + b_modelica_path = os.path.join(scaffold.loads_path.files_dir, b) new_package = PackageParser.new_from_template( - b_modelica_path, b, ["coupling", "building"], within=f"{project_name}.Loads" + b_modelica_path, b, ["coupling", "building"], within=f"{scaffold.project_name}.Loads" ) new_package.save() # now create the Loads level package. This (for now) will create the package without considering any existing # files in the Loads directory. package = PackageParser.new_from_template( - loads_root_path, "Loads", building_names, within=f"{project_name}" + scaffold.loads_path.files_dir, "Loads", building_names, within=f"{scaffold.project_name}" ) package.save() # now create the Package level package. This really needs to happen at the GeoJSON to modelica stage, but - # do it here for now for testing. + # do it here for now to aid in testing. pp = PackageParser.new_from_template( - os.path.join(root_building_dir, project_name), project_name, ["Loads"] + scaffold.loads_path.files_dir, scaffold.project_name, ["Loads"] ) pp.save() diff --git a/geojson_modelica_translator/model_connectors/teaser.py b/geojson_modelica_translator/model_connectors/teaser.py index 0f5a5542e..36f6b9bea 100644 --- a/geojson_modelica_translator/model_connectors/teaser.py +++ b/geojson_modelica_translator/model_connectors/teaser.py @@ -55,6 +55,8 @@ def add_building(self, urbanopt_building, mapper=None): # TODO: Need to convert units, these should exist on the urbanopt_building object # TODO: Abstract out the GeoJSON functionality if mapper is None: + number_stories = urbanopt_building.feature.properties["number_of_stories"] + number_stories_above_ground = urbanopt_building.feature.properties["number_of_stories_above_ground"] self.buildings.append( { "area": urbanopt_building.feature.properties["floor_area"] * 0.092936, # ft2 -> m2 @@ -62,8 +64,7 @@ def add_building(self, urbanopt_building, mapper=None): "building_type": urbanopt_building.feature.properties["building_type"], "floor_height": urbanopt_building.feature.properties["height"] * 0.3048, # ft -> m "num_stories": urbanopt_building.feature.properties["number_of_stories_above_ground"], - "num_stories_below_grade": urbanopt_building.feature.properties["number_of_stories"] - - urbanopt_building.feature.properties["number_of_stories_above_ground"], + "num_stories_below_grade": number_stories - number_stories_above_ground, "year_built": urbanopt_building.feature.properties["year_built"], } ) @@ -75,13 +76,12 @@ def lookup_building_type(self, building_type): # TODO: define these mappings 'office', 'institute', 'institute4', institute8' return "office" - def to_modelica(self, project_name, root_building_dir, keep_original_models=False): + def to_modelica(self, scaffold, keep_original_models=False): """ Save the TEASER representation of the buildings to the filesystem. The path will - be root_building_dir. + be scaffold.loads_path.files_dir. - :param project_name: str, Name of the overall project that will be set in the modelica file - :param root_building_dir: str, root directory where building model will be exported + :param scaffold: Scaffold object, contains all the paths of the project :param keep_original_models: boolean, whether or not to remove the models after exporting from Teaser """ @@ -115,26 +115,13 @@ def to_modelica(self, project_name, root_building_dir, keep_original_models=Fals # calculate the properties of all the buildings and export to the Buildings library prj.calc_all_buildings() - prj.export_ibpsa( - library="Buildings", path=os.path.join(curdir, root_building_dir) - ) + prj.export_ibpsa(library="Buildings", path=os.path.join(curdir, scaffold.loads_path.files_dir)) finally: os.chdir(curdir) - self.post_process( - project_name, - root_building_dir, - building_names, - keep_original_models=keep_original_models, - ) + self.post_process(scaffold, building_names, keep_original_models=keep_original_models) - def post_process( - self, - project_name, - root_building_dir, - building_names, - keep_original_models=False, - ): + def post_process(self, scaffold, building_names, keep_original_models=False): """ Cleanup the export of the TEASER files into a format suitable for the district-based analysis. This includes the following: @@ -156,20 +143,20 @@ def post_process( string_replace_list = [] # create a new modelica based path for the buildings # TODO: make this work at the toplevel, somehow. - b_modelica_path = ModelicaPath(f"B{b}", root_building_dir, True) + b_modelica_path = ModelicaPath(f"B{b}", scaffold.loads_path.files_dir, True) # copy over the entire model to the new location copytree( - os.path.join(root_building_dir, f"Project/B{b}/B{b}_Models"), + os.path.join(scaffold.loads_path.files_dir, f"Project/B{b}/B{b}_Models"), b_modelica_path.files_dir, ) # read in the package to apply the changes as they other files are processed # TODO: these should be linked, so a rename method should act across the model and the package.order - package = PackageParser(os.path.join(root_building_dir, f"B{b}")) + package = PackageParser(os.path.join(scaffold.loads_path.files_dir, f"B{b}")) # move the internal gains files to a new resources folder - mat_files = glob.glob(os.path.join(root_building_dir, f"B{b}/*.txt")) + mat_files = glob.glob(os.path.join(scaffold.loads_path.files_dir, f"B{b}/*.txt")) for f in mat_files: new_file_name = os.path.basename(f).replace(f"B{b}", "") os.rename(f, f"{b_modelica_path.resources_dir}/{new_file_name}") @@ -181,7 +168,7 @@ def post_process( ) # process each of the building models - mo_files = glob.glob(os.path.join(root_building_dir, f"B{b}/*.mo")) + mo_files = glob.glob(os.path.join(scaffold.loads_path.files_dir, f"B{b}/*.mo")) for f in mo_files: # ignore the package.mo file if os.path.basename(f) == "package.mo": @@ -191,7 +178,7 @@ def post_process( # previous paths and replace with the new one. # Make sure to update the names of any resources as well. - mofile.replace_within_string(f"{project_name}.Loads.B{b}") + mofile.replace_within_string(f"{scaffold.project_name}.Loads.B{b}") # remove ReaderTMY3 mofile.remove_object("ReaderTMY3") @@ -202,7 +189,8 @@ def post_process( # add heat port data = [ - "annotation (Placement(transformation(extent={{-10,90},{10,110}}), iconTransformation(extent={{-10,90},{10,110}})));" # noqa + "annotation (Placement(transformation(extent={{-10,90},{10,110}}), " + "iconTransformation(extent={{-10,90},{10,110}})));" ] mofile.add_model_object( "Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a", "port_a", data, @@ -254,25 +242,27 @@ def post_process( # Save as the new filename (without building ID) new_filename = os.path.join( - root_building_dir, f'B{b}/{os.path.basename(f).split("_")[1]}' + scaffold.loads_path.files_dir, f'B{b}/{os.path.basename(f).split("_")[1]}' ) mofile.save_as(new_filename) os.remove(f) # save the updated package.mo and package.order. new_package = PackageParser.new_from_template( - package.path, f"B{b}", package.order, within=f"{project_name}.Loads" + package.path, f"B{b}", package.order, within=f"{scaffold.project_name}.Loads" ) new_package.save() # remaining clean up tasks across the entire exported project if not keep_original_models: - shutil.rmtree(os.path.join(root_building_dir, "Project")) + shutil.rmtree(os.path.join(scaffold.loads_path.files_dir, "Project")) # now create the Loads level package. This (for now) will create the package without considering any existing # files in the Loads directory. # add in the silly 'B' before the building names package = PackageParser.new_from_template( - root_building_dir, "Loads", ["B" + b for b in building_names], within=f"{project_name}", + scaffold.loads_path.files_dir, + "Loads", ["B" + b for b in building_names], + within=f"{scaffold.project_name}" ) package.save() diff --git a/geojson_modelica_translator/model_connectors/templates/RunSpawnBuilding.mot b/geojson_modelica_translator/model_connectors/templates/RunSpawnBuilding.most similarity index 97% rename from geojson_modelica_translator/model_connectors/templates/RunSpawnBuilding.mot rename to geojson_modelica_translator/model_connectors/templates/RunSpawnBuilding.most index d10d679f2..9b673c3b3 100644 --- a/geojson_modelica_translator/model_connectors/templates/RunSpawnBuilding.mot +++ b/geojson_modelica_translator/model_connectors/templates/RunSpawnBuilding.most @@ -1,6 +1,6 @@ old_hidden_avoid_double_computation=Hidden.AvoidDoubleComputation; Hidden.AvoidDoubleComputation=true; -simulateModel("spawn_two_building.Loads.B5a6b99ec37f4de7f94020090.Examples.CouplingSpawnZ6", +simulateModel("{{full_model_name}}", method="cvode", tolerance=1e-6, numberOfIntervals=500, diff --git a/geojson_modelica_translator/modelica/input_parser.py b/geojson_modelica_translator/modelica/input_parser.py index 7df9aedb7..993ef831c 100644 --- a/geojson_modelica_translator/modelica/input_parser.py +++ b/geojson_modelica_translator/modelica/input_parser.py @@ -191,7 +191,7 @@ def parse_mo(self): raise Exception( f"Found other token '{t}' in '{self.modelica_filename}' that is not supported... \ cannot continue" - ) # noqa + ) # now store data that is in between these other blocks if current_block == "model": diff --git a/geojson_modelica_translator/scaffold.py b/geojson_modelica_translator/scaffold.py new file mode 100644 index 000000000..a876130e8 --- /dev/null +++ b/geojson_modelica_translator/scaffold.py @@ -0,0 +1,95 @@ +""" +**************************************************************************************************** +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted +provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions +and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this list of conditions +and the following disclaimer in the documentation and/or other materials provided with the +distribution. + +Neither the name of the copyright holder nor the names of its contributors may be used to endorse +or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**************************************************************************************************** +""" + +import logging +import os +import shutil + +from geojson_modelica_translator.utils import ModelicaPath + +_log = logging.getLogger(__name__) + + +class Scaffold(object): + """Scaffold to hold the entire directory structure for the project. The purpose of this class is to + allow a developer/user to easily access the various paths of the project without having to + manually strip/replace strings/filenames/paths/etc. + + The project structure where an URBANopt-Modelica analysis will occur follows a well + defined structure and includes multiple levels of nested directories, data files, and scripts. + + Presently, the scaffold stops at the loads, substation, plant, districts, scripts path and does not + create a list of all of the submodels (yet). + """ + + def __init__(self, root_dir, project_name, overwrite=False): + """Initialize the scaffold. This will clear out the directory if it already exists, so use this + with caution. + + :param root_dir: Directory where to create the scaffold + :param project_name: Name of the project to create (should contain no spaces) + :param overwrite: boolean, overwrite the project if it already exists? + """ + self.root_dir = root_dir + self.project_name = project_name + self.loads_path = None + self.substations_path = None + self.plants_path = None + self.districts_path = None + self.scripts_path = None + self.overwrite = overwrite + + # clear out the project path + self.project_path = os.path.join(self.root_dir, self.project_name) + print(self.project_path) + if os.path.exists(self.project_path): + if not self.overwrite: + raise Exception("Directory already exists and overwrite is false for %s" % self.project_path) + else: + shutil.rmtree(self.project_path) + + def create(self): + """run the scaffolding""" + + # leverage the ModelicaPath function + self.loads_path = ModelicaPath("Loads", root_dir=self.project_path, overwrite=self.overwrite) + self.substations_path = ModelicaPath("Substations", root_dir=self.project_path, overwrite=self.overwrite) + self.plants_path = ModelicaPath("Plants", root_dir=self.project_path, overwrite=self.overwrite) + self.districts_path = ModelicaPath("Districts", root_dir=self.project_path, overwrite=self.overwrite) + + def clear_or_create_path(self, path, overwrite=False): + if os.path.exists(path): + if not overwrite: + raise Exception("Directory already exists and overwrite is false for %s" % path) + else: + shutil.rmtree(path) + os.makedirs(path, exist_ok=True) + + return path diff --git a/geojson_modelica_translator/utils.py b/geojson_modelica_translator/utils.py index e1984ef05..cabb759a6 100644 --- a/geojson_modelica_translator/utils.py +++ b/geojson_modelica_translator/utils.py @@ -51,7 +51,7 @@ def copytree(src, dst, symlinks=False, ignore=None): class ModelicaPath(object): """ Class for storing Modelica paths. This allows the path to point to - the model directory and the resources directory. + the model directory, resources, and scripts directory. """ def __init__(self, name, root_dir, overwrite=False): @@ -62,17 +62,20 @@ def __init__(self, name, root_dir, overwrite=False): """ self.name = name self.root_dir = root_dir + self.overwrite = overwrite # create the directories if root_dir is not None: check_path = os.path.join(self.files_dir) - self.clear_path(check_path, overwrite=overwrite) + self.clear_or_create_path(check_path) check_path = os.path.join(self.resources_dir) - self.clear_path(check_path, overwrite=overwrite) + self.clear_or_create_path(check_path) + check_path = os.path.join(self.scripts_dir) + self.clear_or_create_path(check_path) - def clear_path(self, path, overwrite=False): + def clear_or_create_path(self, path): if os.path.exists(path): - if overwrite: + if not self.overwrite: raise Exception("Directory already exists and overwrite is false for %s" % path) else: shutil.rmtree(path) @@ -87,7 +90,7 @@ def files_dir(self): :return: string, path to where files (models) are stored, without trailing slash """ if self.root_dir is None: - return self.name + return self.files_relative_dir else: return os.path.join(self.root_dir, self.name) @@ -96,10 +99,25 @@ def resources_relative_dir(self): """ Return the relative resource directory instead of the full path. This is useful when replacing strings within modelica files which are relative to the package. - :return: + + :return: string, relative resource's data path """ return os.path.join("Resources", "Data", self.name) + @property + def scripts_relative_dir(self, platform='Dymola'): + """Return the scripts directory that is in the resources directory. This only returns the + relative directory and is useful when replacing string values within Modelica files. + + :return: string, relative scripts path + """ + return os.path.join("Resources", "Scripts", self.name, platform) + + @property + def files_relative_dir(self): + """Return the path to the files relative to the project name.""" + return os.path.join(self.name) + @property def resources_dir(self): """ @@ -112,3 +130,16 @@ def resources_dir(self): return self.resources_relative_dir else: return os.path.join(self.root_dir, self.resources_relative_dir) + + @property + def scripts_dir(self): + """ + Return the path to the scripts directory (in the resources dir) for the specified ModelicaPath. + This path does not include the trailing slash. + + :return: string, path to where scripts are stored, without trailing slash. + """ + if self.root_dir is None: + return self.scripts_relative_dir + else: + return os.path.join(self.root_dir, self.scripts_relative_dir) diff --git a/tests/model_connectors/test_spawn.py b/tests/model_connectors/test_spawn.py index 77aa0d42a..1bf3d756e 100644 --- a/tests/model_connectors/test_spawn.py +++ b/tests/model_connectors/test_spawn.py @@ -29,6 +29,7 @@ """ import os +import shutil import unittest from geojson_modelica_translator.geojson_modelica_translator import ( @@ -43,68 +44,63 @@ class SpawnModelConnectorSingleBuildingTest(unittest.TestCase): def setUp(self): - prj_dir = "tests/model_connectors/output/spawn_single" + root_dir, project_name = "tests/model_connectors/output", "spawn_single" + + if os.path.exists(os.path.join(root_dir, project_name)): + shutil.rmtree(os.path.join(root_dir, project_name)) # load in the example geojson with a single offie building filename = os.path.abspath("tests/model_connectors/data/spawn_geojson_ex1.json") - gj = GeoJsonModelicaTranslator.from_geojson(filename) - gj.scaffold_directory( - prj_dir - ) # use the GeoJson translator to scaffold out the directory + self.gj = GeoJsonModelicaTranslator.from_geojson(filename) + self.gj.scaffold_directory(root_dir, project_name) # use the GeoJson translator to scaffold out the directory # load system parameter data - filename = os.path.abspath( - "tests/model_connectors/data/spawn_system_params_ex1.json" - ) + filename = os.path.abspath("tests/model_connectors/data/spawn_system_params_ex1.json") sys_params = SystemParameters(filename) # now test the spawn connector (independent of the larger geojson translator self.spawn = SpawnConnector(sys_params) - for b in gj.buildings: + for b in self.gj.buildings: self.spawn.add_building(b) def test_spawn_init(self): self.assertIsNotNone(self.spawn) - self.assertEqual( - self.spawn.system_parameters.get_param("buildings.custom")[0]["load_model"], - "Spawn", - ) + self.assertEqual(self.spawn.system_parameters.get_param("buildings.custom")[0]["load_model"], "Spawn", ) def test_spawn_to_modelica(self): - self.spawn.to_modelica("spawn_single", "tests/model_connectors/output") + self.spawn.to_modelica(self.gj.scaffold) class SpawnModelConnectorTwoBuildingTest(unittest.TestCase): def setUp(self): - prj_dir = "tests/model_connectors/output/spawn_two_building" + root_dir, project_name = "tests/model_connectors/output", "spawn_two_building" + + if os.path.exists(os.path.join(root_dir, project_name)): + shutil.rmtree(os.path.join(root_dir, project_name)) # load in the example geojson with a single offie building filename = os.path.abspath("tests/model_connectors/data/spawn_geojson_ex2.json") - gj = GeoJsonModelicaTranslator.from_geojson(filename) - gj.scaffold_directory( - prj_dir - ) # use the GeoJson translator to scaffold out the directory + self.gj = GeoJsonModelicaTranslator.from_geojson(filename) + self.gj.scaffold_directory(root_dir, project_name) # use the GeoJson translator to scaffold out the directory # load system parameter data - filename = os.path.abspath( - "tests/model_connectors/data/spawn_system_params_ex2.json" - ) + filename = os.path.abspath("tests/model_connectors/data/spawn_system_params_ex2.json") sys_params = SystemParameters(filename) # now test the spawn connector (independent of the larger geojson translator self.spawn = SpawnConnector(sys_params) - for b in gj.buildings: + for b in self.gj.buildings: self.spawn.add_building(b) def test_spawn_to_modelica_and_run(self): - self.spawn.to_modelica('spawn_two_building', 'tests/model_connectors/output') + self.spawn.to_modelica(self.gj.scaffold) # make sure the model can run using the ModelicaRunner class mr = ModelicaRunner() file_to_run = os.path.abspath( - 'tests/model_connectors/output/spawn_two_building/Loads/B5a6b99ec37f4de7f94020090/coupling.mo' + os.path.join(self.gj.scaffold.loads_path.files_dir, 'B5a6b99ec37f4de7f94020090', 'coupling.mo') ) exitcode = mr.run_in_docker(file_to_run) self.assertEqual(0, exitcode) diff --git a/tests/modelica/test_input_parser.py b/tests/modelica/test_input_parser.py index c9fd1989c..7dca779ab 100644 --- a/tests/modelica/test_input_parser.py +++ b/tests/modelica/test_input_parser.py @@ -99,7 +99,6 @@ def test_rename_filename(self): 'Modelica.Blocks.Sources.CombiTimeTable', 'internalGains', 'modelica://Project/B5a6b99ec37f4de7f94020090/B5a6b99ec37f4de7f94020090_Models/InternalGains_B5a6b99ec37f4de7f94020090Floor.mat', # noqa - # noqa 'modelica://a/new/path.mat' ) f1.save_as(new_filename) @@ -118,7 +117,6 @@ def test_add_model_obj(self): f1 = InputParser(filename) data = [ 'annotation (Placement(transformation(extent={{-10,90},{10,110}}), iconTransformation(extent={{-10,90},{10,110}})));' # noqa - # noqa ] f1.add_model_object( "Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a", "port_a", data diff --git a/tests/test_scaffold.py b/tests/test_scaffold.py new file mode 100644 index 000000000..dad9d7fa8 --- /dev/null +++ b/tests/test_scaffold.py @@ -0,0 +1,49 @@ +""" +**************************************************************************************************** +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted +provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions +and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this list of conditions +and the following disclaimer in the documentation and/or other materials provided with the +distribution. + +Neither the name of the copyright holder nor the names of its contributors may be used to endorse +or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**************************************************************************************************** +""" + +import os +import unittest + +from geojson_modelica_translator.scaffold import Scaffold + + +class ScaffoldTest(unittest.TestCase): + def test_scaffold(self): + root_dir = os.path.abspath(os.path.join("tests", "output")) + scaffold = Scaffold(root_dir, "scaffold_01", overwrite=True) + scaffold.create() + # self.assertEqual(mp.resources_dir, os.path.join("Resources", "Data", "Loads")) + self.assertTrue( + os.path.exists(os.path.join(root_dir, "scaffold_01", "Resources", "Scripts", "Loads", "Dymola")) + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_translator.py b/tests/test_translator.py index 84af6af3a..c12a81e5c 100644 --- a/tests/test_translator.py +++ b/tests/test_translator.py @@ -83,7 +83,7 @@ def test_to_modelica_defaults(self): "Storage", ] building_paths = [ - os.path.join(gj.loads_path.files_dir, b.dirname) for b in gj.buildings + os.path.join(gj.scaffold.loads_path.files_dir, b.dirname) for b in gj.buildings ] path_checks = [ f"{os.path.sep.join(r)}.mo" @@ -106,7 +106,7 @@ def test_to_modelica_defaults(self): for resource_name in resource_names: # TEASER 0.7.2 used .txt for schedule files path = os.path.join( - gj.loads_path.files_dir, + gj.scaffold.loads_path.files_dir, "Resources", "Data", b.dirname, @@ -140,7 +140,7 @@ def test_to_modelica_rc_order_4(self): "Storage", ] building_paths = [ - os.path.join(gj.loads_path.files_dir, b.dirname) for b in gj.buildings + os.path.join(gj.scaffold.loads_path.files_dir, b.dirname) for b in gj.buildings ] path_checks = [ f"{os.path.sep.join(r)}.mo" @@ -162,7 +162,7 @@ def test_to_modelica_rc_order_4(self): for resource_name in resource_names: # TEASER 0.7.2 used .txt for schedule files path = os.path.join( - gj.loads_path.files_dir, + gj.scaffold.loads_path.files_dir, "Resources", "Data", b.dirname, diff --git a/tests/test_utils.py b/tests/test_utils.py index c88f2e9de..8bc8d487d 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -40,15 +40,11 @@ def test_properties(self): self.assertEqual(mp.files_dir, "Loads") self.assertEqual(mp.resources_dir, os.path.join("Resources", "Data", "Loads")) - def test_scaffold(self): - root_dir = os.path.abspath(os.path.join("tests", "output", "test_02")) - ModelicaPath("RandomContainer", root_dir) + def test_single_sub_resource(self): + root_dir = os.path.abspath(os.path.join("tests", "output", "modelica_path_01")) + ModelicaPath("RandomContainer", root_dir, overwrite=True) self.assertTrue(os.path.exists(os.path.join(root_dir, "RandomContainer"))) - self.assertTrue( - os.path.exists( - os.path.join(root_dir, "Resources", "Data", "RandomContainer") - ) - ) + self.assertTrue(os.path.exists(os.path.join(root_dir, "Resources", "Data", "RandomContainer"))) if __name__ == "__main__": From 4bd171953ed96334257bdc3395dc011d88ee534a Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Tue, 24 Mar 2020 17:52:48 -0600 Subject: [PATCH 47/62] flake8l --- geojson_modelica_translator/model_connectors/spawn.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/geojson_modelica_translator/model_connectors/spawn.py b/geojson_modelica_translator/model_connectors/spawn.py index 541d2c41a..31aadfdcf 100644 --- a/geojson_modelica_translator/model_connectors/spawn.py +++ b/geojson_modelica_translator/model_connectors/spawn.py @@ -179,8 +179,10 @@ def to_modelica(self, scaffold): f.write(file_data) full_model_name = os.path.join( - scaffold.project_name, scaffold.loads_path.files_relative_dir, f"B{building['building_id']}", 'coupling').replace( - os.path.sep, '.') + scaffold.project_name, + scaffold.loads_path.files_relative_dir, + f"B{building['building_id']}", + "coupling").replace(os.path.sep, '.') file_data = spawn_mos_template.render(full_model_name=full_model_name) with open(os.path.join(os.path.join(b_modelica_path.scripts_dir, "RunSpawnBuilding.mos")), "w") as f: From 03f3700f4a28858a3d63fc0069b4f282497885a9 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Wed, 25 Mar 2020 08:52:13 -0600 Subject: [PATCH 48/62] fix run command --- .../model_connectors/spawn.py | 2 +- .../templates/spawn_building.mot | 2 +- .../templates/spawn_coupling.mot | 36 +++++++++++-------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/geojson_modelica_translator/model_connectors/spawn.py b/geojson_modelica_translator/model_connectors/spawn.py index 31aadfdcf..c9a52b49d 100644 --- a/geojson_modelica_translator/model_connectors/spawn.py +++ b/geojson_modelica_translator/model_connectors/spawn.py @@ -231,6 +231,6 @@ def post_process(self, scaffold, building_names): # now create the Package level package. This really needs to happen at the GeoJSON to modelica stage, but # do it here for now to aid in testing. pp = PackageParser.new_from_template( - scaffold.loads_path.files_dir, scaffold.project_name, ["Loads"] + scaffold.project_path, scaffold.project_name, ["Loads"] ) pp.save() diff --git a/geojson_modelica_translator/model_connectors/templates/spawn_building.mot b/geojson_modelica_translator/model_connectors/templates/spawn_building.mot index b7655e89c..8d4f49c96 100644 --- a/geojson_modelica_translator/model_connectors/templates/spawn_building.mot +++ b/geojson_modelica_translator/model_connectors/templates/spawn_building.mot @@ -15,7 +15,7 @@ extends Buildings.Applications.DHC.Loads.BaseClasses.PartialBuilding( "Load side medium"; parameter Integer nZon - "Number of thermal zones" + "Number of thermal zones"; parameter String idfPat= "modelica://{{project_name}}/Loads/{{data['load_resources_path']}}/{{data['idf']['filename']}}" "Path of the IDF file"; diff --git a/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot b/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot index 5b0eefcbd..311851bcf 100644 --- a/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot +++ b/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot @@ -1,6 +1,7 @@ within {{project_name}}.Loads.{{model_name}}; model coupling "FMU Template for Spawn" + extends Modelica.Icons.Example; replaceable package MediumW = Buildings.Media.Water "Source side medium"; @@ -80,20 +81,21 @@ equation // TODO: determine how to handle the "lines" {% raw %} -annotation (Diagram( - coordinateSystem(preserveAspectRatio=false, extent={{-120,-100},{160,60}}), - graphics={Text( - extent={{-46,36},{86,10}}, - lineColor={28,108,200}, - textString="")}), - __Dymola_Commands(file= - "Resources/Scripts/Dymola/SpawnTwoBuildings/CouplingSpawnZ6.mos" - "Simulate and plot"), - experiment( - StopTime=604800, - Tolerance=1e-06, - __Dymola_Algorithm="Cvode"), -Documentation (info= + annotation (Diagram( + coordinateSystem(preserveAspectRatio=false, extent={{-120,-100},{160,60}}), + graphics={Text( + extent={{-46,36},{86,10}}, + lineColor={28,108,200}, + textString="")}), +{% endraw %} + __Dymola_Commands(file="modelica://{{project_name}}/Loads/Resources/Scripts/{{model_name}}/Dymola/RunSpawnBuilding.mos" +{% raw %} + "Simulate and plot"), + experiment( + StopTime=604800, + Tolerance=1e-06, + __Dymola_Algorithm="Cvode"), + Documentation(info= "

    This example illustrates the use of @@ -126,8 +128,12 @@ revisions= "

    • +March 21, 2020, by Nicholas Long:
      +GeoJson-Modelica translator template first implementation. +
    • +
    • February 21, 2020, by Antoine Gautier:
      -First implementation. +Model first implementation.
    ")); From 45b799d5d0db2a8aef1439ab1c9295772d71e250 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Wed, 25 Mar 2020 18:29:01 -0600 Subject: [PATCH 49/62] add fraSca and nZon --- geojson_modelica_translator/model_connectors/spawn.py | 2 ++ .../model_connectors/templates/spawn_building.mot | 8 ++++---- .../model_connectors/templates/spawn_coupling.mot | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/geojson_modelica_translator/model_connectors/spawn.py b/geojson_modelica_translator/model_connectors/spawn.py index c9a52b49d..78072fd09 100644 --- a/geojson_modelica_translator/model_connectors/spawn.py +++ b/geojson_modelica_translator/model_connectors/spawn.py @@ -132,6 +132,8 @@ def to_modelica(self, scaffold): "path": os.path.dirname(mos_weather_filename), }, "thermal_zones": [], + "thermal_zones_count": len(thermal_zones), + } for tz in thermal_zones: # TODO: method for creating nice zone names for modelica diff --git a/geojson_modelica_translator/model_connectors/templates/spawn_building.mot b/geojson_modelica_translator/model_connectors/templates/spawn_building.mot index 8d4f49c96..665bbda36 100644 --- a/geojson_modelica_translator/model_connectors/templates/spawn_building.mot +++ b/geojson_modelica_translator/model_connectors/templates/spawn_building.mot @@ -14,7 +14,7 @@ extends Buildings.Applications.DHC.Loads.BaseClasses.PartialBuilding( replaceable package MediumA = Buildings.Media.Air "Load side medium"; - parameter Integer nZon + parameter Integer nZon={{data['thermal_zones_count']}} "Number of thermal zones"; parameter String idfPat= "modelica://{{project_name}}/Loads/{{data['load_resources_path']}}/{{data['idf']['filename']}}" @@ -66,7 +66,7 @@ extends Buildings.Applications.DHC.Loads.BaseClasses.PartialBuilding( Buildings.Applications.DHC.Loads.Examples.BaseClasses.FanCoil4Pipe terUni[nZon]( redeclare package Medium1 = MediumW, redeclare package Medium2 = MediumA, - each facSca=facSca, + each facSca=1, QHea_flow_nominal={50000,10000,10000,10000,10000,10000}, QCoo_flow_nominal={-10000,-10000,-10000,-10000,-10000,-10000}, each T_aLoaHea_nominal=293.15, @@ -182,8 +182,8 @@ equation connect(terUni[i].TSetCoo, maxTSet[i].y) {% raw %}annotation (Line(points={{-140.833,12.6667},{-164,12.6667},{-164,30},{-198,30}},{% endraw %} color={0,0,127})); - end for; - {% endfor %} + end for; +{% endfor %} {% raw %} annotation ( diff --git a/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot b/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot index 311851bcf..0489548f1 100644 --- a/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot +++ b/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot @@ -9,7 +9,7 @@ model coupling "Load side medium"; {% raw %} - Buildings.Applications.DHC.Loads.Examples.BaseClasses.BuildingSpawnZ1 bui( + building bui( nPorts_a=2, nPorts_b=2) "Building" From c4228d2bbf98e355a04680ea022fa8d67ccb5898 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Wed, 25 Mar 2020 20:42:38 -0600 Subject: [PATCH 50/62] fix template --- .../templates/spawn_building.mot | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/geojson_modelica_translator/model_connectors/templates/spawn_building.mot b/geojson_modelica_translator/model_connectors/templates/spawn_building.mot index 665bbda36..6e37d4b1e 100644 --- a/geojson_modelica_translator/model_connectors/templates/spawn_building.mot +++ b/geojson_modelica_translator/model_connectors/templates/spawn_building.mot @@ -142,47 +142,49 @@ equation color={0,0,127})); {% endraw %} -{% for zone in data['thermal_zones'] %} - connect(multiplex3_1.y, {{zone['modelica_object_name']}}.qGai_flow) - {% raw %}annotation (Line(points={{1,138},{20,138},{20,30},{22,30}}, color={0,0,127}));{% endraw %} - for i in 1:nZon loop - connect ({{zone['modelica_object_name']}}.ports[1], terUni[i].port_aLoa) - {% raw %}annotation (Line(points={{-160,-41.6667},{-172,-41.6667},{-180,-41.6667},{-180,-19.2},{62,-19.2}},{% endraw %} - color={0,127,255})); - connect ({{zone['modelica_object_name']}}.ports[2], terUni[i].port_bLoa) - {% raw %}annotation (Line(points={{-140,18.1667},{-20,18.1667},{-20,-39.2},{46,-39.2}},{% endraw %} - color={0,127,255})); - connect ({{zone['modelica_object_name']}}.TAir, terUni[i].TSen) - {% raw %}annotation (Line(points={{65,33.8},{72,33.8},{72,34},{80,34},{80,160},{-152,160},{-152,10.8333},{-140.833,10.8333}},{% endraw %} - color={0,0,127})); - connect(terUni[i].PFan, mulSum.u[i]) +for i in 1:nZon loop + connect(terUni[i].PFan, mulSum.u[i]) {% raw %}annotation (Line(points={{-119.167,9},{-100,9},{-100,220},{220,220},{220,118.333},{258,118.333}},{% endraw %} color={0,0,127})); - connect(disFloCoo.ports_a1[i], terUni[i].port_bChiWat) + connect(disFloCoo.ports_a1[i], terUni[i].port_bChiWat) {% raw %}annotation (Line(points={{-140,-214},{-38,-214},{-38,1.66667},{-120,1.66667}},{% endraw %} color={0,127,255})); - connect(disFloCoo.ports_b1[i], terUni[i].port_aChiWat) + connect(disFloCoo.ports_b1[i], terUni[i].port_aChiWat) {% raw %}annotation (Line(points={{-160,-214},{-260,-214},{-260,1.66667},{-140,1.66667}},{% endraw %} color={0,127,255})); - connect(disFloHea.ports_a1[i], terUni[i].port_bHeaWat) + connect(disFloHea.ports_a1[i], terUni[i].port_bHeaWat) {% raw %}annotation (Line(points={{-220,-164},{-40,-164},{-40,-0.166667},{-120,-0.166667}},{% endraw %} color={0,127,255})); - connect(disFloHea.ports_b1[i], terUni[i].port_aHeaWat) + connect(disFloHea.ports_b1[i], terUni[i].port_aHeaWat) {% raw %}annotation (Line(points={{-240,-164},{-260,-164},{-260,-0.166667},{-140,-0.166667}},{% endraw %} color={0,127,255})); - connect(terUni[i].mReqChiWat_flow, disFloCoo.mReq_flow[i]) + connect(terUni[i].mReqChiWat_flow, disFloCoo.mReq_flow[i]) {% raw %}annotation (Line(points={{-119.167,3.5},{-104,3.5},{-104,-80},{-180,-80},{-180,-224},{-161,-224}},{% endraw %} color={0,0,127})); - connect(terUni[i].mReqHeaWat_flow, disFloHea.mReq_flow[i]) + connect(terUni[i].mReqHeaWat_flow, disFloHea.mReq_flow[i]) {% raw %}annotation (Line(points={{-119.167,5.33333},{-100,5.33333},{-100,-90.5},{-241,-90.5},{-241,-174}},{% endraw %} color={0,0,127})); - connect(terUni[i].TSetHea, minTSet[i].y) + connect(terUni[i].TSetHea, minTSet[i].y) {% raw %}annotation (Line(points={{-140.833,14.5},{-160,14.5},{-160,70},{-198,70}},{% endraw %} color={0,0,127})); - connect(terUni[i].TSetCoo, maxTSet[i].y) + connect(terUni[i].TSetCoo, maxTSet[i].y) {% raw %}annotation (Line(points={{-140.833,12.6667},{-164,12.6667},{-164,30},{-198,30}},{% endraw %} color={0,0,127})); - end for; +end for; + +//----------------Depending on number of thermal zones----------------- +{% for zone in data['thermal_zones'] %} + connect(multiplex3_1.y, {{zone['modelica_object_name']}}.qGai_flow) + {% raw %}annotation (Line(points={{1,138},{20,138},{20,30},{22,30}}, color={0,0,127}));{% endraw %} + connect ({{zone['modelica_object_name']}}.ports[1], terUni[{{loop.index}}].port_aLoa) + {% raw %}annotation (Line(points={{-160,-41.6667},{-172,-41.6667},{-180,-41.6667},{-180,-19.2},{62,-19.2}},{% endraw %} + color={0,127,255})); + connect ({{zone['modelica_object_name']}}.ports[2], terUni[{{loop.index}}].port_bLoa) + {% raw %}annotation (Line(points={{-140,18.1667},{-20,18.1667},{-20,-39.2},{46,-39.2}},{% endraw %} + color={0,127,255})); + connect ({{zone['modelica_object_name']}}.TAir, terUni[{{loop.index}}].TSen) + {% raw %}annotation (Line(points={{65,33.8},{72,33.8},{72,34},{80,34},{80,160},{-152,160},{-152,10.8333},{-140.833,10.8333}},{% endraw %} + color={0,0,127})); {% endfor %} {% raw %} @@ -193,7 +195,7 @@ Icon(coordinateSystem(extent={{-100,-100},{100,100}}), graphics={ fileName="modelica://Buildings/Resources/Images/ThermalZones/EnergyPlus/EnergyPlusLogo.png")}), Documentation( info="

    - This is a simplified six-zone building model based on EnergyPlus + This is a simplified building model based on EnergyPlus building envelope model. It was generated from translating a GeoJSON model specified within URBANopt UI. The heating and cooling loads are computed with a four-pipe From 0e4cc5b44c10b7f979dd0073265fcbe3bb13308c Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Fri, 27 Mar 2020 10:47:59 -0600 Subject: [PATCH 51/62] fraSca --- .../templates/spawn_building.mot | 25 ++-- .../templates/spawn_coupling.mot | 111 ++++++++---------- 2 files changed, 65 insertions(+), 71 deletions(-) diff --git a/geojson_modelica_translator/model_connectors/templates/spawn_building.mot b/geojson_modelica_translator/model_connectors/templates/spawn_building.mot index 6e37d4b1e..4983e7f1b 100644 --- a/geojson_modelica_translator/model_connectors/templates/spawn_building.mot +++ b/geojson_modelica_translator/model_connectors/templates/spawn_building.mot @@ -13,14 +13,15 @@ extends Buildings.Applications.DHC.Loads.BaseClasses.PartialBuilding( "Source side medium"; replaceable package MediumA = Buildings.Media.Air "Load side medium"; - + parameter Real facSca = 1 + "Scaling factor to be applied to each extensive quantity"; parameter Integer nZon={{data['thermal_zones_count']}} "Number of thermal zones"; parameter String idfPat= - "modelica://{{project_name}}/Loads/{{data['load_resources_path']}}/{{data['idf']['filename']}}" + "modelica://{{project_name}}/Loads/{{data['load_resources_path']}}/{{data['idf']['filename']}}" "Path of the IDF file"; parameter String weaPat= - "modelica://{{project_name}}/Loads/{{data['load_resources_path']}}/{{data['epw']['filename']}}" + "modelica://{{project_name}}/Loads/{{data['load_resources_path']}}/{{data['mos_weather']['filename']}}" "Path of the weather file"; // TODO: Minimum and Maximum TSet: make a function of the outdoor air temperature, type of building,occupancy schedule or woking/idle days? {% raw %} @@ -46,6 +47,7 @@ extends Buildings.Applications.DHC.Loads.BaseClasses.PartialBuilding( {% for zone in data['thermal_zones'] %} Buildings.ThermalZones.EnergyPlus.ThermalZone {{zone['modelica_object_name']}}( redeclare package Medium = MediumA, + nPorts=2, zoneName="{{zone['spawn_object_name']}}") "Thermal zone" {% raw %} annotation (Placement(transformation(extent={{40,-20},{80,20}}))); {% endraw %} {% endfor %} @@ -58,7 +60,7 @@ extends Buildings.Applications.DHC.Loads.BaseClasses.PartialBuilding( "Building outer component" annotation (Placement(transformation(extent={{40,60},{60,80}}))); - Buildings.Controls.OBC.CDL.Continuous.MultiSum mulSum(nin=6) + Buildings.Controls.OBC.CDL.Continuous.MultiSum mulSum(nin=nZon) annotation (Placement(transformation(extent={{260,110},{280,130}}))); Buildings.Controls.OBC.CDL.Continuous.MultiSum mulSum3(nin=2) annotation (Placement(transformation(extent={{260,70},{280,90}}))); @@ -66,7 +68,7 @@ extends Buildings.Applications.DHC.Loads.BaseClasses.PartialBuilding( Buildings.Applications.DHC.Loads.Examples.BaseClasses.FanCoil4Pipe terUni[nZon]( redeclare package Medium1 = MediumW, redeclare package Medium2 = MediumA, - each facSca=1, + each facSca=facSca, QHea_flow_nominal={50000,10000,10000,10000,10000,10000}, QCoo_flow_nominal={-10000,-10000,-10000,-10000,-10000,-10000}, each T_aLoaHea_nominal=293.15, @@ -82,6 +84,7 @@ extends Buildings.Applications.DHC.Loads.BaseClasses.PartialBuilding( Buildings.Applications.DHC.Loads.BaseClasses.FlowDistribution disFloHea( redeclare package Medium = MediumW, m_flow_nominal=sum(terUni.mHeaWat_flow_nominal .* terUni.facSca), + have_pum=true, dp_nominal=100000, nPorts_a1=nZon, nPorts_b1=nZon) @@ -92,6 +95,7 @@ extends Buildings.Applications.DHC.Loads.BaseClasses.PartialBuilding( m_flow_nominal=sum(terUni.mChiWat_flow_nominal .* terUni.facSca), typDis=Buildings.Applications.DHC.Loads.Types.DistributionType.ChilledWater, dp_nominal=100000, + have_pum=true, nPorts_a1=nZon, nPorts_b1=nZon) "Chilled water distribution system" @@ -176,14 +180,19 @@ end for; {% for zone in data['thermal_zones'] %} connect(multiplex3_1.y, {{zone['modelica_object_name']}}.qGai_flow) {% raw %}annotation (Line(points={{1,138},{20,138},{20,30},{22,30}}, color={0,0,127}));{% endraw %} + connect ({{zone['modelica_object_name']}}.ports[1], terUni[{{loop.index}}].port_aLoa) - {% raw %}annotation (Line(points={{-160,-41.6667},{-172,-41.6667},{-180,-41.6667},{-180,-19.2},{62,-19.2}},{% endraw %} + {% raw %} annotation(Line(points={{42,-119.2},{-8,-119.2},{-8,18.1667},{-120,18.1667}},{% endraw %} color={0,127,255})); + + connect ({{zone['modelica_object_name']}}.ports[2], terUni[{{loop.index}}].port_bLoa) - {% raw %}annotation (Line(points={{-140,18.1667},{-20,18.1667},{-20,-39.2},{46,-39.2}},{% endraw %} + {% raw %} annotation (Line(points={{-140,18.1667},{-20,18.1667},{-20,-119.2},{46,-119.2}},{% endraw %} color={0,127,255})); + + connect ({{zone['modelica_object_name']}}.TAir, terUni[{{loop.index}}].TSen) - {% raw %}annotation (Line(points={{65,33.8},{72,33.8},{72,34},{80,34},{80,160},{-152,160},{-152,10.8333},{-140.833,10.8333}},{% endraw %} + {% raw %}annotation (Line(points={{81,13.8},{80,13.8},{80,160},{-152,160},{-152,10.8333},{-140.833,10.8333}},{% endraw %} color={0,0,127})); {% endfor %} diff --git a/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot b/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot index 0489548f1..7bb68dca3 100644 --- a/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot +++ b/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot @@ -3,80 +3,66 @@ model coupling "FMU Template for Spawn" extends Modelica.Icons.Example; - replaceable package MediumW = Buildings.Media.Water + replaceable package MediumW = Buildings.Media.Water "Source side medium"; - replaceable package MediumA = Buildings.Media.Air + replaceable package MediumA = Buildings.Media.Air "Load side medium"; {% raw %} - building bui( - nPorts_a=2, - nPorts_b=2) - "Building" + spawn_two_building.Loads.B5a6b99ec37f4de7f94020090.building bui( + nPorts_b=2, + nPorts_a=2) "Building spawn model" annotation (Placement(transformation(extent={{40,-40},{60,-20}}))); - Buildings.Fluid.Sources.MassFlowSource_T supHeaWat( - use_m_flow_in=true, - redeclare package Medium = MediumW, - use_T_in=true, - nPorts=1) "Heating water supply" - annotation (Placement(transformation( - extent={{-10,-10},{10,10}}, - rotation=0, - origin={-30,0}))); - Modelica.Blocks.Sources.RealExpression THeaWatSup(y=bui.terUni.T_aHeaWat_nominal) - "Heating water supply temperature" - annotation (Placement(transformation(extent={{-80,-10},{-60,10}}))); - Modelica.Blocks.Sources.RealExpression mHeaWat_flow(y=bui.disFloHea.mReqTot_flow) - "Heating water flow rate" - annotation (Placement(transformation(extent={{-80,10},{-60,30}}))); - Buildings.Fluid.Sources.MassFlowSource_T supChiWat( - use_m_flow_in=true, - redeclare package Medium = MediumW, - use_T_in=true, - nPorts=1) "Chilled water supply" - annotation (Placement(transformation( - extent={{-10,-10},{10,10}}, - rotation=0, - origin={-30,-80}))); - Modelica.Blocks.Sources.RealExpression TChiWatSup(y=bui.terUni.T_aChiWat_nominal) - "Chilled water supply temperature" - annotation (Placement(transformation(extent={{-80,-90},{-60,-70}}))); - Modelica.Blocks.Sources.RealExpression mChiWat_flow(y=bui.disFloCoo.mReqTot_flow) - "Chilled water flow rate" - annotation (Placement(transformation(extent={{-80,-70},{-60,-50}}))); Buildings.Fluid.Sources.Boundary_pT sinHeaWat( redeclare package Medium = MediumW, - p=300000, - nPorts=1) "Sink for heating water" annotation (Placement(transformation( + nPorts=1) + "Sink for heating water" + annotation (Placement(transformation( extent={{10,-10},{-10,10}}, rotation=0, - origin={130,0}))); + origin={134,20}))); Buildings.Fluid.Sources.Boundary_pT sinChiWat( - redeclare package Medium = MediumW, - p=300000, - nPorts=1) "Sink for chilled water" annotation (Placement(transformation( - extent={{10,-10},{-10,10}}, - rotation=0, - origin={130,-80}))); + redeclare package Medium = MediumW, nPorts=1) + "Sink for chilled water" + annotation (Placement(transformation( + extent={{10,-10},{-10,10}}, + rotation=0, + origin={134,-40}))); + Modelica.Blocks.Sources.RealExpression THeaWatSup(y=max(bui.terUni.T_aHeaWat_nominal)) + "Heating water supply temperature" + annotation (Placement(transformation(extent={{-110,10},{-90,30}}))); + Modelica.Blocks.Sources.RealExpression TChiWatSup(y=min(bui.terUni.T_aChiWat_nominal)) + "Chilled water supply temperature" + annotation (Placement(transformation(extent={{-110,-50},{-90,-30}}))); + Buildings.Fluid.Sources.Boundary_pT supHeaWat( + redeclare package Medium = MediumW, + use_T_in=true, + nPorts=1) "Heating water supply" annotation (Placement(transformation( + extent={{-10,-10},{10,10}}, + rotation=0, + origin={-40,20}))); + Buildings.Fluid.Sources.Boundary_pT supChiWat( + redeclare package Medium = MediumW, + use_T_in=true, + nPorts=1) "Chilled water supply" annotation (Placement(transformation( + extent={{-10,-10},{10,10}}, + rotation=0, + origin={-40,-40}))); {% endraw %} equation {% raw %} - connect(THeaWatSup.y, supHeaWat.T_in) annotation (Line(points={{-59,0},{-50,0}, - {-50,4},{-42,4}}, color={0,0,127})); - connect(mHeaWat_flow.y, supHeaWat.m_flow_in) annotation (Line(points={{-59,20}, - {-50,20},{-50,8},{-42,8}}, color={0,0,127})); - connect(TChiWatSup.y, supChiWat.T_in) annotation (Line(points={{-59,-80},{-50, - -80},{-50,-76},{-42,-76}}, color={0,0,127})); - connect(mChiWat_flow.y, supChiWat.m_flow_in) annotation (Line(points={{-59, - -60},{-50,-60},{-50,-72},{-42,-72}}, color={0,0,127})); - connect(supHeaWat.ports[1], bui.ports_a[1]) annotation (Line(points={{-20,0}, - {0,0},{0,-50},{20,-50}}, color={0,127,255})); - connect(supChiWat.ports[1], bui.ports_a[2]) annotation (Line(points={{-20,-80}, - {0,-80},{0,-46},{20,-46}}, color={0,127,255})); - connect(bui.ports_b[1], sinHeaWat.ports[1]) annotation (Line(points={{80,-50}, - {94,-50},{94,0},{120,0}}, color={0,127,255})); - connect(bui.ports_b[2], sinChiWat.ports[1]) annotation (Line(points={{80,-46}, - {94,-46},{94,-80},{120,-80}}, color={0,127,255})); + connect(bui.ports_b[1],sinHeaWat. ports[1]) annotation (Line(points={{80,-50}, + {104,-50},{104,20},{124,20}},color={0,127,255})); + connect(bui.ports_b[2],sinChiWat. ports[1]) annotation (Line(points={{80,-46}, + {104,-46},{104,-40},{124,-40}}, color={0,127,255})); + connect(supHeaWat.T_in,THeaWatSup. y) annotation (Line(points={{-52,24},{-70,24}, + {-70,20},{-89,20}},color={0,0,127})); + connect(supHeaWat.ports[1], bui.ports_a[1]) annotation (Line(points={{-30,20}, + {-10,20},{-10,-50},{20,-50}},color={0,127,255})); + connect(TChiWatSup.y,supChiWat. T_in) annotation (Line(points={{-89,-40},{-70, + -40},{-70,-36},{-52,-36}}, color={0,0,127})); + connect(supChiWat.ports[1], bui.ports_a[2]) annotation (Line(points={{-30,-40}, + {-10,-40},{-10,-46},{20,-46}},color={0,127,255})); {% endraw %} // TODO: determine how to handle the "lines" @@ -93,8 +79,7 @@ equation "Simulate and plot"), experiment( StopTime=604800, - Tolerance=1e-06, - __Dymola_Algorithm="Cvode"), + Tolerance=1e-06), Documentation(info= "

    From 46c9c4e6964f62f664d88abb224afd958bcc74bb Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Fri, 27 Mar 2020 10:57:20 -0600 Subject: [PATCH 52/62] set project name --- .../model_connectors/templates/spawn_coupling.mot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot b/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot index 7bb68dca3..ebb54ad55 100644 --- a/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot +++ b/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot @@ -8,8 +8,8 @@ model coupling replaceable package MediumA = Buildings.Media.Air "Load side medium"; + {{project_name}}.Loads.B5a6b99ec37f4de7f94020090.building bui( {% raw %} - spawn_two_building.Loads.B5a6b99ec37f4de7f94020090.building bui( nPorts_b=2, nPorts_a=2) "Building spawn model" annotation (Placement(transformation(extent={{40,-40},{60,-20}}))); From 6a84032a17380b3c9a108931dcdfe16b5e2f99af Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Fri, 27 Mar 2020 11:06:57 -0600 Subject: [PATCH 53/62] remove issue1442 --- .github/ISSUE_TEMPLATE.md | 2 +- .travis.yml | 2 +- README.rst | 4 ++-- requirements.txt | 2 +- setup.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 6f7827a4d..f4c08060c 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -10,6 +10,6 @@ -Modelica Buildings Library: `7.0.0 or GithubBranch/SHA (e.g., issue1442_loadCoupling/SHA_XYZ)` +Modelica Buildings Library: `7.0.0 or GithubBranch/SHA (e.g., issue1437_district_heating_cooling /SHA_XYZ)` TEASER: `URBANopt/0.6.9` Python: `3.6.5` diff --git a/.travis.yml b/.travis.yml index adf8dea61..ae899d9d1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ env: - TOX_ENV=flake8 - TOX_ENV=docs before_script: - - git clone --single-branch --branch issue1442_loadCoupling https://github.com/lbl-srg/modelica-buildings.git + - git clone --single-branch --branch issue1437_district_heating_cooling https://github.com/lbl-srg/modelica-buildings.git - export MODELICAPATH=$(pwd)/modelica-buildings script: - tox -e $TOX_ENV diff --git a/README.rst b/README.rst index 95c46deba..82bf31743 100644 --- a/README.rst +++ b/README.rst @@ -89,9 +89,9 @@ will automatically run the models without having to follow the steps below. * Clone https://github.com/lbl-srg/docker-ubuntu-jmodelica and follow the set up instructions. * Copy jmodelica.py (from docker-ubuntu-jmodelica) to root of project where you will simulate (e.g., geojson-modelica-translator/tests/model_connectors/output) -* Pull https://github.com/lbl-srg/modelica-buildings/tree/issue1442_loadCoupling +* Pull https://github.com/lbl-srg/modelica-buildings/tree/issue1437_district_heating_cooling * **Make sure you have git-lfs installed**. You may need to checkout out the library again after install lfs. - * Please make sure you are in the issue1442_loadCoupling branch. + * Please make sure you are in the issue1437_district_heating_cooling branch. * Mac: `brew install git-lfs; git lfs install` * Ubuntu: `sudo apt install git-lfs; git lfs install` * Add the Buildings Library path to your MODELICAPATH environment variable (e.g., export MODELICAPATH=${MODELICAPATH}:/home//github/modelica-buildings). diff --git a/requirements.txt b/requirements.txt index 2dcadfded..65fa5e5ca 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ requests==2.22.0 # dependent projects BuildingsPy==2.0.0 # There may be a need to require the MBL. Note this is a large dependency, but needed to assemble the models for running -#-e git+https://github.com/lbl-srg/modelica-buildings.git@issue1442_loadCoupling#egg=buildings +#-e git+https://github.com/lbl-srg/modelica-buildings.git@issue1437_district_heating_cooling #egg=buildings # Use the core teaser library. Note: if using the github checkout, then it will be saved in the ./src directory if python is system python. teaser==0.7.2 diff --git a/setup.py b/setup.py index 55fffab97..7ff1c0eed 100755 --- a/setup.py +++ b/setup.py @@ -79,7 +79,7 @@ shutil.rmtree(save_path) if os.path.exists(tmp_save_path): shutil.rmtree(tmp_save_path) -mbl_archive_name = "issue1442_loadCoupling" +mbl_archive_name = "issue1437_district_heating_cooling" r = get(f"https://github.com/lbl-srg/{repo_name}/archive/{mbl_archive_name}.zip") with ZipFile(BytesIO(r.content)) as zip: files = zip.namelist() From 7ca5218b6ce49081f5c6982450ae6f37b2cd1f23 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Fri, 27 Mar 2020 12:12:50 -0600 Subject: [PATCH 54/62] fixes to run in JModelica --- geojson_modelica_translator/model_connectors/spawn.py | 2 +- .../model_connectors/templates/spawn_building.mot | 11 +++++++---- .../model_connectors/templates/spawn_coupling.mot | 4 +++- tests/model_connectors/test_spawn.py | 11 +++++++++++ 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/geojson_modelica_translator/model_connectors/spawn.py b/geojson_modelica_translator/model_connectors/spawn.py index 78072fd09..1ac0d63ea 100644 --- a/geojson_modelica_translator/model_connectors/spawn.py +++ b/geojson_modelica_translator/model_connectors/spawn.py @@ -219,7 +219,7 @@ def post_process(self, scaffold, building_names): for b in building_names: b_modelica_path = os.path.join(scaffold.loads_path.files_dir, b) new_package = PackageParser.new_from_template( - b_modelica_path, b, ["coupling", "building"], within=f"{scaffold.project_name}.Loads" + b_modelica_path, b, ["building", "coupling"], within=f"{scaffold.project_name}.Loads" ) new_package.save() diff --git a/geojson_modelica_translator/model_connectors/templates/spawn_building.mot b/geojson_modelica_translator/model_connectors/templates/spawn_building.mot index 4983e7f1b..569e91e5e 100644 --- a/geojson_modelica_translator/model_connectors/templates/spawn_building.mot +++ b/geojson_modelica_translator/model_connectors/templates/spawn_building.mot @@ -17,11 +17,13 @@ extends Buildings.Applications.DHC.Loads.BaseClasses.PartialBuilding( "Scaling factor to be applied to each extensive quantity"; parameter Integer nZon={{data['thermal_zones_count']}} "Number of thermal zones"; + // The IDF and MOS file paths may need to be different for Dymola and JModelica. The below works + // for JModelica using Docker. parameter String idfPat= - "modelica://{{project_name}}/Loads/{{data['load_resources_path']}}/{{data['idf']['filename']}}" + "../../Resources/Data/{{model_name}}/{{data['idf']['filename']}}" "Path of the IDF file"; parameter String weaPat= - "modelica://{{project_name}}/Loads/{{data['load_resources_path']}}/{{data['mos_weather']['filename']}}" + "../../Resources/Data/{{model_name}}/{{data['mos_weather']['filename']}}" "Path of the weather file"; // TODO: Minimum and Maximum TSet: make a function of the outdoor air temperature, type of building,occupancy schedule or woking/idle days? {% raw %} @@ -53,9 +55,10 @@ extends Buildings.Applications.DHC.Loads.BaseClasses.PartialBuilding( {% endfor %} {% raw %} + inner Buildings.ThermalZones.EnergyPlus.Building building( - idfName=Modelica.Utilities.Files.loadResource(idfPat), - weaName=Modelica.Utilities.Files.loadResource(weaPat), + idfName=idfPat, + weaName=weaPat, showWeatherData=false) "Building outer component" annotation (Placement(transformation(extent={{40,60},{60,80}}))); diff --git a/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot b/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot index ebb54ad55..8ee21614c 100644 --- a/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot +++ b/geojson_modelica_translator/model_connectors/templates/spawn_coupling.mot @@ -8,8 +8,10 @@ model coupling replaceable package MediumA = Buildings.Media.Air "Load side medium"; - {{project_name}}.Loads.B5a6b99ec37f4de7f94020090.building bui( {% raw %} + // Do not fully qualify the path to building as {{project_name}}.Loads... That does + // not work in JModelica. + building bui( nPorts_b=2, nPorts_a=2) "Building spawn model" annotation (Placement(transformation(extent={{40,-40},{60,-20}}))); diff --git a/tests/model_connectors/test_spawn.py b/tests/model_connectors/test_spawn.py index 1bf3d756e..3438e3ad3 100644 --- a/tests/model_connectors/test_spawn.py +++ b/tests/model_connectors/test_spawn.py @@ -71,6 +71,17 @@ def test_spawn_init(self): def test_spawn_to_modelica(self): self.spawn.to_modelica(self.gj.scaffold) + def test_spawn_to_modelica_and_run(self): + self.spawn.to_modelica(self.gj.scaffold) + + # make sure the model can run using the ModelicaRunner class + mr = ModelicaRunner() + file_to_run = os.path.abspath( + os.path.join(self.gj.scaffold.loads_path.files_dir, 'B5a6b99ec37f4de7f94020090', 'coupling.mo') + ) + exitcode = mr.run_in_docker(file_to_run) + self.assertEqual(0, exitcode) + class SpawnModelConnectorTwoBuildingTest(unittest.TestCase): def setUp(self): From cab161b3e00f394ebaa75f82d46e3c6b0401cae2 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Mon, 30 Mar 2020 10:16:54 -0600 Subject: [PATCH 55/62] add notes about paths and try to run RC models --- .../model_connectors/templates/spawn_building.mot | 15 +++++++++++---- tests/model_connectors/test_spawn.py | 2 +- tests/test_translator.py | 9 +++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/geojson_modelica_translator/model_connectors/templates/spawn_building.mot b/geojson_modelica_translator/model_connectors/templates/spawn_building.mot index 569e91e5e..221f24372 100644 --- a/geojson_modelica_translator/model_connectors/templates/spawn_building.mot +++ b/geojson_modelica_translator/model_connectors/templates/spawn_building.mot @@ -16,11 +16,18 @@ extends Buildings.Applications.DHC.Loads.BaseClasses.PartialBuilding( parameter Real facSca = 1 "Scaling factor to be applied to each extensive quantity"; parameter Integer nZon={{data['thermal_zones_count']}} - "Number of thermal zones"; - // The IDF and MOS file paths may need to be different for Dymola and JModelica. The below works - // for JModelica using Docker. + "Number of thermal zones"; + // The IDF and MOS file paths may need to be different for Dymola and JModelica. + // For Dymola use the paths below: + // parameter String idfPat= + // "modelica://{{project_name}}/Loads/{{data['load_resources_path']}}/{{data['idf']['filename']}}" + // "Path of the IDF file"; + // parameter String weaPat= + // "modelica://{{project_name}}/Loads/{{data['load_resources_path']}}/{{data['mos_weather']['filename']}}" + // "Path of the weather file"; + // For JModelica use the paths below: parameter String idfPat= - "../../Resources/Data/{{model_name}}/{{data['idf']['filename']}}" + "../../Resources/Data/{{model_name}}/{{data['idf']['filename']}}" "Path of the IDF file"; parameter String weaPat= "../../Resources/Data/{{model_name}}/{{data['mos_weather']['filename']}}" diff --git a/tests/model_connectors/test_spawn.py b/tests/model_connectors/test_spawn.py index 3438e3ad3..fcd51c21f 100644 --- a/tests/model_connectors/test_spawn.py +++ b/tests/model_connectors/test_spawn.py @@ -111,7 +111,7 @@ def test_spawn_to_modelica_and_run(self): # make sure the model can run using the ModelicaRunner class mr = ModelicaRunner() file_to_run = os.path.abspath( - os.path.join(self.gj.scaffold.loads_path.files_dir, 'B5a6b99ec37f4de7f94020090', 'coupling.mo') + os.path.join(self.gj.scaffold.loads_path.files_dir, '5a6b99ec37f4de7f94021950', 'coupling.mo') ) exitcode = mr.run_in_docker(file_to_run) self.assertEqual(0, exitcode) diff --git a/tests/test_translator.py b/tests/test_translator.py index c12a81e5c..e0b64b8cb 100644 --- a/tests/test_translator.py +++ b/tests/test_translator.py @@ -39,6 +39,7 @@ from geojson_modelica_translator.geojson_modelica_translator import ( GeoJsonModelicaTranslator ) +from geojson_modelica_translator.modelica.modelica_runner import ModelicaRunner from geojson_modelica_translator.system_parameters.system_parameters import ( SystemParameters ) @@ -114,6 +115,14 @@ def test_to_modelica_defaults(self): ) self.assertTrue(os.path.exists(path), f"Path not found: {path}") + # verify that the models run in JModelica + mr = ModelicaRunner() + file_to_run = os.path.abspath( + os.path.join(gj.scaffold.loads_path.files_dir, 'B5a6b99ec37f4de7f94020090', 'Office.mo') + ) + exitcode = mr.run_in_docker(file_to_run) + self.assertEqual(0, exitcode) + def test_to_modelica_rc_order_4(self): self.results_path = os.path.abspath("tests/output/rc_order_4") if os.path.exists(self.results_path): From 9361af478e137491e33a33b2c9a01066f978f1cf Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Mon, 30 Mar 2020 10:48:01 -0600 Subject: [PATCH 56/62] use modelica:// format --- .../templates/spawn_building.mot | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/geojson_modelica_translator/model_connectors/templates/spawn_building.mot b/geojson_modelica_translator/model_connectors/templates/spawn_building.mot index 221f24372..6e2d208d8 100644 --- a/geojson_modelica_translator/model_connectors/templates/spawn_building.mot +++ b/geojson_modelica_translator/model_connectors/templates/spawn_building.mot @@ -16,21 +16,13 @@ extends Buildings.Applications.DHC.Loads.BaseClasses.PartialBuilding( parameter Real facSca = 1 "Scaling factor to be applied to each extensive quantity"; parameter Integer nZon={{data['thermal_zones_count']}} - "Number of thermal zones"; - // The IDF and MOS file paths may need to be different for Dymola and JModelica. - // For Dymola use the paths below: - // parameter String idfPat= - // "modelica://{{project_name}}/Loads/{{data['load_resources_path']}}/{{data['idf']['filename']}}" - // "Path of the IDF file"; - // parameter String weaPat= - // "modelica://{{project_name}}/Loads/{{data['load_resources_path']}}/{{data['mos_weather']['filename']}}" - // "Path of the weather file"; - // For JModelica use the paths below: + "Number of thermal zones"; + // TODO: Verify that the paths work in JModelica, Dymola, and in py.tests. parameter String idfPat= - "../../Resources/Data/{{model_name}}/{{data['idf']['filename']}}" + "modelica://{{project_name}}/Loads/{{data['load_resources_path']}}/{{data['idf']['filename']}}" "Path of the IDF file"; parameter String weaPat= - "../../Resources/Data/{{model_name}}/{{data['mos_weather']['filename']}}" + "modelica://{{project_name}}/Loads/{{data['load_resources_path']}}/{{data['mos_weather']['filename']}}" "Path of the weather file"; // TODO: Minimum and Maximum TSet: make a function of the outdoor air temperature, type of building,occupancy schedule or woking/idle days? {% raw %} @@ -64,8 +56,8 @@ extends Buildings.Applications.DHC.Loads.BaseClasses.PartialBuilding( {% raw %} inner Buildings.ThermalZones.EnergyPlus.Building building( - idfName=idfPat, - weaName=weaPat, + idfName=Modelica.Utilities.Files.loadResource(idfPat), + weaName=Modelica.Utilities.Files.loadResource(weaPat), showWeatherData=false) "Building outer component" annotation (Placement(transformation(extent={{40,60},{60,80}}))); From c3762ffd4d828d1cd9d443d8502201edc31f2949 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Tue, 31 Mar 2020 16:58:51 -0600 Subject: [PATCH 57/62] shift run dir up to project level --- .../modelica/modelica_runner.py | 13 +++++++---- tests/model_connectors/test_spawn.py | 11 ++++++---- tests/test_translator.py | 22 +++++++++---------- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/geojson_modelica_translator/modelica/modelica_runner.py b/geojson_modelica_translator/modelica/modelica_runner.py index d581892cb..643cdbff5 100644 --- a/geojson_modelica_translator/modelica/modelica_runner.py +++ b/geojson_modelica_translator/modelica/modelica_runner.py @@ -29,7 +29,6 @@ """ import os - import shutil import subprocess @@ -64,7 +63,7 @@ def __init__(self, modelica_lib_path=None): r = subprocess.call(['docker', 'ps'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) self.docker_configured = r == 0 - def run_in_docker(self, file_to_run): + def run_in_docker(self, file_to_run, run_path=None): """ Run the Modelica project in a docker-based environment. Results are saved into the path of the file that was selected to run. @@ -82,7 +81,9 @@ def run_in_docker(self, file_to_run): if not os.path.isfile(file_to_run): raise Exception(f'Expecting to run a file, not a folder in {file_to_run}') - run_path = os.path.dirname(file_to_run) + if not run_path: + run_path = os.path.dirname(file_to_run) + new_jm_ipython = os.path.join(run_path, os.path.basename(self.jm_ipython_path)) shutil.copyfile(self.jm_ipython_path, new_jm_ipython) os.chmod(new_jm_ipython, 0o775) @@ -91,8 +92,12 @@ def run_in_docker(self, file_to_run): os.chdir(run_path) stdout_log = open('stdout.log', 'w') try: + # get the relative difference between the file to run and the path which everything is running in. + # make sure to simulate at a directory above the project directory! + run_model = os.path.relpath(file_to_run, run_path) # .replace(os.sep, '.') Use this if removing .mo file filename + # TODO: Create a logger to show more information such as the actual run command being executed. p = subprocess.Popen( - ['./jm_ipython.sh', 'jmodelica.py', os.path.basename(file_to_run)], + ['./jm_ipython.sh', 'jmodelica.py', run_model], stdout=stdout_log, stderr=subprocess.STDOUT, cwd=run_path diff --git a/tests/model_connectors/test_spawn.py b/tests/model_connectors/test_spawn.py index fcd51c21f..2e5d87f39 100644 --- a/tests/model_connectors/test_spawn.py +++ b/tests/model_connectors/test_spawn.py @@ -31,6 +31,7 @@ import os import shutil import unittest +from pathlib import Path from geojson_modelica_translator.geojson_modelica_translator import ( GeoJsonModelicaTranslator @@ -77,9 +78,10 @@ def test_spawn_to_modelica_and_run(self): # make sure the model can run using the ModelicaRunner class mr = ModelicaRunner() file_to_run = os.path.abspath( - os.path.join(self.gj.scaffold.loads_path.files_dir, 'B5a6b99ec37f4de7f94020090', 'coupling.mo') + os.path.join(self.gj.scaffold.loads_path.files_dir, 'B5a6b99ec37f4de7f94020090', 'coupling.mo'), ) - exitcode = mr.run_in_docker(file_to_run) + run_path = Path(os.path.abspath(self.gj.scaffold.project_path)).parent + exitcode = mr.run_in_docker(file_to_run, run_path=run_path) self.assertEqual(0, exitcode) @@ -111,7 +113,8 @@ def test_spawn_to_modelica_and_run(self): # make sure the model can run using the ModelicaRunner class mr = ModelicaRunner() file_to_run = os.path.abspath( - os.path.join(self.gj.scaffold.loads_path.files_dir, '5a6b99ec37f4de7f94021950', 'coupling.mo') + os.path.join(self.gj.scaffold.loads_path.files_dir, 'B5a6b99ec37f4de7f94021950', 'coupling.mo') ) - exitcode = mr.run_in_docker(file_to_run) + run_path = Path(os.path.abspath(self.gj.scaffold.project_path)).parent + exitcode = mr.run_in_docker(file_to_run, run_path=run_path) self.assertEqual(0, exitcode) diff --git a/tests/test_translator.py b/tests/test_translator.py index e0b64b8cb..063ded616 100644 --- a/tests/test_translator.py +++ b/tests/test_translator.py @@ -33,17 +33,16 @@ import shutil import unittest -# from geojson_modelica_translator.modelica.modelica_runner import ( -# ModelicaRunner -# ) from geojson_modelica_translator.geojson_modelica_translator import ( GeoJsonModelicaTranslator ) -from geojson_modelica_translator.modelica.modelica_runner import ModelicaRunner +# from geojson_modelica_translator.modelica.modelica_runner import ModelicaRunner from geojson_modelica_translator.system_parameters.system_parameters import ( SystemParameters ) +# from pathlib import Path + class GeoJSONTranslatorTest(unittest.TestCase): def test_init(self): @@ -115,13 +114,14 @@ def test_to_modelica_defaults(self): ) self.assertTrue(os.path.exists(path), f"Path not found: {path}") - # verify that the models run in JModelica - mr = ModelicaRunner() - file_to_run = os.path.abspath( - os.path.join(gj.scaffold.loads_path.files_dir, 'B5a6b99ec37f4de7f94020090', 'Office.mo') - ) - exitcode = mr.run_in_docker(file_to_run) - self.assertEqual(0, exitcode) + # verify that the models run in JModelica -- this is broken! + # mr = ModelicaRunner() + # file_to_run = os.path.abspath( + # os.path.join(gj.scaffold.loads_path.files_dir, 'B5a6b99ec37f4de7f94020090', 'Office.mo') + # ) + # run_path = Path(os.path.abspath(gj.scaffold.project_path)).parent + # exitcode = mr.run_in_docker(file_to_run, run_path=run_path) + # self.assertEqual(0, exitcode) def test_to_modelica_rc_order_4(self): self.results_path = os.path.abspath("tests/output/rc_order_4") From afcab6373178a543061816df883f34b429bc1263 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Tue, 31 Mar 2020 17:14:32 -0600 Subject: [PATCH 58/62] flake errors --- geojson_modelica_translator/modelica/modelica_runner.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/geojson_modelica_translator/modelica/modelica_runner.py b/geojson_modelica_translator/modelica/modelica_runner.py index 643cdbff5..6a3b2b69a 100644 --- a/geojson_modelica_translator/modelica/modelica_runner.py +++ b/geojson_modelica_translator/modelica/modelica_runner.py @@ -94,7 +94,10 @@ def run_in_docker(self, file_to_run, run_path=None): try: # get the relative difference between the file to run and the path which everything is running in. # make sure to simulate at a directory above the project directory! - run_model = os.path.relpath(file_to_run, run_path) # .replace(os.sep, '.') Use this if removing .mo file filename + + # Use slashes for now, but can make these periods `.replace(os.sep, '.')` but must strip off + # the .mo extension on the model to run + run_model = os.path.relpath(file_to_run, run_path) # TODO: Create a logger to show more information such as the actual run command being executed. p = subprocess.Popen( ['./jm_ipython.sh', 'jmodelica.py', run_model], From 0396c8902d4e9454884e0c6785d056055aecbe41 Mon Sep 17 00:00:00 2001 From: YanfeiNREL Date: Tue, 31 Mar 2020 00:52:03 -0700 Subject: [PATCH 59/62] templating geojson_13buildings into Modelica models --- .gitignore | 2 + .pre-commit-config.yaml | 5 +- .../geojson_modelica_translator.py | 1 + .../model_connectors/teaser.py | 8 +- .../modelica/lib/runner/jmodelica.py | 4 +- .../modelica/modelica_runner.py | 1 - .../data/example_geojson_13buildings.json | 660 ++++++++++++++++++ tests/geojson/test_geojson_13_buildings.py | 66 ++ tests/modelica/data/BouncingBall.mo | 2 +- tests/modelica/test_modelica_runner.py | 7 +- tests/test_translator_13buildings.py | 203 ++++++ 11 files changed, 949 insertions(+), 10 deletions(-) create mode 100644 tests/geojson/data/example_geojson_13buildings.json create mode 100644 tests/geojson/test_geojson_13_buildings.py create mode 100644 tests/test_translator_13buildings.py diff --git a/.gitignore b/.gitignore index e502eddb3..419405798 100644 --- a/.gitignore +++ b/.gitignore @@ -97,6 +97,8 @@ tests/output tests/output_ets tests/modelica/output tests/model_connectors/output +tests/tests_13buildings_modelica/ +tests/tests_13buildings_rc4/ /geojson_modelica_translator/modelica/buildingslibrary/ # TODO: this file shoud not be writtent out, but it is... fix this eventually /geojson_modelica_translator/modelica/ets_cooling_indirect_templated.mo diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8c111ba72..914479af2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,9 +1,11 @@ exclude: | (?x)( ^docs/conf.py| + ^tests/modelica/| ^modelica_builder/modelica_parser/ ) + repos: - repo: git://github.com/pre-commit/pre-commit-hooks rev: v2.2.3 @@ -18,9 +20,10 @@ repos: - id: debug-statements - id: end-of-file-fixer # - id: requirements-txt-fixer - - id: mixed-line-ending - id: flake8 args: ['--max-line-length=140'] # default of Black + - id: mixed-line-ending + - repo: https://github.com/pre-commit/mirrors-isort rev: v4.3.4 diff --git a/geojson_modelica_translator/geojson_modelica_translator.py b/geojson_modelica_translator/geojson_modelica_translator.py index 62f04be9c..c7276d678 100644 --- a/geojson_modelica_translator/geojson_modelica_translator.py +++ b/geojson_modelica_translator/geojson_modelica_translator.py @@ -109,6 +109,7 @@ def to_modelica(self, project_name, save_dir, model_connector_str="TeaserConnect _log.info("Exporting to Modelica") for building in self.buildings: + # print("Jing2: ", building.feature.properties["type"]) _log.info(f"Adding building to model connector: {mc_klass.__class__}") model_connector.add_building(building) diff --git a/geojson_modelica_translator/model_connectors/teaser.py b/geojson_modelica_translator/model_connectors/teaser.py index 36f6b9bea..80c3ca8bc 100644 --- a/geojson_modelica_translator/model_connectors/teaser.py +++ b/geojson_modelica_translator/model_connectors/teaser.py @@ -54,18 +54,22 @@ def add_building(self, urbanopt_building, mapper=None): """ # TODO: Need to convert units, these should exist on the urbanopt_building object # TODO: Abstract out the GeoJSON functionality + # note-1(Yanfei): any building/district geojson file needs to have the following properties. + # note-2(Yanfei): there is a need to clean the building/district geojson file, before making into modelica if mapper is None: number_stories = urbanopt_building.feature.properties["number_of_stories"] + # print("Jing: ", urbanopt_building.feature.properties.keys()) number_stories_above_ground = urbanopt_building.feature.properties["number_of_stories_above_ground"] self.buildings.append( { "area": urbanopt_building.feature.properties["floor_area"] * 0.092936, # ft2 -> m2 "building_id": urbanopt_building.feature.properties["id"], "building_type": urbanopt_building.feature.properties["building_type"], - "floor_height": urbanopt_building.feature.properties["height"] * 0.3048, # ft -> m - "num_stories": urbanopt_building.feature.properties["number_of_stories_above_ground"], + "floor_height": urbanopt_building.feature.properties["floor_height"], # ft -> m + "num_stories": urbanopt_building.feature.properties["number_of_stories"], "num_stories_below_grade": number_stories - number_stories_above_ground, "year_built": urbanopt_building.feature.properties["year_built"], + } ) diff --git a/geojson_modelica_translator/modelica/lib/runner/jmodelica.py b/geojson_modelica_translator/modelica/lib/runner/jmodelica.py index 967ddc2d0..6e2ac84d4 100644 --- a/geojson_modelica_translator/modelica/lib/runner/jmodelica.py +++ b/geojson_modelica_translator/modelica/lib/runner/jmodelica.py @@ -5,10 +5,10 @@ # Import the function for compilation of models and the load_fmu method import os - -import pymodelica import shutil import sys + +import pymodelica from pyfmi import load_fmu from pymodelica import compile_fmu diff --git a/geojson_modelica_translator/modelica/modelica_runner.py b/geojson_modelica_translator/modelica/modelica_runner.py index d581892cb..711cd221b 100644 --- a/geojson_modelica_translator/modelica/modelica_runner.py +++ b/geojson_modelica_translator/modelica/modelica_runner.py @@ -29,7 +29,6 @@ """ import os - import shutil import subprocess diff --git a/tests/geojson/data/example_geojson_13buildings.json b/tests/geojson/data/example_geojson_13buildings.json new file mode 100644 index 000000000..5d4817641 --- /dev/null +++ b/tests/geojson/data/example_geojson_13buildings.json @@ -0,0 +1,660 @@ +{ + "features": [ + { + "geometry": { + "coordinates": [ + -78.84948467732347, + 42.81677154451123 + ], + "type": "Point" + }, + "properties": { + "begin_date": "2017-01-01T07:00:00.000Z", + "cec_climate_zone": null, + "climate_zone": "6A", + "default_template": "90.1-2013", + "end_date": "2017-13-31T07:00:00.000Z", + "id": "53340c2c-ab20-40db-aba1-11ac607c52a7", + "import_surrounding_buildings_as_shading": null, + "name": "Site Origin", + "surface_elevation": null, + "tariff_filename": null, + "timesteps_per_hour": 1, + "type": "Site Origin", + "weather_filename": "USA_NY_Buffalo-Greater.Buffalo.Intl.AP.725280_TMY3.epw" + }, + "type": "Feature" + }, + { + "geometry": { + "coordinates": [ + [ + [ + -78.84650338745196, + 42.81331301863236 + ], + [ + -78.84652443964629, + 42.81463974371101 + ], + [ + -78.84680142363833, + 42.815293654042534 + ], + [ + -78.84744455124724, + 42.81514110006128 + ], + [ + -78.84728610028628, + 42.81478165791734 + ], + [ + -78.84786797764677, + 42.814643631760134 + ], + [ + -78.84721106637106, + 42.813153418927016 + ], + [ + -78.84650338745196, + 42.81331301863236 + ] + ] + ], + "type": "Polygon" + }, + "properties": { + "id": "1", + "number_of_stories": 3, + "number_of_stories_above_ground": 3, + "building_type": "Mixed use", + "floor_area": 752184, + "floor_height": 12, + "year_built": 1980, + + "footprint_area": 188046, + "mixed_type_1": "Office", + "mixed_type_1_percentage": 50, + "mixed_type_2": "Food service", + "mixed_type_2_percentage": 50, + "mixed_type_3": "Strip shopping mall", + "mixed_type_3_percentage": 0, + "mixed_type_4": "Lodging", + "mixed_type_4_percentage": 0, + "name": "Mixed_use 1", + + "type": "Building" + }, + "type": "Feature" + }, + { + "geometry": { + "coordinates": [ + [ + [ + -78.8500120420453, + 42.81812185529549 + ], + [ + -78.85038975191084, + 42.81803226424341 + ], + [ + -78.850630729414, + 42.81857888627522 + ], + [ + -78.85025301954843, + 42.81866847653532 + ], + [ + -78.8500120420453, + 42.81812185529549 + ] + ] + ], + "type": "Polygon" + }, + "properties": { + "building_type": "Food service", + "floor_area": 22313, + "footprint_area": 22313, + "id": "2", + "name": "Restaurant 1", + "number_of_stories": 1, + "number_of_stories_above_ground": 1, + "year_built": 1980, + "floor_height": 12, + "type": "Building" + }, + "type": "Feature" + }, + { + "type": "Feature", + "properties": { + "id": "3", + "name": "Restaurant 10", + "type": "Building", + "footprint_area": 41877, + "floor_area": 125631, + "floor_height": 12, + "year_built": 1980, + "number_of_stories": 3, + "number_of_stories_above_ground": 3, + "building_type": "Food service" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -78.84962224800356, + 42.81329273502644 + ], + [ + -78.84929833482822, + 42.81337083838241 + ], + [ + -78.84983265832118, + 42.814563298664666 + ], + [ + -78.85015657149653, + 42.81448519681467 + ], + [ + -78.84962224800356, + 42.81329273502644 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "id": "4", + "name": "Restaurant 12", + "type": "Building", + "footprint_area": 10541, + "floor_area": 31623, + "floor_height": 12, + "number_of_stories": 3, + "number_of_stories_above_ground": 3, + "year_built": 1980, + "building_type": "Food service" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -78.84907318596754, + 42.81342719667407 + ], + [ + -78.84862090048105, + 42.81353625345659 + ], + [ + -78.84871721918239, + 42.813751210926796 + ], + [ + -78.84916950466888, + 42.81364215452331 + ], + [ + -78.84907318596754, + 42.81342719667407 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "id": "5", + "name": "Restaurant 14", + "type": "Building", + "footprint_area": 8804, + "floor_area": 8804, + "floor_height": 12, + "number_of_stories": 1, + "number_of_stories_above_ground": 1, + "year_built": 1980, + "building_type": "Food service" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -78.84809175426629, + 42.81367038997507 + ], + [ + -78.84848670778973, + 42.81357515750889 + ], + [ + -78.84857883872144, + 42.81378076888831 + ], + [ + -78.84818388519801, + 42.81387600103781 + ], + [ + -78.84809175426629, + 42.81367038997507 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "id": "6", + "name": "Restaurant 15", + "type": "Building", + "footprint_area": 10689, + "floor_area": 10689, + "floor_height": 12, + "number_of_stories": 1, + "number_of_stories_above_ground": 1, + "year_built": 1980, + "building_type": "Food service" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -78.84846106738529, + 42.814495803077364 + ], + [ + -78.8486903952376, + 42.81444050756261 + ], + [ + -78.8484977578349, + 42.81401059666683 + ], + [ + -78.84826842998261, + 42.81406589256599 + ], + [ + -78.84846106738529, + 42.814495803077364 + ] + ] + ] + } + }, + { + "geometry": { + "coordinates": [ + [ + [ + -78.84733878006863, + 42.816466983030836 + ], + [ + -78.84854275129324, + 42.81617669028003 + ], + [ + -78.848356395545, + 42.81576080994094 + ], + [ + -78.84715242432038, + 42.81605110464406 + ], + [ + -78.84733878006863, + 42.816466983030836 + ] + ] + ], + "type": "Polygon" + }, + "properties": { + "building_type": "Office", + "id": "7", + "detailed_model_filename": "7.osm", + "number_of_stories": 6, + "number_of_stories_above_ground": 6, + "floor_area": 10689, + "floor_height": 12, + "year_built": 1980, + "name": "Office 1", + "type": "Building" + }, + "type": "Feature" + }, + { + "geometry": { + "coordinates": [ + [ + [ + -78.84973966335251, + 42.8154441454509 + ], + [ + -78.85049562542395, + 42.81525669280299 + ], + [ + -78.85078257620685, + 42.81588131780643 + ], + [ + -78.8505086568277, + 42.81594736368234 + ], + [ + -78.85041233812638, + 42.815732413845666 + ], + [ + -78.84991755499783, + 42.81585689105046 + ], + [ + -78.84973966335251, + 42.8154441454509 + ] + ] + ], + "type": "Polygon" + }, + "properties": { + "building_type": "Outpatient health care", + "id": "8", + "name": "Hospital 1", + "detailed_model_filename": "8.osm", + "number_of_stories": 10, + "number_of_stories_above_ground": 10, + "year_built": 1980, + "floor_area": 10689, + "floor_height": 12, + "type": "Building" + }, + "type": "Feature" + }, + { + "geometry": { + "coordinates": [ + [ + [ + -78.85083627755732, + 42.81600678613279 + ], + [ + -78.85056039001891, + 42.816076133580566 + ], + [ + -78.85072568130569, + 42.816450649528036 + ], + [ + -78.84940134236577, + 42.81677160705479 + ], + [ + -78.84958014898304, + 42.81716858994267 + ], + [ + -78.8507262115271, + 42.816890840117026 + ], + [ + -78.8508565789851, + 42.81719595796099 + ], + [ + -78.85132137101688, + 42.81708331517635 + ], + [ + -78.85083627755732, + 42.81600678613279 + ] + ] + ], + "type": "Polygon" + }, + "properties": { + "building_type": "Inpatient health care", + "id": "9", + "detailed_model_filename": "9.osm", + "name": "Hospital 2", + "number_of_stories": 3, + "number_of_stories_above_ground": 3, + "year_built": 1980, + "floor_area": 10689, + "floor_height": 12, + "type": "Building" + }, + "type": "Feature" + }, + { + "geometry": { + "coordinates": [ + [ + [ + -78.85115264550463, + 42.81786093060211 + ], + [ + -78.85163483958878, + 42.81774467026972 + ], + [ + -78.85246596719499, + 42.819583261120755 + ], + [ + -78.85082390085432, + 42.819979162017745 + ], + [ + -78.85060552295334, + 42.81947573727234 + ], + [ + -78.85174564783776, + 42.81920483484765 + ], + [ + -78.85115264550463, + 42.81786093060211 + ] + ] + ], + "type": "Polygon" + }, + "properties": { + "building_type": "Mixed use", + "floor_area": 1278384, + "floor_height": 12, + "footprint_area": 159798, + "id": "10", + "mixed_type_1": "Strip shopping mall", + "mixed_type_1_percentage": 25, + "mixed_type_2": "Food service", + "mixed_type_2_percentage": 25, + "mixed_type_3": "Office", + "mixed_type_3_percentage": 15, + "mixed_type_4": "Lodging", + "mixed_type_4_percentage": 35, + "name": "Mixed use 2", + "number_of_stories": 8, + "number_of_stories_above_ground": 8, + "year_built": 1980, + "type": "Building" + }, + "type": "Feature" + }, + { + "type": "Feature", + "properties": { + "id": "11", + "name": "Restaurant 13", + "type": "Building", + "footprint_area": 10837, + "floor_area": 32511, + "floor_height": 12, + "number_of_stories": 3, + "number_of_stories_above_ground": 3, + "year_built": 1980, + "building_type": "Food service" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -78.84961163640645, + 42.81460851835703 + ], + [ + -78.84914661048371, + 42.81472064501696 + ], + [ + -78.84905029178236, + 42.81450569091638 + ], + [ + -78.84951531770513, + 42.81439356386673 + ], + [ + -78.84961163640645, + 42.81460851835703 + ] + ] + ] + } + }, + { + "geometry": { + "coordinates": [ + [ + [ + -78.84768338040897, + 42.817161656757065 + ], + [ + -78.8482630702579, + 42.8170218879136 + ], + [ + -78.84915297130291, + 42.81900776764229 + ], + [ + -78.84857328145401, + 42.81914753199706 + ], + [ + -78.84768338040897, + 42.817161656757065 + ] + ] + ], + "type": "Polygon" + }, + "properties": { + "building_type": "Strip shopping mall", + "floor_area": 374409, + "floor_height": 12, + "footprint_area": 124803, + "id": "12", + "name": "Mall 1", + "number_of_stories": 3, + "number_of_stories_above_ground": 3, + "year_built": 1980, + "type": "Building" + }, + "type": "Feature" + }, + { + "geometry": { + "coordinates": [ + [ + [ + -78.8494955083645, + 42.819748790984335 + ], + [ + -78.84891089471263, + 42.81989327725856 + ], + [ + -78.8491389243777, + 42.82038967009544 + ], + [ + -78.84972353802956, + 42.82024518498119 + ], + [ + -78.8494955083645, + 42.819748790984335 + ] + ] + ], + "type": "Polygon" + }, + "properties": { + "building_type": "Lodging", + "floor_area": 316160, + "floor_height": 12, + "footprint_area": 31616, + "id": "13", + "name": "Hotel 1", + "number_of_stories": 10, + "number_of_stories_above_ground": 10, + "year_built": 1980, + "type": "Building" + }, + "type": "Feature" + } + ], + "mappers": [], + "project": { + "begin_date": "2017-01-01T07:00:00.000Z", + "cec_climate_zone": null, + "climate_zone": "6A", + "default_template": "90.1-2013", + "end_date": "2017-12-31T07:00:00.000Z", + "id": "7c33a001-bccb-413e-ac87-67558b5d4b07", + "import_surrounding_buildings_as_shading": null, + "name": "New Project", + "surface_elevation": null, + "tariff_filename": null, + "timesteps_per_hour": 1, + "weather_filename": "USA_NY_Buffalo-Greater.Buffalo.Intl.AP.725280_TMY3.epw" + }, + "scenarios": [ + { + "feature_mappings": [], + "id": "72301739-c6c3-4dd7-bf1a-f37c8eff40db", + "name": "New Scenario" + } + ], + "type": "FeatureCollection" +} diff --git a/tests/geojson/test_geojson_13_buildings.py b/tests/geojson/test_geojson_13_buildings.py new file mode 100644 index 000000000..2e727e0a4 --- /dev/null +++ b/tests/geojson/test_geojson_13_buildings.py @@ -0,0 +1,66 @@ +""" +**************************************************************************************************** +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted +provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions +and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this list of conditions +and the following disclaimer in the documentation and/or other materials provided with the +distribution. + +Neither the name of the copyright holder nor the names of its contributors may be used to endorse +or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**************************************************************************************************** +""" + +import os +import unittest + +from geojson_modelica_translator.geojson.urbanopt_geojson import ( + UrbanOptGeoJson +) + + +class GeoJSONTest(unittest.TestCase): + def test_load_geojson(self): + # filename = os.path.abspath( "tests/geojson/data/example_geojson_13buildings.json") + filename = os.path.abspath(os.path.join(os.getcwd() + "/data/example_geojson_13buildings.json")) + json = UrbanOptGeoJson(filename) + self.assertIsNotNone(json.data) + # there are 13 buildings, plus 1 site, thus totally it is 14 features. + self.assertEqual(len(json.data.features), 14) + + def test_missing_file(self): + fn = "non-existent-path" + with self.assertRaises(Exception) as exc: + UrbanOptGeoJson(fn) + self.assertEqual(f"URBANopt GeoJSON file does not exist: {fn}", str(exc.exception)) + + def test_validate(self): + # filename = os.path.abspath("tests/geojson/data/example_geojson_13_buildings.json") + filename = os.path.abspath(os.path.join(os.getcwd()+"/data/example_geojson_13buildings.json")) + json = UrbanOptGeoJson(filename) + valid, results = json.validate() + self.assertFalse(valid) + self.assertEqual(len(results["building"]), 13) + # The id of buildings is numbered by 1-13, instead of uuid. + self.assertEqual(results["building"][0]["id"], "1") + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/modelica/data/BouncingBall.mo b/tests/modelica/data/BouncingBall.mo index e6f51a306..f30b5824c 100644 --- a/tests/modelica/data/BouncingBall.mo +++ b/tests/modelica/data/BouncingBall.mo @@ -20,4 +20,4 @@ equation reinit(v, v_new); end when; -end BouncingBall; \ No newline at end of file +end BouncingBall; diff --git a/tests/modelica/test_modelica_runner.py b/tests/modelica/test_modelica_runner.py index 132d95480..113a3be95 100644 --- a/tests/modelica/test_modelica_runner.py +++ b/tests/modelica/test_modelica_runner.py @@ -28,14 +28,15 @@ **************************************************************************************************** """ -from ..context import geojson_modelica_translator # noqa - Do not remove this line - -import shutil import os +import shutil import unittest from geojson_modelica_translator.modelica.modelica_runner import ModelicaRunner +from ..context import \ + geojson_modelica_translator # noqa - Do not remove this line + class ModelicaRunnerTest(unittest.TestCase): # create a run directory and copy in a project to test run diff --git a/tests/test_translator_13buildings.py b/tests/test_translator_13buildings.py new file mode 100644 index 000000000..914d1d4df --- /dev/null +++ b/tests/test_translator_13buildings.py @@ -0,0 +1,203 @@ +""" +**************************************************************************************************** +:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted +provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions +and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this list of conditions +and the following disclaimer in the documentation and/or other materials provided with the +distribution. + +Neither the name of the copyright holder nor the names of its contributors may be used to endorse +or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**************************************************************************************************** +""" + +# import itertools +import os +import shutil +import unittest + +# from geojson_modelica_translator.modelica.modelica_runner import ( +# ModelicaRunner +# ) +from geojson_modelica_translator.geojson_modelica_translator import ( + GeoJsonModelicaTranslator +) +from geojson_modelica_translator.system_parameters.system_parameters import ( + SystemParameters +) + + +class GeoJSONTranslatorTest(unittest.TestCase): + def test_init(self): + gj = GeoJSONTranslatorTest() + self.assertIsNotNone(gj) + + def test_from_geojson(self): + # filename = os.path.abspath("tests/geojson/data/geojson_1.json") + filename = os.path.abspath(os.path.join(os.getcwd()+"/geojson/data/example_geojson_13buildings.json")) + + gj = GeoJsonModelicaTranslator.from_geojson(filename) + + self.assertEqual(len(gj.buildings), 13) + + def test_missing_geojson(self): + fn = "non-existent-path" + with self.assertRaises(Exception) as exc: + GeoJsonModelicaTranslator.from_geojson(fn) + self.assertEqual(f"GeoJSON file does not exist: {fn}", str(exc.exception)) + + def test_to_modelica_defaults(self): + self.results_path = os.path.abspath("tests_13buildings_modelica/output/geojson_13buildings") + if os.path.exists(self.results_path): + shutil.rmtree(self.results_path) + + # filename = os.path.abspath("tests/geojson/data/geojson_1.json") + filename = os.path.abspath(os.path.join(os.getcwd()+"/geojson/data/example_geojson_13buildings.json")) + + gj = GeoJsonModelicaTranslator.from_geojson(filename) + sys_params = SystemParameters() + gj.set_system_parameters(sys_params) + gj.to_modelica("geojson_13buildings", "tests_13buildings_modelica/output") + + # setup what we are going to check + """ + model_names = [ + "Floor", + "ICT", + "Meeting", + "Office", + "package", + "Restroom", + "Storage", + ] + """ + """ + for b in gj.buildings: + print ("Jing3: ", vars(gj)) + + building_paths = [ + os.path.join(gj.loads_path.files_dir, b.dirname) for b in gj.buildings + ] + """ + + """ + building_paths = os.listdir( os.path.join(os.getcwd()+"/tests_13buildings/output/geojson_13buildings/Loads/") ) + + + path_checks = [ + f"{os.path.sep.join(r)}.mo" + for r in itertools.product(building_paths, model_names) + ] + print("Jing3: ", path_checks ) + for p in path_checks: + self.assertTrue(os.path.exists(p), f"Path not found {p}") + """ + # go through the generated buildings and ensure that the resources are created + resource_names = [ + "InternalGains_Floor", + "InternalGains_ICT", + "InternalGains_Meeting", + "InternalGains_Office", + "InternalGains_Restroom", + "InternalGains_Storage", + ] + for b in gj.buildings: + for resource_name in resource_names: + # TEASER 0.7.2 used .txt for schedule files + path = os.path.join( + os.path.join(os.getcwd()+"/tests_13buildings_modelica/output/geojson_13buildings/Loads/"), + "Resources", + "Data", + b.dirname, + f"{resource_name}.txt", + ) + self.assertTrue(os.path.exists(path), f"Path not found: {path}") + + def test_to_modelica_rc_order_4(self): + self.results_path = os.path.abspath("tests_13buildings_rc4/output/geojson_13buildings") + if os.path.exists(self.results_path): + shutil.rmtree(self.results_path) + + # filename = os.path.abspath("tests/geojson/data/geojson_1.json") + filename = os.path.abspath(os.path.join(os.getcwd()+"/geojson/data/example_geojson_13buildings.json")) + + gj = GeoJsonModelicaTranslator.from_geojson(filename) + sys_params = SystemParameters.loadd( + {"buildings": {"default": {"load_model_parameters": {"rc": {"order": 4}}}}} + ) + self.assertEqual(len(sys_params.validate()), 0) + gj.set_system_parameters(sys_params) + + gj.to_modelica("geojson_13buildings", "tests_13buildings_rc4/output") + # setup what we are going to check + """ + model_names = [ + "Floor", + "ICT", + "Meeting", + "Office", + "package", + "Restroom", + "Storage", + ] + """ + """ + building_paths = [ + os.path.join(gj.loads_path.files_dir, b.dirname) for b in gj.buildings + ] + path_checks = [ + f"{os.path.sep.join(r)}.mo" + for r in itertools.product(building_paths, model_names) + ] + + for p in path_checks: + self.assertTrue(os.path.exists(p), f"Path not found: {p}") + """ + resource_names = [ + "InternalGains_Floor", + "InternalGains_ICT", + "InternalGains_Meeting", + "InternalGains_Office", + "InternalGains_Restroom", + "InternalGains_Storage", + ] + for b in gj.buildings: + for resource_name in resource_names: + # TEASER 0.7.2 used .txt for schedule files + path = os.path.join( + os.path.join(os.getcwd()+"/tests_13buildings_rc4/output/geojson_13buildings/Loads/"), + "Resources", + "Data", + b.dirname, + f"{resource_name}.txt", + ) + self.assertTrue(os.path.exists(path), f"Path not found: {path}") + + # # make sure the model can run using the ModelicaRunner class + # mr = ModelicaRunner() + # file_to_run = os.path.abspath( + # f'{self.results_path}/Loads/B5a6b99ec37f4de7f94020090/Office.mo' + # ) + # exitcode = mr.run_in_docker(file_to_run) + # self.assertEqual(0, exitcode) + + +if __name__ == "__main__": + unittest.main() From 11b9d5ab0aa9fd1d33f6e56ea8ffef0849eba717 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Tue, 31 Mar 2020 18:46:58 -0600 Subject: [PATCH 60/62] fix and clean up tests, update keys --- tests/geojson/data/geojson_1.json | 6 +- tests/geojson/data/nrel_campus.json | 70 ++++----- tests/test_translator.py | 44 ++++++ tests/test_translator_13buildings.py | 203 --------------------------- 4 files changed, 82 insertions(+), 241 deletions(-) delete mode 100644 tests/test_translator_13buildings.py diff --git a/tests/geojson/data/geojson_1.json b/tests/geojson/data/geojson_1.json index afee07d12..877011621 100644 --- a/tests/geojson/data/geojson_1.json +++ b/tests/geojson/data/geojson_1.json @@ -42,7 +42,7 @@ "created_at": "2018-01-26T21:13:16.655Z", "building_type": "Office", "number_of_stories": 3, - "height": 9, + "floor_height": 9, "number_of_stories_above_ground": 3, "building_status": "Proposed", "include_in_energy_analysis": true, @@ -91,7 +91,7 @@ "created_at": "2018-01-31T20:35:04.192Z", "building_type": "Retail other than mall", "number_of_stories": 1, - "height": 3, + "floor_height": 3, "number_of_stories_above_ground": 1, "floor_area": 24567, "building_status": "Proposed", @@ -139,7 +139,7 @@ "updated_at": "2018-01-31T20:42:06.981Z", "created_at": "2018-01-31T20:41:11.756Z", "number_of_stories": 4, - "height": 12, + "floor_height": 12, "floor_area": 34448, "number_of_stories_above_ground": 4, "building_status": "Proposed", diff --git a/tests/geojson/data/nrel_campus.json b/tests/geojson/data/nrel_campus.json index ddf45e79b..2e01a3522 100644 --- a/tests/geojson/data/nrel_campus.json +++ b/tests/geojson/data/nrel_campus.json @@ -52,7 +52,7 @@ "footprint_perimeter": 228, "updated_at": "2017-09-01T21:17:07.590Z", "created_at": "2017-09-01T21:16:27.788Z", - "height": 6, + "floor_height": 6, "geometryType": "Polygon" } }, @@ -107,7 +107,7 @@ "footprint_perimeter": 245, "updated_at": "2017-09-01T21:17:07.507Z", "created_at": "2017-09-01T21:16:27.649Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -162,7 +162,7 @@ "footprint_perimeter": 83, "updated_at": "2017-09-01T21:17:07.607Z", "created_at": "2017-09-01T21:16:27.805Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -217,7 +217,7 @@ "footprint_perimeter": 151, "updated_at": "2017-09-01T21:17:07.585Z", "created_at": "2017-09-01T21:16:27.782Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -272,7 +272,7 @@ "footprint_perimeter": 192, "updated_at": "2017-09-01T21:17:07.612Z", "created_at": "2017-09-01T21:16:27.817Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -327,7 +327,7 @@ "footprint_perimeter": 101, "updated_at": "2017-09-01T21:17:07.618Z", "created_at": "2017-09-01T21:16:27.829Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -394,7 +394,7 @@ "footprint_perimeter": 447, "updated_at": "2017-09-01T21:17:07.518Z", "created_at": "2017-09-01T21:16:27.662Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -457,7 +457,7 @@ "footprint_perimeter": 464, "updated_at": "2017-09-01T21:17:07.524Z", "created_at": "2017-09-01T21:16:27.668Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -512,7 +512,7 @@ "footprint_perimeter": 187, "updated_at": "2017-09-01T21:17:07.529Z", "created_at": "2017-09-01T21:16:27.674Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -603,7 +603,7 @@ "footprint_perimeter": 459, "updated_at": "2017-09-01T21:17:07.513Z", "created_at": "2017-09-01T21:16:27.656Z", - "height": 6, + "floor_height": 6, "geometryType": "Polygon" } }, @@ -658,7 +658,7 @@ "footprint_perimeter": 65, "updated_at": "2017-09-01T21:17:07.573Z", "created_at": "2017-09-01T21:16:27.764Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -713,7 +713,7 @@ "footprint_perimeter": 84, "updated_at": "2017-09-01T21:17:07.602Z", "created_at": "2017-09-01T21:16:27.799Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -768,7 +768,7 @@ "footprint_perimeter": 61, "updated_at": "2017-09-01T21:17:07.623Z", "created_at": "2017-09-01T21:16:27.835Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -823,7 +823,7 @@ "footprint_perimeter": 61, "updated_at": "2017-09-01T21:17:07.629Z", "created_at": "2017-09-01T21:16:27.842Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -878,7 +878,7 @@ "footprint_perimeter": 74, "updated_at": "2017-09-01T21:17:07.635Z", "created_at": "2017-09-01T21:16:27.850Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -933,7 +933,7 @@ "footprint_perimeter": 201, "updated_at": "2017-09-01T21:17:07.502Z", "created_at": "2017-09-01T21:16:27.643Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -1052,7 +1052,7 @@ "footprint_perimeter": 2987, "updated_at": "2017-09-01T21:17:07.496Z", "created_at": "2017-09-01T21:16:27.637Z", - "height": 15, + "floor_height": 15, "geometryType": "Polygon" } }, @@ -1139,7 +1139,7 @@ "footprint_perimeter": 519, "updated_at": "2017-09-01T21:17:07.484Z", "created_at": "2017-09-01T21:16:27.622Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -1242,7 +1242,7 @@ "footprint_perimeter": 1304, "updated_at": "2017-09-01T21:17:07.490Z", "created_at": "2017-09-01T21:16:27.629Z", - "height": 6, + "floor_height": 6, "geometryType": "Polygon" } }, @@ -1381,7 +1381,7 @@ "footprint_perimeter": 1562, "updated_at": "2017-09-01T21:17:07.478Z", "created_at": "2017-09-01T21:16:27.616Z", - "height": 6, + "floor_height": 6, "geometryType": "Polygon" } }, @@ -1436,7 +1436,7 @@ "footprint_perimeter": 211, "updated_at": "2017-09-01T21:17:07.579Z", "created_at": "2017-09-01T21:16:27.776Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -1503,7 +1503,7 @@ "footprint_perimeter": 1422, "updated_at": "2017-09-01T21:17:07.640Z", "created_at": "2017-09-01T21:16:27.866Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -1662,7 +1662,7 @@ "footprint_perimeter": 1318, "updated_at": "2017-09-01T21:17:07.472Z", "created_at": "2017-09-01T21:16:27.607Z", - "height": 9, + "floor_height": 9, "geometryType": "Polygon" } }, @@ -1813,7 +1813,7 @@ "footprint_perimeter": 3325, "updated_at": "2017-09-01T21:17:07.465Z", "created_at": "2017-09-01T21:16:27.599Z", - "height": 12, + "floor_height": 12, "geometryType": "Polygon" } }, @@ -1940,7 +1940,7 @@ "footprint_perimeter": 2194, "updated_at": "2017-09-01T21:17:07.447Z", "created_at": "2017-09-01T21:16:27.579Z", - "height": 9, + "floor_height": 9, "geometryType": "Polygon" } }, @@ -2019,7 +2019,7 @@ "footprint_perimeter": 432, "updated_at": "2017-09-01T21:17:07.453Z", "created_at": "2017-09-01T21:16:27.586Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -2098,7 +2098,7 @@ "footprint_perimeter": 134, "updated_at": "2017-09-01T21:17:07.458Z", "created_at": "2017-09-01T21:16:27.592Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -2161,7 +2161,7 @@ "footprint_perimeter": 758, "updated_at": "2017-09-01T21:17:07.535Z", "created_at": "2017-09-01T21:16:27.687Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -2216,7 +2216,7 @@ "footprint_perimeter": 65, "updated_at": "2017-09-01T21:17:07.552Z", "created_at": "2017-09-01T21:16:27.707Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -2271,7 +2271,7 @@ "footprint_perimeter": 288, "updated_at": "2017-09-01T21:17:07.557Z", "created_at": "2017-09-01T21:16:27.746Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -2326,7 +2326,7 @@ "footprint_perimeter": 478, "updated_at": "2017-09-01T21:17:07.546Z", "created_at": "2017-09-01T21:16:27.700Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -2381,7 +2381,7 @@ "footprint_perimeter": 286, "updated_at": "2017-09-01T21:17:07.563Z", "created_at": "2017-09-01T21:16:27.753Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -2500,7 +2500,7 @@ "footprint_perimeter": 1467, "updated_at": "2017-09-01T21:17:07.541Z", "created_at": "2017-09-01T21:16:27.693Z", - "height": 6, + "floor_height": 6, "geometryType": "Polygon" } }, @@ -2555,7 +2555,7 @@ "footprint_perimeter": 97, "updated_at": "2017-09-01T21:17:07.568Z", "created_at": "2017-09-01T21:16:27.759Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, @@ -2610,7 +2610,7 @@ "footprint_perimeter": 96, "updated_at": "2017-09-01T21:17:07.596Z", "created_at": "2017-09-01T21:16:27.793Z", - "height": 3, + "floor_height": 3, "geometryType": "Polygon" } }, diff --git a/tests/test_translator.py b/tests/test_translator.py index c12a81e5c..95fdb6f89 100644 --- a/tests/test_translator.py +++ b/tests/test_translator.py @@ -179,5 +179,49 @@ def test_to_modelica_rc_order_4(self): # self.assertEqual(0, exitcode) +class GeoJSONUrbanOptExampleFileTranslatorTest(unittest.TestCase): + def test_init(self): + gj = GeoJSONTranslatorTest() + self.assertIsNotNone(gj) + + def test_from_geojson(self): + filename = os.path.abspath("tests/geojson/data/example_geojson_13buildings.json") + gj = GeoJsonModelicaTranslator.from_geojson(filename) + + self.assertEqual(len(gj.buildings), 13) + + def test_to_modelica_defaults(self): + self.results_path = os.path.abspath("tests/output/geojson_urbanopt") + if os.path.exists(self.results_path): + shutil.rmtree(self.results_path) + + filename = os.path.abspath("tests/geojson/data/example_geojson_13buildings.json") + gj = GeoJsonModelicaTranslator.from_geojson(filename) + sys_params = SystemParameters() + gj.set_system_parameters(sys_params) + gj.to_modelica("geojson_urbanopt", "tests/output") + + # go through the generated buildings and ensure that the resources are created + resource_names = [ + "InternalGains_Floor", + "InternalGains_ICT", + "InternalGains_Meeting", + "InternalGains_Office", + "InternalGains_Restroom", + "InternalGains_Storage", + ] + for b in gj.buildings: + for resource_name in resource_names: + # TEASER 0.7.2 used .txt for schedule files + path = os.path.join( + gj.scaffold.loads_path.files_dir, + "Resources", + "Data", + b.dirname, + f"{resource_name}.txt", + ) + self.assertTrue(os.path.exists(path), f"Path not found: {path}") + + if __name__ == "__main__": unittest.main() diff --git a/tests/test_translator_13buildings.py b/tests/test_translator_13buildings.py deleted file mode 100644 index 914d1d4df..000000000 --- a/tests/test_translator_13buildings.py +++ /dev/null @@ -1,203 +0,0 @@ -""" -**************************************************************************************************** -:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. - -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted -provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list of conditions -and the following disclaimer. - -Redistributions in binary form must reproduce the above copyright notice, this list of conditions -and the following disclaimer in the documentation and/or other materials provided with the -distribution. - -Neither the name of the copyright holder nor the names of its contributors may be used to endorse -or promote products derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER -IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**************************************************************************************************** -""" - -# import itertools -import os -import shutil -import unittest - -# from geojson_modelica_translator.modelica.modelica_runner import ( -# ModelicaRunner -# ) -from geojson_modelica_translator.geojson_modelica_translator import ( - GeoJsonModelicaTranslator -) -from geojson_modelica_translator.system_parameters.system_parameters import ( - SystemParameters -) - - -class GeoJSONTranslatorTest(unittest.TestCase): - def test_init(self): - gj = GeoJSONTranslatorTest() - self.assertIsNotNone(gj) - - def test_from_geojson(self): - # filename = os.path.abspath("tests/geojson/data/geojson_1.json") - filename = os.path.abspath(os.path.join(os.getcwd()+"/geojson/data/example_geojson_13buildings.json")) - - gj = GeoJsonModelicaTranslator.from_geojson(filename) - - self.assertEqual(len(gj.buildings), 13) - - def test_missing_geojson(self): - fn = "non-existent-path" - with self.assertRaises(Exception) as exc: - GeoJsonModelicaTranslator.from_geojson(fn) - self.assertEqual(f"GeoJSON file does not exist: {fn}", str(exc.exception)) - - def test_to_modelica_defaults(self): - self.results_path = os.path.abspath("tests_13buildings_modelica/output/geojson_13buildings") - if os.path.exists(self.results_path): - shutil.rmtree(self.results_path) - - # filename = os.path.abspath("tests/geojson/data/geojson_1.json") - filename = os.path.abspath(os.path.join(os.getcwd()+"/geojson/data/example_geojson_13buildings.json")) - - gj = GeoJsonModelicaTranslator.from_geojson(filename) - sys_params = SystemParameters() - gj.set_system_parameters(sys_params) - gj.to_modelica("geojson_13buildings", "tests_13buildings_modelica/output") - - # setup what we are going to check - """ - model_names = [ - "Floor", - "ICT", - "Meeting", - "Office", - "package", - "Restroom", - "Storage", - ] - """ - """ - for b in gj.buildings: - print ("Jing3: ", vars(gj)) - - building_paths = [ - os.path.join(gj.loads_path.files_dir, b.dirname) for b in gj.buildings - ] - """ - - """ - building_paths = os.listdir( os.path.join(os.getcwd()+"/tests_13buildings/output/geojson_13buildings/Loads/") ) - - - path_checks = [ - f"{os.path.sep.join(r)}.mo" - for r in itertools.product(building_paths, model_names) - ] - print("Jing3: ", path_checks ) - for p in path_checks: - self.assertTrue(os.path.exists(p), f"Path not found {p}") - """ - # go through the generated buildings and ensure that the resources are created - resource_names = [ - "InternalGains_Floor", - "InternalGains_ICT", - "InternalGains_Meeting", - "InternalGains_Office", - "InternalGains_Restroom", - "InternalGains_Storage", - ] - for b in gj.buildings: - for resource_name in resource_names: - # TEASER 0.7.2 used .txt for schedule files - path = os.path.join( - os.path.join(os.getcwd()+"/tests_13buildings_modelica/output/geojson_13buildings/Loads/"), - "Resources", - "Data", - b.dirname, - f"{resource_name}.txt", - ) - self.assertTrue(os.path.exists(path), f"Path not found: {path}") - - def test_to_modelica_rc_order_4(self): - self.results_path = os.path.abspath("tests_13buildings_rc4/output/geojson_13buildings") - if os.path.exists(self.results_path): - shutil.rmtree(self.results_path) - - # filename = os.path.abspath("tests/geojson/data/geojson_1.json") - filename = os.path.abspath(os.path.join(os.getcwd()+"/geojson/data/example_geojson_13buildings.json")) - - gj = GeoJsonModelicaTranslator.from_geojson(filename) - sys_params = SystemParameters.loadd( - {"buildings": {"default": {"load_model_parameters": {"rc": {"order": 4}}}}} - ) - self.assertEqual(len(sys_params.validate()), 0) - gj.set_system_parameters(sys_params) - - gj.to_modelica("geojson_13buildings", "tests_13buildings_rc4/output") - # setup what we are going to check - """ - model_names = [ - "Floor", - "ICT", - "Meeting", - "Office", - "package", - "Restroom", - "Storage", - ] - """ - """ - building_paths = [ - os.path.join(gj.loads_path.files_dir, b.dirname) for b in gj.buildings - ] - path_checks = [ - f"{os.path.sep.join(r)}.mo" - for r in itertools.product(building_paths, model_names) - ] - - for p in path_checks: - self.assertTrue(os.path.exists(p), f"Path not found: {p}") - """ - resource_names = [ - "InternalGains_Floor", - "InternalGains_ICT", - "InternalGains_Meeting", - "InternalGains_Office", - "InternalGains_Restroom", - "InternalGains_Storage", - ] - for b in gj.buildings: - for resource_name in resource_names: - # TEASER 0.7.2 used .txt for schedule files - path = os.path.join( - os.path.join(os.getcwd()+"/tests_13buildings_rc4/output/geojson_13buildings/Loads/"), - "Resources", - "Data", - b.dirname, - f"{resource_name}.txt", - ) - self.assertTrue(os.path.exists(path), f"Path not found: {path}") - - # # make sure the model can run using the ModelicaRunner class - # mr = ModelicaRunner() - # file_to_run = os.path.abspath( - # f'{self.results_path}/Loads/B5a6b99ec37f4de7f94020090/Office.mo' - # ) - # exitcode = mr.run_in_docker(file_to_run) - # self.assertEqual(0, exitcode) - - -if __name__ == "__main__": - unittest.main() From 1854af616258c75a120e43a782d9d20d17a25939 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Tue, 31 Mar 2020 18:50:06 -0600 Subject: [PATCH 61/62] dont need to test geojson 13 --- tests/geojson/test_geojson_13_buildings.py | 66 ---------------------- 1 file changed, 66 deletions(-) delete mode 100644 tests/geojson/test_geojson_13_buildings.py diff --git a/tests/geojson/test_geojson_13_buildings.py b/tests/geojson/test_geojson_13_buildings.py deleted file mode 100644 index 2e727e0a4..000000000 --- a/tests/geojson/test_geojson_13_buildings.py +++ /dev/null @@ -1,66 +0,0 @@ -""" -**************************************************************************************************** -:copyright (c) 2019-2020 URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. - -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted -provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list of conditions -and the following disclaimer. - -Redistributions in binary form must reproduce the above copyright notice, this list of conditions -and the following disclaimer in the documentation and/or other materials provided with the -distribution. - -Neither the name of the copyright holder nor the names of its contributors may be used to endorse -or promote products derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER -IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**************************************************************************************************** -""" - -import os -import unittest - -from geojson_modelica_translator.geojson.urbanopt_geojson import ( - UrbanOptGeoJson -) - - -class GeoJSONTest(unittest.TestCase): - def test_load_geojson(self): - # filename = os.path.abspath( "tests/geojson/data/example_geojson_13buildings.json") - filename = os.path.abspath(os.path.join(os.getcwd() + "/data/example_geojson_13buildings.json")) - json = UrbanOptGeoJson(filename) - self.assertIsNotNone(json.data) - # there are 13 buildings, plus 1 site, thus totally it is 14 features. - self.assertEqual(len(json.data.features), 14) - - def test_missing_file(self): - fn = "non-existent-path" - with self.assertRaises(Exception) as exc: - UrbanOptGeoJson(fn) - self.assertEqual(f"URBANopt GeoJSON file does not exist: {fn}", str(exc.exception)) - - def test_validate(self): - # filename = os.path.abspath("tests/geojson/data/example_geojson_13_buildings.json") - filename = os.path.abspath(os.path.join(os.getcwd()+"/data/example_geojson_13buildings.json")) - json = UrbanOptGeoJson(filename) - valid, results = json.validate() - self.assertFalse(valid) - self.assertEqual(len(results["building"]), 13) - # The id of buildings is numbered by 1-13, instead of uuid. - self.assertEqual(results["building"][0]["id"], "1") - - -if __name__ == "__main__": - unittest.main() From bd263b9865d39f8942cd2434e63fa2a882c29449 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Tue, 31 Mar 2020 19:06:20 -0600 Subject: [PATCH 62/62] flake8 --- tests/modelica/test_modelica_runner.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/modelica/test_modelica_runner.py b/tests/modelica/test_modelica_runner.py index 113a3be95..25672ccc1 100644 --- a/tests/modelica/test_modelica_runner.py +++ b/tests/modelica/test_modelica_runner.py @@ -34,9 +34,6 @@ from geojson_modelica_translator.modelica.modelica_runner import ModelicaRunner -from ..context import \ - geojson_modelica_translator # noqa - Do not remove this line - class ModelicaRunnerTest(unittest.TestCase): # create a run directory and copy in a project to test run