From df9b3472cc0522869f1f19f4924487098224af84 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Tue, 5 Mar 2024 11:01:39 -0700 Subject: [PATCH 01/35] add `cop_heat_pump_cooling` to required 5G ets params --- geojson_modelica_translator/system_parameters/schema.json | 1 + 1 file changed, 1 insertion(+) diff --git a/geojson_modelica_translator/system_parameters/schema.json b/geojson_modelica_translator/system_parameters/schema.json index 64ce004f8..0c8782fcc 100755 --- a/geojson_modelica_translator/system_parameters/schema.json +++ b/geojson_modelica_translator/system_parameters/schema.json @@ -1329,6 +1329,7 @@ "chilled_water_supply_temp", "hot_water_supply_temp", "cop_heat_pump_heating", + "cop_heat_pump_cooling", "pump_flow_rate", "pump_design_head", "ets_pump_flow_rate", From 30c7b6ed993f243e49ea7c273155b7696473cc9c Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Tue, 5 Mar 2024 11:12:18 -0700 Subject: [PATCH 02/35] change how we specify which type of district system to consider --- .../system_parameters/system_parameters.py | 99 +++++++++---------- 1 file changed, 48 insertions(+), 51 deletions(-) diff --git a/geojson_modelica_translator/system_parameters/system_parameters.py b/geojson_modelica_translator/system_parameters/system_parameters.py index e46f0829a..9760c9963 100644 --- a/geojson_modelica_translator/system_parameters/system_parameters.py +++ b/geojson_modelica_translator/system_parameters/system_parameters.py @@ -4,6 +4,7 @@ import json import logging import math +from contextlib import suppress from copy import deepcopy from pathlib import Path from typing import Union @@ -163,15 +164,13 @@ def get_param_by_id(self, id, jsonpath): for b in self.param_template.get("buildings", []): if b.get("geojson_id") == id: return self.get_param(jsonpath, data=b) - try: + with suppress(KeyError): + # If this dict key doesn't exist then either this is a 4G district, no id was passed, or it wasn't a ghe_id + # Don't crash or quit, just keep a stiff upper lip and carry on. district = self.param_template.get("district_system") for ghe in district["fifth_generation"]["ghe_parameters"]["ghe_specific_params"]: if ghe.get("ghe_id") == id: return self.get_param(jsonpath, data=ghe) - except KeyError: - # If this dict key doesn't exist then either this is a 4G district, no id was passed, or it wasn't a ghe_id - # Don't crash or quit, just keep a stiff upper lip and carry on. - pass if id is None: raise SystemExit("No id submitted. Please retry and include the appropriate id") @@ -703,7 +702,7 @@ def csv_to_sys_param(self, scenario_dir: Path, feature_file: Path, sys_param_filename: Path, - ghe=False, + district_type: str, overwrite=True, microgrid=False, **kwargs) -> None: @@ -714,8 +713,8 @@ def csv_to_sys_param(self, :param scenario_dir: Path, location/name of folder with uo_sdk results :param feature_file: Path, location/name of uo_sdk input file :param sys_param_filename: Path, location/name of system parameter file to be created + :param district_type: str, district type to model :param overwrite: Boolean, whether to overwrite existing sys-param file - :param ghe: Boolean, flag to add Ground Heat Exchanger properties to System Parameter File :param microgrid: Boolean, Optional. If set to true, also process microgrid fields :kwargs (optional): @@ -733,10 +732,7 @@ def csv_to_sys_param(self, if model_type == 'time_series': # TODO: delineate between time_series and time_series_massflow_rate - if microgrid: - param_template_path = Path(__file__).parent / 'time_series_microgrid_template.json' - else: - param_template_path = Path(__file__).parent / 'time_series_template.json' + param_template_path = Path(__file__).parent / 'time_series_template.json' elif model_type == 'spawn': # TODO: We should support spawn as well raise SystemExit('Spawn models are not implemented at this time.') @@ -752,8 +748,10 @@ def csv_to_sys_param(self, if Path(self.sys_param_filename).is_file() and not overwrite: raise SystemExit(f"Output file already exists and overwrite is False: {self.sys_param_filename}") - with open(param_template_path, "r") as f: - self.param_template = json.load(f) + if district_type not in ['steam', '4G', '5G', '5G_ghe']: + raise SystemExit(f"{district_type} is not a valid district type. Available options are: ['steam', '4G', '5G', '5G_ghe']") + + self.param_template = json.loads(param_template_path.read_text()) measure_list = [] @@ -770,8 +768,7 @@ def csv_to_sys_param(self, # Get each building feature id from the SDK FeatureFile building_ids = [] - with open(feature_file) as json_file: - sdk_input = json.load(json_file) + sdk_input = json.loads(feature_file.read_text()) weather_filename = sdk_input['project']['weather_filename'] weather_path = self.sys_param_filename.parent / weather_filename for feature in sdk_input['features']: @@ -792,9 +789,12 @@ def csv_to_sys_param(self, # Make sys_param template entries for each feature_id building_list = [] for building in building_ids: - feature_info = deepcopy(self.param_template['buildings'][0]) - feature_info['geojson_id'] = str(building) - building_list.append(feature_info) + if "4G" in district_type: + building_params = deepcopy(self.param_template['buildings'][0]) + elif "5G" in district_type: + building_params = deepcopy(self.param_template['buildings'][1]) + building_params['geojson_id'] = str(building) + building_list.append(building_params) # Grab the modelica file for the each Feature, and add it to the appropriate building dict district_nominal_massflow_rate = 0 @@ -813,21 +813,6 @@ def csv_to_sys_param(self, to_file_path = to_file_path.relative_to(self.rel_path) building['load_model_parameters']['time_series']['filepath'] = str(to_file_path) - if (measure_file_path.suffix == '.csv') and ('_export_time_series_modelica' in str(measure_folder_name)): - massflow_rate_df = pd.read_csv(measure_file_path) - try: - building_nominal_massflow_rate = round( - massflow_rate_df['massFlowRateHeating'].max(), - 3) # round max to 3 decimal places - # Force casting to float even if building_nominal_massflow_rate == 0 - # FIXME: This might be related to building_type == `lodging` for non-zero building percentages - building['ets_indirect_parameters']['nominal_mass_flow_building'] = float(building_nominal_massflow_rate) - except KeyError: - # If massFlowRateHeating is not in the export_time_series_modelica output, just skip this step. - # It probably won't be in the export for hpxml residential buildings, at least as of 2022-06-29 - logger.info("mass-flow-rate heating is not present. It is not expected in residential buildings. Skipping.") - continue - district_nominal_massflow_rate += building_nominal_massflow_rate if measure_file_path.suffix == '.csv' and measure_folder_name.endswith('_export_modelica_loads'): try: # only use the one column to make the df small @@ -836,6 +821,22 @@ def csv_to_sys_param(self, continue max_electricity_load = int(building_loads['ElectricityFacility'].max()) building['load_model_parameters']['time_series']['max_electrical_load'] = max_electricity_load + if "4G" in district_type: + if (measure_file_path.suffix == '.csv') and ('_export_time_series_modelica' in str(measure_folder_name)): + massflow_rate_df = pd.read_csv(measure_file_path) + try: + building_nominal_massflow_rate = round( + massflow_rate_df['massFlowRateHeating'].max(), + 3) # round max to 3 decimal places + # Force casting to float even if building_nominal_massflow_rate == 0 + # FIXME: This might be related to building_type == `lodging` for non-zero building percentages + building['ets_indirect_parameters']['nominal_mass_flow_building'] = float(building_nominal_massflow_rate) + except KeyError: + # If massFlowRateHeating is not in the export_time_series_modelica output, just skip this step. + # It probably won't be in the export for hpxml residential buildings, at least as of 2022-06-29 + logger.info("mass-flow-rate heating is not present. It is not expected in residential buildings. Skipping.") + continue + district_nominal_massflow_rate += building_nominal_massflow_rate # Remove template buildings that weren't used or don't have successful simulations with modelica outputs # TODO: Another place where we only support time series for now. @@ -849,7 +850,8 @@ def csv_to_sys_param(self, # Update specific sys-param settings for each building for building in building_list: - building['ets_indirect_parameters']['nominal_mass_flow_district'] = district_nominal_massflow_rate + if "4G" in district_type: + building['ets_indirect_parameters']['nominal_mass_flow_district'] = district_nominal_massflow_rate feature_opt_file = scenario_dir / building['geojson_id'] / 'feature_reports' / 'feature_optimization.json' if microgrid and not feature_opt_file.exists(): logger.debug(f"No feature optimization file found for {building['geojson_id']}. Skipping REopt for this building") @@ -860,7 +862,7 @@ def csv_to_sys_param(self, self.param_template['buildings'] = building_list # Update district sys-param settings - # Parens are to allow the line break + # Process community microgrid inputs to_file_path = mos_weather_path if self.rel_path: to_file_path = to_file_path.relative_to(self.rel_path) @@ -868,24 +870,21 @@ def csv_to_sys_param(self, if microgrid and not feature_opt_file.exists(): logger.warn("Microgrid requires OpenDSS and REopt feature optimization for full functionality.\n" "Run opendss and reopt-feature post-processing in the UO SDK for a full-featured microgrid.") - try: - self.process_microgrid_inputs(scenario_dir) - except UnboundLocalError: - raise SystemExit(f"\nError: No scenario_optimization.json file found in {scenario_dir}\n" - "Perhaps you haven't run REopt post-processing step in the UO sdk?") + elif microgrid and feature_opt_file.exists(): + try: + self.process_microgrid_inputs(scenario_dir) + except UnboundLocalError: + raise SystemExit(f"\nError: No scenario_optimization.json file found in {scenario_dir}\n" + "Perhaps you haven't run REopt post-processing step in the UO sdk?") # Update ground heat exchanger properties if true - if ghe: + if "5G_ghe" in district_type: ghe_ids = [] # add properties from the feature file - with open(feature_file) as json_file: - sdk_input = json.load(json_file) for feature in sdk_input['features']: if feature['properties']['type'] == 'District System': - try: + with suppress(KeyError): district_system_type = feature['properties']['district_system_type'] - except KeyError: - pass if district_system_type == 'Ground Heat Exchanger': length, width = self.calculate_dimensions( feature['properties']['footprint_area'], feature['properties']['footprint_perimeter']) @@ -916,12 +915,10 @@ def csv_to_sys_param(self, # remove fourth generation district system type del self.param_template['district_system']['fourth_generation'] - else: - # remove fifth generation district system type if it exists in template and ghe is not true - try: + elif "4G" in district_type or "steam" in district_type: + # remove fifth generation district system type + with suppress(KeyError): del self.param_template['district_system']['fifth_generation'] - except KeyError: - pass # save the file to disk self.save() From 5189d3f441cff1627f42d2fe9abcbad8ad2ee69f Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Tue, 5 Mar 2024 11:13:32 -0700 Subject: [PATCH 03/35] put all time_series sys-param template data into one file --- .../time_series_template.json | 58 ++++++++++++++++++- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/geojson_modelica_translator/system_parameters/time_series_template.json b/geojson_modelica_translator/system_parameters/time_series_template.json index 62cd569f6..359101bf3 100644 --- a/geojson_modelica_translator/system_parameters/time_series_template.json +++ b/geojson_modelica_translator/system_parameters/time_series_template.json @@ -1,7 +1,7 @@ { "buildings": [ { - "geojson_id": "0", + "geojson_id": "4G", "load_model": "time_series", "load_model_parameters": { "time_series": { @@ -40,7 +40,49 @@ "cooling_controller_y_min": 0, "heating_controller_y_max": 1, "heating_controller_y_min": 0 - } + }, + "photovoltaic_panels": [], + "diesel_generators": [], + "battery_banks": [] + }, + { + "geojson_id": "5G", + "load_model": "time_series", + "load_model_parameters": { + "time_series": { + "filepath": "To be populated", + "delta_temp_air_cooling": 10, + "delta_temp_air_heating": 18, + "has_liquid_cooling": true, + "has_liquid_heating": true, + "has_electric_cooling": false, + "has_electric_heating": false, + "max_electrical_load": 0, + "temp_chw_return": 12, + "temp_chw_supply": 7, + "temp_hw_return": 35, + "temp_hw_supply": 40, + "temp_setpoint_cooling": 24, + "temp_setpoint_heating": 20 + } + }, + "ets_model": "Fifth Gen Heat Pump", + "fifth_gen_ets_parameters": { + "supply_water_temperature_building": 15, + "chilled_water_supply_temp": 5, + "hot_water_supply_temp": 50, + "cop_heat_pump_heating": 2.5, + "cop_heat_pump_cooling": 3.5, + "pump_flow_rate": 0.01, + "pump_design_head": 150000, + "ets_pump_flow_rate": 0.0005, + "ets_pump_head": 10000, + "fan_design_flow_rate": 0.25, + "fan_design_head": 150 + }, + "photovoltaic_panels": [], + "diesel_generators": [], + "battery_banks": [] } ], "district_system": { @@ -144,5 +186,15 @@ } } }, - "weather": "../../data_shared/USA_CA_San.Francisco.Intl.AP.724940_TMY3.mos" + "weather": "../../data_shared/USA_CA_San.Francisco.Intl.AP.724940_TMY3.mos", + "electrical_grid": {}, + "photovoltaic_panels": [], + "wind_turbines": [], + "combined_heat_and_power_systems": [], + "capacitor_banks": [], + "substations": [], + "transformers": [], + "power_converters": [], + "distribution_lines": [], + "battery_banks": [] } From fdcf41feaa63e140ae7309509ea3a43f9c71fdc2 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Tue, 5 Mar 2024 11:13:58 -0700 Subject: [PATCH 04/35] remove now-outdated microgrid sys-param template --- .../time_series_microgrid_template.json | 97 ------------------- 1 file changed, 97 deletions(-) delete mode 100644 geojson_modelica_translator/system_parameters/time_series_microgrid_template.json diff --git a/geojson_modelica_translator/system_parameters/time_series_microgrid_template.json b/geojson_modelica_translator/system_parameters/time_series_microgrid_template.json deleted file mode 100644 index cb2ace171..000000000 --- a/geojson_modelica_translator/system_parameters/time_series_microgrid_template.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "buildings": [ - { - "geojson_id": "0", - "load_model": "time_series", - "load_model_parameters": { - "time_series": { - "filepath": "To be populated", - "delta_temp_air_cooling": 10, - "delta_temp_air_heating": 18, - "has_liquid_cooling": true, - "has_liquid_heating": true, - "has_electric_cooling": false, - "has_electric_heating": false, - "max_electrical_load": 0, - "temp_chw_return": 12, - "temp_chw_supply": 7, - "temp_hw_return": 35, - "temp_hw_supply": 40, - "temp_setpoint_cooling": 24, - "temp_setpoint_heating": 20 - } - }, - "ets_model": "Indirect Heating and Cooling", - "ets_indirect_parameters": { - "heat_flow_nominal": 8000, - "heat_exchanger_efficiency": 0.8, - "nominal_mass_flow_district": 0.5, - "nominal_mass_flow_building": 0.5, - "valve_pressure_drop": 6000, - "heat_exchanger_secondary_pressure_drop": 500, - "heat_exchanger_primary_pressure_drop": 500, - "cooling_supply_water_temperature_building": 7, - "heating_supply_water_temperature_building": 50, - "delta_temp_chw_building": 5, - "delta_temp_chw_district": 8, - "delta_temp_hw_building": 15, - "delta_temp_hw_district": 20, - "cooling_controller_y_max": 1, - "cooling_controller_y_min": 0, - "heating_controller_y_max": 1, - "heating_controller_y_min": 0 - }, - "photovoltaic_panels": [], - "diesel_generators": [], - "battery_banks": [] - } - ], - "district_system": { - "fourth_generation": { - "central_cooling_plant_parameters": { - "heat_flow_nominal": 7999, - "cooling_tower_fan_power_nominal": 4999, - "mass_chw_flow_nominal": 9.9, - "chiller_water_flow_minimum": 9.9, - "mass_cw_flow_nominal": 9.9, - "chw_pump_head": 300000, - "cw_pump_head": 200000, - "pressure_drop_chw_nominal": 5999, - "pressure_drop_cw_nominal": 5999, - "pressure_drop_setpoint": 49999, - "temp_setpoint_chw": 6, - "pressure_drop_chw_valve_nominal": 5999, - "pressure_drop_cw_pum_nominal": 5999, - "temp_air_wb_nominal": 24.9, - "temp_cw_in_nominal": 34.9, - "cooling_tower_water_temperature_difference_nominal": 6.56, - "delta_temp_approach": 3.25, - "ratio_water_air_nominal": 0.6 - }, - "central_heating_plant_parameters": { - "heat_flow_nominal": 8001, - "mass_hhw_flow_nominal": 1, - "boiler_water_flow_minimum": 0.1, - "pressure_drop_hhw_nominal": 55001, - "pressure_drop_setpoint": 50000, - "temp_setpoint_hhw": 54, - "pressure_drop_hhw_valve_nominal": 6001, - "chp_installed": false - }, - "central_pump_parameters": { - "pump_design_head": 60000 - } - } - }, - "weather": "../../data_shared/USA_CA_San.Francisco.Intl.AP.724940_TMY3.mos", - "electrical_grid": {}, - "photovoltaic_panels": [], - "wind_turbines": [], - "combined_heat_and_power_systems": [], - "capacitor_banks": [], - "substations": [], - "transformers": [], - "power_converters": [], - "distribution_lines": [], - "battery_banks": [] -} From 9d7146719a7fd454a9c3028d8125d174ba6cb53d Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Tue, 5 Mar 2024 11:14:30 -0700 Subject: [PATCH 05/35] update cli to use new paradigm for specifying district type --- management/uo_des.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/management/uo_des.py b/management/uo_des.py index 4a50b3c34..c8199ada0 100644 --- a/management/uo_des.py +++ b/management/uo_des.py @@ -35,6 +35,10 @@ def cli(): "feature_file", type=click.Path(exists=True, file_okay=True, dir_okay=False), ) +@click.argument( + "district_type", + default='4G', +) @click.argument( "model_type", default='time_series', @@ -53,18 +57,11 @@ def cli(): help="If specified, microgrid inputs will be added to system parameters file", default=False ) -@click.option( - '-g', - '--ghe', - is_flag=True, - help="If specified, Ground Heat Exchanger properties will be added to System Parameters File", - default=False -) def build_sys_param(model_type: str, sys_param_filename: Path, scenario_file: Path, feature_file: Path, - ghe: bool, + district_type: str, overwrite: bool, microgrid: bool ): @@ -77,6 +74,8 @@ def build_sys_param(model_type: str, FEATURE_FILE: Path to sdk json feature file with data about the buildings. + DISTRICT_TYPE: selection for which kind of simulation this sys-param file will support. + \b MODEL_TYPE: selection for which kind of simulation this sys-param file will support. Valid choices for MODEL_TYPE: "time_series" @@ -86,7 +85,7 @@ def build_sys_param(model_type: str, :param sys_param_filename: Path, location & name of json output file to save :param scenario_file: Path, location of SDK scenario_file :param feature_file: Path, location of SDK feature_file - :param ghe: Boolean, flag to add Ground Heat Exchanger properties to System Parameter File + :param district_type: string, district type to model :param overwrite: Boolean, flag to overwrite an existing file of the same name/location :param microgrid: Boolean, flag to add Microgrid properties to System Parameter File """ @@ -100,7 +99,7 @@ def build_sys_param(model_type: str, sys_param_filename=Path(sys_param_filename), scenario_dir=Path(scenario_dir), feature_file=Path(feature_file), - ghe=ghe, + district_type=district_type, overwrite=overwrite, microgrid=microgrid ) From 389d50c79cdfbb78c2aab45765142b837ffb8808 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Tue, 5 Mar 2024 11:14:55 -0700 Subject: [PATCH 06/35] update tests to use new system type designation --- tests/management/test_uo_des.py | 4 ++-- tests/system_parameters/test_system_parameters.py | 13 ++++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/tests/management/test_uo_des.py b/tests/management/test_uo_des.py index fb0ff80bc..1a0f03930 100644 --- a/tests/management/test_uo_des.py +++ b/tests/management/test_uo_des.py @@ -59,7 +59,7 @@ def test_cli_builds_sys_params_with_ghe(self): str(self.sys_param_path), str(self.scenario_file_path.resolve()), str(self.feature_file_path_ghe.resolve()), - '--ghe' + '5G_ghe' ] ) @@ -145,7 +145,7 @@ def test_cli_makes_model_with_ghe(self): str(self.sys_param_path), str(self.scenario_file_path.resolve()), str(self.feature_file_path_ghe.resolve()), - '--ghe' + '5G_ghe' ] ) diff --git a/tests/system_parameters/test_system_parameters.py b/tests/system_parameters/test_system_parameters.py index 11188ad77..b442376e4 100644 --- a/tests/system_parameters/test_system_parameters.py +++ b/tests/system_parameters/test_system_parameters.py @@ -229,6 +229,7 @@ def test_missing_files(self): model_type='time_series', scenario_dir=missing_scenario_dir, feature_file=self.feature_file, + district_type="4G", sys_param_filename=output_sys_param_file) self.assertIn( f"Unable to find your scenario. The path you provided was: {missing_scenario_dir}", str(context.exception)) @@ -239,6 +240,7 @@ def test_missing_files(self): model_type='time_series', scenario_dir=self.scenario_dir, feature_file=missing_feature_file, + district_type="4G", sys_param_filename=output_sys_param_file) self.assertIn( f"Unable to find your feature file. The path you provided was: {missing_feature_file}", str(context.exception)) @@ -251,6 +253,7 @@ def test_csv_to_sys_param_does_not_overwrite(self): model_type='time_series', scenario_dir=self.scenario_dir, feature_file=self.feature_file, + district_type="4G", sys_param_filename=output_sys_param_file, overwrite=True) sp = SystemParameters() @@ -258,6 +261,7 @@ def test_csv_to_sys_param_does_not_overwrite(self): model_type='time_series', scenario_dir=self.scenario_dir, feature_file=self.feature_file, + district_type="4G", sys_param_filename=output_sys_param_file, overwrite=False) self.assertIn("Output file already exists and overwrite is False:", str(context.exception)) @@ -269,6 +273,7 @@ def test_csv_to_sys_param(self): model_type='time_series', scenario_dir=self.scenario_dir, feature_file=self.feature_file, + district_type="4G", sys_param_filename=output_sys_param_file) # debug @@ -285,12 +290,11 @@ def test_csv_to_sys_param_ghe(self): model_type='time_series', scenario_dir=self.scenario_dir, feature_file=self.feature_file, - ghe=True, + district_type="5G_ghe", sys_param_filename=output_sys_param_file) self.assertTrue(output_sys_param_file.is_file()) - with open(output_sys_param_file, "r") as f: - sys_param_data = json.load(f) + sys_param_data = json.loads(output_sys_param_file.read_text()) # ghe self.assertTrue(sys_param_data['district_system']['fifth_generation']['ghe_parameters']) @@ -302,6 +306,7 @@ def test_csv_to_sys_param_microgrid(self): model_type='time_series', scenario_dir=self.microgrid_scenario_dir, feature_file=self.microgrid_feature_file, + district_type="4G", sys_param_filename=output_sys_param_file, microgrid=True) self.assertTrue(output_sys_param_file.exists()) @@ -330,6 +335,7 @@ def test_validate_sys_param_template(self): sp.csv_to_sys_param( scenario_dir=self.scenario_dir, feature_file=self.feature_file, + district_type="4G", sys_param_filename=output_sys_param_file) self.assertIn("csv_to_sys_param() missing 1 required positional argument: 'model_type'", str(context.exception)) @@ -340,6 +346,7 @@ def test_validate_sys_param_template(self): model_type=bogus_template_type, scenario_dir=self.scenario_dir, feature_file=self.feature_file, + district_type="4G", sys_param_filename=output_sys_param_file) self.assertIn(f"No template found. {bogus_template_type} is not a valid template", str(context.exception)) From 4332082b7a8d1f00bc671d3e82976d6eed59afab Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Wed, 6 Mar 2024 08:21:36 -0700 Subject: [PATCH 07/35] improve comment in sys-param.py --- .../system_parameters/system_parameters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geojson_modelica_translator/system_parameters/system_parameters.py b/geojson_modelica_translator/system_parameters/system_parameters.py index e8b880b3c..cf2208bf7 100644 --- a/geojson_modelica_translator/system_parameters/system_parameters.py +++ b/geojson_modelica_translator/system_parameters/system_parameters.py @@ -942,7 +942,7 @@ def csv_to_sys_param( if "5G_ghe" in district_type: self.process_ghe_inputs(scenario_dir) elif "4G" in district_type or "steam" in district_type: - # remove fifth generation district system type if it exists in template and ghe is not true + # remove fifth generation district system type if it's not appropriate with suppress(KeyError): del self.param_template["district_system"]["fifth_generation"] From 1e54cef1eb9953fbc577f1d0cd09da1a2fd40284 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Wed, 6 Mar 2024 09:27:33 -0700 Subject: [PATCH 08/35] add steam boiler parameters to schema --- .../system_parameters/schema.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/geojson_modelica_translator/system_parameters/schema.json b/geojson_modelica_translator/system_parameters/schema.json index 0c8782fcc..c047f186a 100644 --- a/geojson_modelica_translator/system_parameters/schema.json +++ b/geojson_modelica_translator/system_parameters/schema.json @@ -841,6 +841,11 @@ "description": "Central heating plant with maximum number of two boilers. Parameters associated with the model.", "type": "object", "properties": { + "boiler_efficiency_nominal": { + "description": "Boiler efficiency (fraction)", + "type": "number", + "default": 0.9 + }, "heat_flow_nominal": { "description": "Nominal district heating load. (W)", "type": "number", @@ -871,6 +876,11 @@ "type": "number", "default": 6000 }, + "steam_pressure_setpoint": { + "description": "The heating water circuit pressure drop setpoint. (Pa)", + "type": "number", + "default": 500000 + }, "chp_installed": { "description": "True if heating plant is a Combined Heat and Power plant", "type": "boolean" From b758d9e94fb924563d02ceae65b583546a3f3180 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Mon, 11 Mar 2024 13:39:01 -0600 Subject: [PATCH 09/35] new steam boiler template for 1G districts --- .../plants/templates/SteamBoiler.mot | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 geojson_modelica_translator/model_connectors/plants/templates/SteamBoiler.mot diff --git a/geojson_modelica_translator/model_connectors/plants/templates/SteamBoiler.mot b/geojson_modelica_translator/model_connectors/plants/templates/SteamBoiler.mot new file mode 100644 index 000000000..e76149fbc --- /dev/null +++ b/geojson_modelica_translator/model_connectors/plants/templates/SteamBoiler.mot @@ -0,0 +1,59 @@ +within {{ project_name }}.Plants; +model SteamBoiler + // "Single steam boiler model for GMT level 1 testing in district heating application" + // Central heating plant + parameter Real boiler_efficiency[:]={{data["boiler_efficiency"]}} + "Coefficients for boiler efficiency curve"; + // Steam properties + parameter Modelica.Units.SI.AbsolutePressure pSat_GMT={{data["Saturatation pressure"]}} + "Saturation pressure, high pressure"; + parameter Modelica.Units.SI.AbsolutePressure pLow_GMT={{data["Reduced pressure"]}} + "Reduced pressure, after PRV"; + parameter Modelica.Units.SI.Temperature TSat_GMT=MediumSte.saturationTemperature( + pSat_GMT) + "Saturation temperature, at high pressure"; + // Building + parameter Integer N_GMT={{data["number_of_loads"]}} + "Number of buildings"; + // Distribution Network + parameter Modelica.Units.SI.PressureDifference dp_nominal_GMT={{data["condensate_pressure_drop_nominal"]}} + "Pressure drop of distribution at nominal mass flow rate"; + extends Buildings.Experimental.DHC.Examples.Steam.SingleBoiler( + // Central heating plant + pla( + boi( + a=boiler_efficiency + "Boiler efficiency")), + pSat=pSat_GMT, + pLow=pLow_GMT, + TSat=TSat_GMT, + QBui_flow_nominal=QBui_flow_nominal_GMT, + N=N_GMT, + dp_nominal=dp_nominal_GMT); + annotation ( + Icon( + coordinateSystem( + preserveAspectRatio=false)), + Diagram( + coordinateSystem( + preserveAspectRatio=false)), + experiment( + StopTime=86400, + Tolerance=1e-06), + Documentation( + info=" +

This model validates the steam boiler in district heating system template model implemented in + +Buildings.Experimental.DHC.Examples.Steam.SingleBoiler.mot. +

+", + revisions=" +
    +
  • + +February 01, 2024 by Zhanwei He:
    +First implementation. +
  • +
+")); +end SteamBoiler; From 1a34187715370e1eff8f2e6239044a5f674e39a5 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Mon, 11 Mar 2024 13:42:33 -0600 Subject: [PATCH 10/35] wip: central steam plant characteristics --- .../system_parameters/schema.json | 57 +++++++++++++++---- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/geojson_modelica_translator/system_parameters/schema.json b/geojson_modelica_translator/system_parameters/schema.json index c047f186a..105bc27b9 100644 --- a/geojson_modelica_translator/system_parameters/schema.json +++ b/geojson_modelica_translator/system_parameters/schema.json @@ -21,6 +21,9 @@ "description": "Parameters associated with district systems.", "type": "object", "oneOf": [ + { + "$ref": "#/definitions/first_generation_def" + }, { "$ref": "#/definitions/fourth_generation_def" }, @@ -841,11 +844,6 @@ "description": "Central heating plant with maximum number of two boilers. Parameters associated with the model.", "type": "object", "properties": { - "boiler_efficiency_nominal": { - "description": "Boiler efficiency (fraction)", - "type": "number", - "default": 0.9 - }, "heat_flow_nominal": { "description": "Nominal district heating load. (W)", "type": "number", @@ -876,11 +874,6 @@ "type": "number", "default": 6000 }, - "steam_pressure_setpoint": { - "description": "The heating water circuit pressure drop setpoint. (Pa)", - "type": "number", - "default": 500000 - }, "chp_installed": { "description": "True if heating plant is a Combined Heat and Power plant", "type": "boolean" @@ -920,6 +913,22 @@ } } }, + "central_steam_plant_parameters": { + "description": "Central steam heating plant.", + "type": "object", + "properties": { + "boiler_efficiency_nominal": { + "description": "Boiler efficiency (fraction)", + "type": "number", + "default": 0.7 + }, + "steam_pressure_setpoint": { + "description": "The heating water circuit pressure drop setpoint. (Pa)", + "type": "number", + "default": 500000 + } + } + }, "ghe_parameters": { "description": "Parameters associated with Ground Heat Exchangers.", "type": "object", @@ -1200,6 +1209,30 @@ } } }, + "first_generation_def": { + "properties": { + "first_generation": { + "properties": { + "central_steam_plant_parameters": { + "$ref": "#/definitions/central_steam_plant_parameters" + }, + "central_pump_parameters": { + "description": "Parameters for central pump", + "type": "object", + "properties": { + "pump_design_head": { + "description": "Central pump nominal pressure (Pa)", + "type": "number" + } + } + } + } + } + }, + "required": [ + "first_generation" + ] + }, "fourth_generation_def": { "properties": { "fourth_generation": { @@ -1229,7 +1262,7 @@ "type": "object", "properties": { "pump_design_head": { - "description": "Measured in Pa", + "description": "Central pump nominal pressure (Pa)", "type": "number" } } @@ -1264,7 +1297,7 @@ "type": "object", "properties": { "pump_design_head": { - "description": "Measured in Pa.", + "description": "Central pump nominal pressure (Pa)", "type": "number" } } From e17a167bc50e2b970aa8b292337e9603273bd3e7 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Fri, 15 Mar 2024 14:47:46 -0600 Subject: [PATCH 11/35] real path for boiler_efficiency in SteamBoiler template --- .../model_connectors/plants/templates/SteamBoiler.mot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geojson_modelica_translator/model_connectors/plants/templates/SteamBoiler.mot b/geojson_modelica_translator/model_connectors/plants/templates/SteamBoiler.mot index e76149fbc..b932e2351 100644 --- a/geojson_modelica_translator/model_connectors/plants/templates/SteamBoiler.mot +++ b/geojson_modelica_translator/model_connectors/plants/templates/SteamBoiler.mot @@ -2,7 +2,7 @@ within {{ project_name }}.Plants; model SteamBoiler // "Single steam boiler model for GMT level 1 testing in district heating application" // Central heating plant - parameter Real boiler_efficiency[:]={{data["boiler_efficiency"]}} + parameter Real boiler_efficiency[:]={{ data["nominal_values"]["boiler_efficiency"] }} "Coefficients for boiler efficiency curve"; // Steam properties parameter Modelica.Units.SI.AbsolutePressure pSat_GMT={{data["Saturatation pressure"]}} From 6792301d4ec97715bc122954857be3c45608bcce Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Fri, 15 Mar 2024 14:49:03 -0600 Subject: [PATCH 12/35] new code to read SteamBoiler template --- .../model_connectors/plants/steam_boiler.py | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 geojson_modelica_translator/model_connectors/plants/steam_boiler.py diff --git a/geojson_modelica_translator/model_connectors/plants/steam_boiler.py b/geojson_modelica_translator/model_connectors/plants/steam_boiler.py new file mode 100644 index 000000000..122e7d8ae --- /dev/null +++ b/geojson_modelica_translator/model_connectors/plants/steam_boiler.py @@ -0,0 +1,63 @@ +# :copyright (c) URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +# See also https://github.com/urbanopt/geojson-modelica-translator/blob/develop/LICENSE.md + +from pathlib import Path + +from modelica_builder.package_parser import PackageParser + +from geojson_modelica_translator.model_connectors.plants.plant_base import PlantBase +from geojson_modelica_translator.utils import simple_uuid + + +class SteamPlant(PlantBase): + model_name = "SteamBoiler" + + def __init__(self, system_parameters): + super().__init__(system_parameters) + self.id = "steBoi" + simple_uuid() + + def to_modelica(self, scaffold): + """ + Create timeSeries models based on the data in the buildings and geojsons + + :param scaffold: Scaffold object, Scaffold of the entire directory of the project. + """ + + template_data = { + "nominal_values": { + "boiler_efficiency": self.system_parameters.get_param( + "$.district_system.first_generation.central_steam_plant_parameters.boiler_efficiency" + ) + } + } + + plant_template = self.template_env.get_template("SteamBoiler.mot") + self.run_template( + template=plant_template, + save_file_name=Path(scaffold.plants_path.files_dir) / "SteamBoiler.mo", + project_name=scaffold.project_name, + data=template_data, + ) + + self.copy_required_mo_files( + dest_folder=scaffold.plants_path.files_dir, within=f"{scaffold.project_name}.Plants" + ) + + package = PackageParser(scaffold.project_path) + if "Plants" not in package.order: + package.add_model("Plants") + package.save() + + package_models = ["SteamBoiler"] + [Path(mo).stem for mo in self.required_mo_files] + plants_package = PackageParser(scaffold.plants_path.files_dir) + if plants_package.order_data is None: + plants_package = PackageParser.new_from_template( + path=scaffold.plants_path.files_dir, name="Plants", order=package_models, within=scaffold.project_name + ) + else: + for model_name in package_models: + plants_package.add_model(model_name) + plants_package.save() + + def get_modelica_type(self, scaffold): + return "Plants.SteamBoiler" From 4f8494992766484872ee04955f004d67a7b1559b Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Fri, 15 Mar 2024 16:40:00 -0600 Subject: [PATCH 13/35] add another property in steam boiler schema --- geojson_modelica_translator/system_parameters/schema.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/geojson_modelica_translator/system_parameters/schema.json b/geojson_modelica_translator/system_parameters/schema.json index 8aaf84a4c..aff193618 100644 --- a/geojson_modelica_translator/system_parameters/schema.json +++ b/geojson_modelica_translator/system_parameters/schema.json @@ -923,9 +923,14 @@ "default": 0.7 }, "steam_pressure_setpoint": { - "description": "The heating water circuit pressure drop setpoint. (Pa)", + "description": "The saturation pressure, high pressure setpoint. (Pa)", "type": "number", "default": 500000 + }, + "condensate_pressure_drop_nominal": { + "description": "Nominal pressure drop along the condensate return pipe. (Pa)", + "type": "number", + "default": 6000 } } }, From 2d36ae61c10888d994d5707c854609bbff430b80 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Fri, 15 Mar 2024 16:40:39 -0600 Subject: [PATCH 14/35] test file & sys-param data for steam boiler --- .../data/time_series_sys_params_steam.json | 94 +++++++++++++++++++ tests/model_connectors/test_steam_boiler.py | 75 +++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 tests/model_connectors/data/time_series_sys_params_steam.json create mode 100644 tests/model_connectors/test_steam_boiler.py diff --git a/tests/model_connectors/data/time_series_sys_params_steam.json b/tests/model_connectors/data/time_series_sys_params_steam.json new file mode 100644 index 000000000..06270e4f2 --- /dev/null +++ b/tests/model_connectors/data/time_series_sys_params_steam.json @@ -0,0 +1,94 @@ +{ + "weather": "../../data_shared/USA_CA_San.Francisco.Intl.AP.724940_TMY3.mos", + "buildings": [ + { + "geojson_id": "5a6b99ec37f4de7f94020090", + "load_model": "time_series", + "load_model_parameters": { + "time_series": { + "filepath": "../../data_shared/time_series_large_office_2013_5B.mos", + "delta_temp_air_cooling": 10, + "delta_temp_air_heating": 18, + "has_liquid_heating": true, + "has_electric_heating": false, + "has_liquid_cooling": true, + "has_electric_cooling": false, + "temp_chw_supply": 7, + "temp_chw_return": 12, + "temp_hw_supply": 40, + "temp_hw_return": 35, + "temp_setpoint_cooling": 24, + "temp_setpoint_heating": 20 + } + }, + "ets_model": "Indirect Heating and Cooling", + "ets_indirect_parameters": { + "heat_flow_nominal": 8000, + "heat_exchanger_efficiency": 0.8, + "nominal_mass_flow_district": 0.5, + "nominal_mass_flow_building": 0.5, + "valve_pressure_drop": 6000, + "heat_exchanger_secondary_pressure_drop": 500, + "heat_exchanger_primary_pressure_drop": 500, + "cooling_supply_water_temperature_building": 7, + "heating_supply_water_temperature_building": 50, + "delta_temp_chw_building": 5, + "delta_temp_chw_district": 8, + "delta_temp_hw_building": 15, + "delta_temp_hw_district": 20, + "cooling_controller_y_max": 1, + "cooling_controller_y_min": 0, + "heating_controller_y_max": 1, + "heating_controller_y_min": 0 + } + }, + { + "geojson_id": "abcdefghijklmnopqrstuvwx", + "load_model": "time_series", + "load_model_parameters": { + "time_series": { + "filepath": "../../data_shared/time_series_large_office_2013_5B.mos", + "delta_temp_air_cooling": 10, + "delta_temp_air_heating": 18, + "has_liquid_heating": true, + "has_electric_heating": false, + "has_liquid_cooling": true, + "has_electric_cooling": false, + "temp_chw_supply": 7, + "temp_chw_return": 12, + "temp_hw_supply": 40, + "temp_hw_return": 35, + "temp_setpoint_cooling": 24, + "temp_setpoint_heating": 20 + } + }, + "ets_model": "Indirect Heating and Cooling", + "ets_indirect_parameters": { + "heat_flow_nominal": 8000, + "heat_exchanger_efficiency": 0.8, + "nominal_mass_flow_district": 0.5, + "nominal_mass_flow_building": 0.5, + "valve_pressure_drop": 6000, + "heat_exchanger_secondary_pressure_drop": 500, + "heat_exchanger_primary_pressure_drop": 500, + "cooling_supply_water_temperature_building": 7, + "heating_supply_water_temperature_building": 50, + "delta_temp_chw_building": 5, + "delta_temp_chw_district": 8, + "delta_temp_hw_building": 15, + "delta_temp_hw_district": 20, + "cooling_controller_y_max": 1, + "cooling_controller_y_min": 0, + "heating_controller_y_max": 1, + "heating_controller_y_min": 0 + } + } + ], + "district_system": { + "first_generation": { + "central_steam_plant_parameters": { + "boiler_efficiency": 0.65 + } + } + } +} diff --git a/tests/model_connectors/test_steam_boiler.py b/tests/model_connectors/test_steam_boiler.py new file mode 100644 index 000000000..d86576009 --- /dev/null +++ b/tests/model_connectors/test_steam_boiler.py @@ -0,0 +1,75 @@ +# :copyright (c) URBANopt, Alliance for Sustainable Energy, LLC, and other contributors. +# See also https://github.com/urbanopt/geojson-modelica-translator/blob/develop/LICENSE.md + +from pathlib import Path + +import pytest + +from geojson_modelica_translator.geojson.urbanopt_geojson import UrbanOptGeoJson +from geojson_modelica_translator.model_connectors.couplings.coupling import Coupling +from geojson_modelica_translator.model_connectors.couplings.graph import CouplingGraph +from geojson_modelica_translator.model_connectors.districts.district import District +from geojson_modelica_translator.model_connectors.energy_transfer_systems.ets_cold_water_stub import EtsColdWaterStub +from geojson_modelica_translator.model_connectors.energy_transfer_systems.heating_indirect import HeatingIndirect +from geojson_modelica_translator.model_connectors.load_connectors.time_series import TimeSeries +from geojson_modelica_translator.model_connectors.networks.network_2_pipe import Network2Pipe +from geojson_modelica_translator.model_connectors.plants.steam_boiler import SteamPlant +from geojson_modelica_translator.system_parameters.system_parameters import SystemParameters +from tests.base_test_case import TestCaseBase + + +class CombinedHeatingPowerTest(TestCaseBase): + def setUp(self): + super().setUp() + + self.project_name = "steam_boiler" + self.data_dir, self.output_dir = self.set_up(Path(__file__).parent, self.project_name) + + # load in the example geojson with a single office building + filename = Path(self.data_dir) / "time_series_ex1.json" + self.gj = self.gj = UrbanOptGeoJson(filename) + + # load system parameter data + filename = Path(self.data_dir) / "time_series_sys_params_steam.json" + self.sys_params = SystemParameters(filename) + + # create network and plant + network = Network2Pipe(self.sys_params) + heating_plant = SteamPlant(self.sys_params) + + # create our our load/ets/stubs + all_couplings = [Coupling(network, heating_plant)] + for geojson_load in self.gj.buildings: + time_series_load = TimeSeries(self.sys_params, geojson_load) + geojson_load_id = geojson_load.feature.properties["id"] + heating_indirect_system = HeatingIndirect(self.sys_params, geojson_load_id) + cold_water_stub = EtsColdWaterStub(self.sys_params) + all_couplings.append(Coupling(time_series_load, heating_indirect_system)) + all_couplings.append(Coupling(time_series_load, cold_water_stub)) + all_couplings.append(Coupling(heating_indirect_system, network)) + + # create the couplings and graph + graph = CouplingGraph(all_couplings) + + self.district = District( + root_dir=self.output_dir, + project_name=self.project_name, + system_parameters=self.sys_params, + coupling_graph=graph, + ) + self.district.to_modelica() + + def test_build_steam_system(self): + root_path = Path(self.district._scaffold.districts_path.files_dir).resolve() + assert (root_path / "DistrictEnergySystem.mo").exists() + + @pytest.mark.simulation() + def test_simulate_steam_system(self): + self.run_and_assert_in_docker( + f"{self.district._scaffold.project_name}.Districts.DistrictEnergySystem", + file_to_load=self.district._scaffold.package_path, + run_path=self.district._scaffold.project_path, + start_time=17280000, # Day 200 (in seconds) (Run in summer to keep chiller happy) + stop_time=17366400, # For 1 day duration (in seconds) + step_size=3600, # At 1 hour step size (in seconds) + ) From 4b4f9d06d0e6d4f51b7bd60511680bb31d6382e8 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Fri, 15 Mar 2024 16:40:56 -0600 Subject: [PATCH 15/35] couplings for steam boiler - this should be reviewed! --- .../Network2Pipe_SteamBoiler/ComponentDefinitions.mopt | 1 + .../Network2Pipe_SteamBoiler/ConnectStatements.mopt | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 geojson_modelica_translator/model_connectors/couplings/templates/Network2Pipe_SteamBoiler/ComponentDefinitions.mopt create mode 100644 geojson_modelica_translator/model_connectors/couplings/templates/Network2Pipe_SteamBoiler/ConnectStatements.mopt diff --git a/geojson_modelica_translator/model_connectors/couplings/templates/Network2Pipe_SteamBoiler/ComponentDefinitions.mopt b/geojson_modelica_translator/model_connectors/couplings/templates/Network2Pipe_SteamBoiler/ComponentDefinitions.mopt new file mode 100644 index 000000000..03fb85e33 --- /dev/null +++ b/geojson_modelica_translator/model_connectors/couplings/templates/Network2Pipe_SteamBoiler/ComponentDefinitions.mopt @@ -0,0 +1 @@ + // No components for pipe and steam plant diff --git a/geojson_modelica_translator/model_connectors/couplings/templates/Network2Pipe_SteamBoiler/ConnectStatements.mopt b/geojson_modelica_translator/model_connectors/couplings/templates/Network2Pipe_SteamBoiler/ConnectStatements.mopt new file mode 100644 index 000000000..d9fc700ce --- /dev/null +++ b/geojson_modelica_translator/model_connectors/couplings/templates/Network2Pipe_SteamBoiler/ConnectStatements.mopt @@ -0,0 +1,10 @@ + connect({{ coupling.plant.id }}.port_a,{{ coupling.network.id }}.port_bDisRet) + annotation ({{ diagram.line.heating_plant.port_a.two_pipe.port_b_dis_ret }}); + connect({{ coupling.network.id }}.dp,{{ coupling.plant.id }}.dpMea) + annotation ({{ diagram.line.two_pipe.dp.heating_plant.dp_mea }}); + connect({{ coupling.plant.id }}.port_b,{{ coupling.network.id }}.port_aDisSup) + annotation ({{ diagram.line.heating_plant.port_b.two_pipe.port_a_dis_sup }}); + connect(mPum_flow_{{ coupling.id }}.y,{{ coupling.plant.id }}.on) + annotation ({{ diagram.line.m_pum_flow.y.heating_plant.on }}); + connect(TDisSetHeaWat_{{ coupling.id }}.y,{{ coupling.plant.id }}.THeaSet) + annotation ({{ diagram.line.t_dis_set_hea_wat.y.heating_plant.t_hea_set }}); From 18ac742069a025db3610d9b5683c0a9a781b2f05 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Tue, 16 Apr 2024 09:32:09 -0600 Subject: [PATCH 16/35] new geojson method to retrieve any feature by id --- .../geojson/urbanopt_geojson.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/geojson_modelica_translator/geojson/urbanopt_geojson.py b/geojson_modelica_translator/geojson/urbanopt_geojson.py index f6ffc83ab..95d2da80a 100644 --- a/geojson_modelica_translator/geojson/urbanopt_geojson.py +++ b/geojson_modelica_translator/geojson/urbanopt_geojson.py @@ -82,3 +82,18 @@ def __init__(self, filename, building_ids=None): if not self.buildings: raise GeoJsonValidationError(f"No valid buildings found in GeoJSON file: {filename}") + + def get_feature_by_id(self, feature_id=None): + """ + return geojson data for a specific feature (building, pipe, wire, junction, district system, etc). + + :param feature_id: string, id of the object to look up in the geojson file + :return: dict, full feature data for the object with the given id + """ + + if feature_id is None: + raise SystemExit("No id submitted. Please retry and include the appropriate id") + + for feature in self.data.features: + if feature["properties"]["id"] == str(feature_id): + return feature From 2b0d68707ea98c137d1e7378a3066ce27c836bd8 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Tue, 30 Apr 2024 15:21:33 -0600 Subject: [PATCH 17/35] skip district steam tests until we build that functionality --- tests/model_connectors/test_steam_boiler.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/model_connectors/test_steam_boiler.py b/tests/model_connectors/test_steam_boiler.py index d86576009..006f98f42 100644 --- a/tests/model_connectors/test_steam_boiler.py +++ b/tests/model_connectors/test_steam_boiler.py @@ -59,17 +59,19 @@ def setUp(self): ) self.district.to_modelica() + @pytest.mark.skip(reason="District steam systems have not been implemented yet.") def test_build_steam_system(self): root_path = Path(self.district._scaffold.districts_path.files_dir).resolve() assert (root_path / "DistrictEnergySystem.mo").exists() @pytest.mark.simulation() + @pytest.mark.skip(reason="District steam systems have not been implemented yet.") def test_simulate_steam_system(self): self.run_and_assert_in_docker( f"{self.district._scaffold.project_name}.Districts.DistrictEnergySystem", file_to_load=self.district._scaffold.package_path, run_path=self.district._scaffold.project_path, - start_time=17280000, # Day 200 (in seconds) (Run in summer to keep chiller happy) - stop_time=17366400, # For 1 day duration (in seconds) + start_time=0, # Day 0 (in seconds) + stop_time=86400, # For 1 day duration (in seconds) step_size=3600, # At 1 hour step size (in seconds) ) From e1be79b161ee93f1699e666bfc3f6e19a85fd089 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Fri, 3 May 2024 15:31:43 -0600 Subject: [PATCH 18/35] test for steam example --- tests/GMT_Lib/test_gmt_lib.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/GMT_Lib/test_gmt_lib.py b/tests/GMT_Lib/test_gmt_lib.py index 7ec79d1f7..4450bd095 100644 --- a/tests/GMT_Lib/test_gmt_lib.py +++ b/tests/GMT_Lib/test_gmt_lib.py @@ -10,6 +10,9 @@ from geojson_modelica_translator.modelica.GMT_Lib.DHC.Components.Plants.Heating.boiler_polynomial import ( PolynomialBoiler, ) +from geojson_modelica_translator.modelica.GMT_Lib.DHC.steam_example import ( + Steam, +) from geojson_modelica_translator.modelica.GMT_Lib.Electrical.AC.ThreePhasesBalanced.Conversion.ACACTransformer import ( ACACTransformer, ) @@ -39,6 +42,7 @@ GMT_LIB_PATH = PARENT_DIR.parent.parent / "geojson_modelica_translator" / "modelica" / "GMT_Lib" COOLING_PLANT_PATH = GMT_LIB_PATH / "DHC" / "Components" / "Plants" / "Cooling" MICROGRID_PARAMS = PARENT_DIR.parent / "data_shared" / "system_params_microgrid_example.json" +STEAM_PARAMS = PARENT_DIR.parent / "data_shared" / "system_params_steam.json" env = Environment( loader=FileSystemLoader(GMT_LIB_PATH), @@ -600,6 +604,22 @@ def test_simulate_transformer(): assert success is True +# @pytest.mark.skip(reason="This functionality is entirely captured by test_simulate_transformer") +def test_build_steam_example(): + # -- Setup + package_output_dir = PARENT_DIR / "output" / "SteamExample" + package_output_dir.mkdir(parents=True, exist_ok=True) + sys_params = SystemParameters(STEAM_PARAMS) + + # -- Act + steam = Steam(sys_params) + steam.build_from_template(package_output_dir) + + # -- Assert + # Did the mofile get created? + assert linecount(package_output_dir / "Steam.mo") > 20 + + # Keeping the code below because it may come back and this was a weird issue. # @pytest.mark.simulation # def test_stub_mbl_v9_with_not_msl_v4(): From da213cea27ea4bdce972cb42974ecdfe853cd217 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Fri, 3 May 2024 15:31:54 -0600 Subject: [PATCH 19/35] steam sys-params --- tests/data_shared/system_params_steam.json | 80 ++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 tests/data_shared/system_params_steam.json diff --git a/tests/data_shared/system_params_steam.json b/tests/data_shared/system_params_steam.json new file mode 100644 index 000000000..644192b2f --- /dev/null +++ b/tests/data_shared/system_params_steam.json @@ -0,0 +1,80 @@ +{ + "weather": "../../data_shared/USA_CA_San.Francisco.Intl.AP.724940_TMY3.mos", + "buildings": [ + { + "geojson_id": "1", + "load_model": "time_series", + "load_model_parameters": { + "time_series": { + "filepath": "./B2.mos", + "delta_temp_air_cooling": 10, + "delta_temp_air_heating": 18, + "temp_setpoint_cooling": 24, + "temp_setpoint_heating": 20, + "temp_hw_supply": 40, + "temp_hw_return": 35, + "has_liquid_heating": true, + "has_electric_heating": false, + "has_liquid_cooling": true, + "has_electric_cooling": false, + "temp_chw_supply": 7, + "temp_chw_return": 12 + } + }, + "ets_model": "None" + }, + { + "geojson_id": "7", + "load_model": "time_series", + "load_model_parameters": { + "time_series": { + "filepath": "./B6.mos", + "delta_temp_air_cooling": 10, + "delta_temp_air_heating": 18, + "temp_setpoint_cooling": 24, + "temp_setpoint_heating": 20, + "temp_hw_supply": 40, + "temp_hw_return": 35, + "has_liquid_heating": true, + "has_electric_heating": false, + "has_liquid_cooling": true, + "has_electric_cooling": false, + "temp_chw_supply": 7, + "temp_chw_return": 12 + } + }, + "ets_model": "None" + }, + { + "geojson_id": "8", + "load_model": "time_series", + "load_model_parameters": { + "time_series": { + "filepath": "./B11.mos", + "delta_temp_air_cooling": 10, + "delta_temp_air_heating": 18, + "temp_setpoint_cooling": 24, + "temp_setpoint_heating": 20, + "temp_hw_supply": 40, + "temp_hw_return": 35, + "has_liquid_heating": true, + "has_electric_heating": false, + "has_liquid_cooling": true, + "has_electric_cooling": false, + "temp_chw_supply": 7, + "temp_chw_return": 12 + } + }, + "ets_model": "None" + } + ], + "district_system": { + "first_generation": { + "central_steam_plant_parameters": { + "steam_pressure_setpoint": 500000, + "reduced_pressure_setpoint": 200000, + "condensate_pressure_drop_nominal": 6000 + } + } + } +} From a642d86220959e0519d520740f88e2b6497da837 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Fri, 3 May 2024 15:32:29 -0600 Subject: [PATCH 20/35] update schema with one more steam object --- geojson_modelica_translator/system_parameters/schema.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/geojson_modelica_translator/system_parameters/schema.json b/geojson_modelica_translator/system_parameters/schema.json index aff193618..be4aace78 100644 --- a/geojson_modelica_translator/system_parameters/schema.json +++ b/geojson_modelica_translator/system_parameters/schema.json @@ -917,7 +917,7 @@ "description": "Central steam heating plant.", "type": "object", "properties": { - "boiler_efficiency_nominal": { + "boiler_efficiency": { "description": "Boiler efficiency (fraction)", "type": "number", "default": 0.7 @@ -927,6 +927,11 @@ "type": "number", "default": 500000 }, + "reduced_pressure_setpoint": { + "description": "The reduced pressure, after the PRV. (Pa)", + "type": "number", + "default": 200000 + }, "condensate_pressure_drop_nominal": { "description": "Nominal pressure drop along the condensate return pipe. (Pa)", "type": "number", From 2038afbbd9b35739faf0113f4fd7491289a75ffa Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Fri, 3 May 2024 15:33:04 -0600 Subject: [PATCH 21/35] WIP: code and template for steam example --- .../modelica/GMT_Lib/DHC/steam_example.mot | 135 ++++++++++++++++++ .../modelica/GMT_Lib/DHC/steam_example.py | 23 +++ 2 files changed, 158 insertions(+) create mode 100644 geojson_modelica_translator/modelica/GMT_Lib/DHC/steam_example.mot create mode 100644 geojson_modelica_translator/modelica/GMT_Lib/DHC/steam_example.py diff --git a/geojson_modelica_translator/modelica/GMT_Lib/DHC/steam_example.mot b/geojson_modelica_translator/modelica/GMT_Lib/DHC/steam_example.mot new file mode 100644 index 000000000..2dfcefba1 --- /dev/null +++ b/geojson_modelica_translator/modelica/GMT_Lib/DHC/steam_example.mot @@ -0,0 +1,135 @@ +within; +model Steam "Example model for a complete steam district heating system with a + central plant that contains a single boiler" + extends Modelica.Icons.Example; + + package MediumSte = Buildings.Media.Steam (p_default=400000, + T_default=273.15+143.61, + h_default=2738100) + "Steam medium"; + package MediumWat = + Buildings.Media.Specialized.Water.TemperatureDependentDensity ( + p_default=101325, + T_default=100+273.15, + h_default=2738100) + "Water medium"; + + parameter Modelica.Units.SI.AbsolutePressure pSat=400000 + "Saturation pressure, high pressure"; + parameter Modelica.Units.SI.AbsolutePressure pLow=200000 + "Reduced pressure, after PRV"; + parameter Modelica.Units.SI.Temperature TSat= + MediumSte.saturationTemperature(pSat) + "Saturation temperature, at high pressure"; + + parameter Integer N = {{ sys_params.num_buildings }} "Number of buildings"; + parameter Modelica.Units.SI.MassFlowRate mDis_flow_nominal=sum(bld.m_flow_nominal)*1.2 + "Nominal mass flow rate of entire district"; + parameter Modelica.Units.SI.HeatFlowRate QDis_flow_nominal=QBui_flow_nominal*N + "Nominal heat flow rate of entire district"; + parameter Modelica.Units.SI.HeatFlowRate QBui_flow_nominal=20000 + "Nominal heat flow rate of each building"; + parameter Modelica.Units.SI.PressureDifference dpPip=6000 + "Pressure drop in the condensate return pipe"; + + parameter Buildings.Fluid.Movers.Data.Generic perPumFW( + pressure(V_flow=(mDis_flow_nominal/1000)*{0,1,2}, + dp=(pSat-101325)*{2,1,0})) + "Performance data for feedwater pump at the plant"; + + parameter Modelica.Units.SI.PressureDifference dp_nominal=6000 + "Pressure drop of distribution at nominal mass flow rate"; + + Buildings.Experimental.DHC.Loads.Steam.BuildingTimeSeriesAtETS bld[N]( + redeclare final package MediumSte = MediumSte, + redeclare final package MediumWat = MediumWat, + each have_prv=true, + each dp_nominal=dpPip/2, + each final pSte_nominal=pSat, + each final Q_flow_nominal=QBui_flow_nominal, + each pLow_nominal=pLow, + each energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial, + each tableOnFile=false, + each QHeaLoa= + [0,0.8; 2,1; 10,1; 12,0.5; 20,0.5; 24,0.8]*[1,0;0,QBui_flow_nominal], + each smoothness=Modelica.Blocks.Types.Smoothness.MonotoneContinuousDerivative1, + each timeScale(displayUnit="s") = 3600, + each show_T=true) + "Buildings" + annotation (Placement(transformation(extent={{60,20},{40,40}}))); + Buildings.Experimental.DHC.Networks.Steam.DistributionCondensatePipe dis( + redeclare final package MediumSup = MediumSte, + redeclare final package MediumRet = MediumWat, + final dp_nominal=dp_nominal, + final nCon=N, + final mDis_flow_nominal=mDis_flow_nominal, + final mCon_flow_nominal=bld.m_flow_nominal) + "Distribution network" + annotation (Placement(transformation(extent={{0,-20},{40,0}}))); + Buildings.Experimental.DHC.Plants.Steam.SingleBoiler pla( + redeclare final package Medium = MediumWat, + redeclare final package MediumHea_b = MediumSte, + final m_flow_nominal=mDis_flow_nominal, + final pSteSet=pSat, + final Q_flow_nominal=QDis_flow_nominal, + final per=perPumFW, + energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial, + kBoi=600, + TiBoi(displayUnit="min") = 120, + kPum=200, + TiPum=1000) + "Plant" + annotation (Placement(transformation(extent={{-50,20},{-30,40}}))); +equation + connect(dis.ports_bCon, bld.port_a) + annotation (Line(points={{8,0},{8,30},{40,30}}, color={0,127,255})); + connect(bld.port_b, dis.ports_aCon) + annotation (Line(points={{40,24},{32,24},{32,0}}, color={0,127,255})); + connect(pla.port_bSerHea, dis.port_aDisSup) + annotation (Line(points={{-30,30},{-20,30},{-20,-10},{0,-10}}, + color={0,127,255})); + connect(dis.port_bDisRet, pla.port_aSerHea) annotation (Line(points={{0,-16},{ + -60,-16},{-60,30},{-50,30}}, color={0,127,255})); + annotation (Icon(coordinateSystem(preserveAspectRatio=false)), Diagram( + coordinateSystem(preserveAspectRatio=false)), + __Dymola_Commands(file= + "modelica://Buildings/Resources/Scripts/Dymola/Experimental/DHC/Examples/Steam/SingleBoiler.mos" + "Simulate and plot"), + experiment( + StopTime=86400, + Tolerance=1e-06), + Documentation(revisions=" +
    +
  • +September 15, 2023, by Kathryn Hinkelman:
    +Added publication references. +
  • +
  • +March 3, 2022 by Kathryn Hinkelman:
    +First implementation. +
  • +
+", info=" +

+This example model demonstrates a complete system simulation for +steam district heating systems. The central plant features a single boiler. +For the distribution network, pressure losses on the condensate return +pipes are included, while the steam pipes are assumed to be lossless. +

+

References

+

+Kathryn Hinkelman, Saranya Anbarasu, Michael Wetter, Antoine Gautier, Wangda Zuo. 2022. +“A Fast and Accurate Modeling Approach for Water and Steam +Thermodynamics with Practical Applications in District Heating System Simulation,” +Energy, 254(A), pp. 124227. +10.1016/j.energy.2022.124227 +

+

+Kathryn Hinkelman, Saranya Anbarasu, Michael Wetter, Antoine Gautier, Baptiste Ravache, Wangda Zuo 2022. +“Towards Open-Source Modelica Models For Steam-Based District Heating Systems.” +Proc. of the 1st International Workshop On Open Source Modelling And Simulation Of +Energy Systems (OSMSES 2022), Aachen, German, April 4-5, 2022. +10.1109/OSMSES54027.2022.9769121 +

+")); +end Steam; diff --git a/geojson_modelica_translator/modelica/GMT_Lib/DHC/steam_example.py b/geojson_modelica_translator/modelica/GMT_Lib/DHC/steam_example.py new file mode 100644 index 000000000..727d31176 --- /dev/null +++ b/geojson_modelica_translator/modelica/GMT_Lib/DHC/steam_example.py @@ -0,0 +1,23 @@ +import logging +from pathlib import Path + +from geojson_modelica_translator.modelica.simple_gmt_base import SimpleGMTBase + +logger = logging.getLogger(__name__) + + +class Steam(SimpleGMTBase): + def __init__(self, system_parameters): + self.system_parameters = system_parameters + self.template_dir = Path(__file__).parent + super().__init__(self.system_parameters, self.template_dir) + + def build_from_template(self, output_dir: Path): + steam_params = self.system_parameters.get_param("$.asdf") + # render template to final modelica file + self.to_modelica( + output_dir=output_dir, + model_name="Steam", + param_data=steam_params, + ) + # If the sys-param file is missing an entry, it will show up as a jinja2.exceptions.UndefinedError From 9afe09c68aec2c5659954785fc0b3a78100a65b0 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Fri, 3 May 2024 15:36:05 -0600 Subject: [PATCH 22/35] rename steam template --- .../modelica/GMT_Lib/DHC/{steam_example.mot => Steam.mot} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename geojson_modelica_translator/modelica/GMT_Lib/DHC/{steam_example.mot => Steam.mot} (100%) diff --git a/geojson_modelica_translator/modelica/GMT_Lib/DHC/steam_example.mot b/geojson_modelica_translator/modelica/GMT_Lib/DHC/Steam.mot similarity index 100% rename from geojson_modelica_translator/modelica/GMT_Lib/DHC/steam_example.mot rename to geojson_modelica_translator/modelica/GMT_Lib/DHC/Steam.mot From df4fe0b91717326d25f9a038df9a086c97558233 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Fri, 3 May 2024 16:03:46 -0600 Subject: [PATCH 23/35] simulation test for steam example --- tests/GMT_Lib/test_gmt_lib.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/tests/GMT_Lib/test_gmt_lib.py b/tests/GMT_Lib/test_gmt_lib.py index 4450bd095..ecf633244 100644 --- a/tests/GMT_Lib/test_gmt_lib.py +++ b/tests/GMT_Lib/test_gmt_lib.py @@ -604,7 +604,7 @@ def test_simulate_transformer(): assert success is True -# @pytest.mark.skip(reason="This functionality is entirely captured by test_simulate_transformer") +# @pytest.mark.skip(reason="This functionality is entirely captured by asdf") def test_build_steam_example(): # -- Setup package_output_dir = PARENT_DIR / "output" / "SteamExample" @@ -620,6 +620,32 @@ def test_build_steam_example(): assert linecount(package_output_dir / "Steam.mo") > 20 +@pytest.mark.simulation() +def test_simulate_steam_example(): + # -- Setup + package_output_dir = PARENT_DIR / "output" / "SteamExample" + package_output_dir.mkdir(parents=True, exist_ok=True) + sys_params = SystemParameters(STEAM_PARAMS) + + # -- Act + steam = Steam(sys_params) + steam.build_from_template(package_output_dir) + + runner = ModelicaRunner() + success, _ = runner.run_in_docker( + "compile_and_run", + "Steam", + file_to_load=package_output_dir / "Steam.mo", + run_path=package_output_dir, + ) + + # -- Assert + # Did the mofile get created? + assert linecount(package_output_dir / "Steam.mo") > 20 + # Did the simulation run? + assert success is True + + # Keeping the code below because it may come back and this was a weird issue. # @pytest.mark.simulation # def test_stub_mbl_v9_with_not_msl_v4(): From fb39fa9286638f4eb2a3956f45e0e4e2f8095134 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Fri, 3 May 2024 16:04:25 -0600 Subject: [PATCH 24/35] templatized steam level 1 example model --- .../modelica/GMT_Lib/DHC/Steam.mot | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/geojson_modelica_translator/modelica/GMT_Lib/DHC/Steam.mot b/geojson_modelica_translator/modelica/GMT_Lib/DHC/Steam.mot index 2dfcefba1..5c5e4a46a 100644 --- a/geojson_modelica_translator/modelica/GMT_Lib/DHC/Steam.mot +++ b/geojson_modelica_translator/modelica/GMT_Lib/DHC/Steam.mot @@ -14,22 +14,22 @@ model Steam "Example model for a complete steam district heating system with a h_default=2738100) "Water medium"; - parameter Modelica.Units.SI.AbsolutePressure pSat=400000 + parameter Modelica.Units.SI.AbsolutePressure pSat={{ data["steam_pressure_setpoint"] }} "Saturation pressure, high pressure"; - parameter Modelica.Units.SI.AbsolutePressure pLow=200000 + parameter Modelica.Units.SI.AbsolutePressure pLow={{ data["reduced_pressure_setpoint"] }} "Reduced pressure, after PRV"; parameter Modelica.Units.SI.Temperature TSat= MediumSte.saturationTemperature(pSat) "Saturation temperature, at high pressure"; - parameter Integer N = {{ sys_params.num_buildings }} "Number of buildings"; + parameter Integer N = 3 "Number of buildings"; parameter Modelica.Units.SI.MassFlowRate mDis_flow_nominal=sum(bld.m_flow_nominal)*1.2 "Nominal mass flow rate of entire district"; parameter Modelica.Units.SI.HeatFlowRate QDis_flow_nominal=QBui_flow_nominal*N "Nominal heat flow rate of entire district"; parameter Modelica.Units.SI.HeatFlowRate QBui_flow_nominal=20000 "Nominal heat flow rate of each building"; - parameter Modelica.Units.SI.PressureDifference dpPip=6000 + parameter Modelica.Units.SI.PressureDifference dpPip={{ data["condensate_pressure_drop_nominal"] }} "Pressure drop in the condensate return pipe"; parameter Buildings.Fluid.Movers.Data.Generic perPumFW( @@ -56,7 +56,7 @@ model Steam "Example model for a complete steam district heating system with a each timeScale(displayUnit="s") = 3600, each show_T=true) "Buildings" - annotation (Placement(transformation(extent={{60,20},{40,40}}))); + {% raw %}annotation (Placement(transformation(extent={{60,20},{40,40}}))); Buildings.Experimental.DHC.Networks.Steam.DistributionCondensatePipe dis( redeclare final package MediumSup = MediumSte, redeclare final package MediumRet = MediumWat, @@ -132,4 +132,4 @@ Energy Systems (OSMSES 2022), Aachen, German, April 4-5, 2022. 10.1109/OSMSES54027.2022.9769121

")); -end Steam; +end Steam;{% endraw %} From 453371e59b630eade1fe73d88df0eeaff08d1ff3 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Fri, 3 May 2024 16:05:07 -0600 Subject: [PATCH 25/35] point to correct part of sys-params for steam-example --- .../modelica/GMT_Lib/DHC/steam_example.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/geojson_modelica_translator/modelica/GMT_Lib/DHC/steam_example.py b/geojson_modelica_translator/modelica/GMT_Lib/DHC/steam_example.py index 727d31176..122a819d8 100644 --- a/geojson_modelica_translator/modelica/GMT_Lib/DHC/steam_example.py +++ b/geojson_modelica_translator/modelica/GMT_Lib/DHC/steam_example.py @@ -13,7 +13,9 @@ def __init__(self, system_parameters): super().__init__(self.system_parameters, self.template_dir) def build_from_template(self, output_dir: Path): - steam_params = self.system_parameters.get_param("$.asdf") + steam_params = self.system_parameters.get_param( + "$.district_system.first_generation.central_steam_plant_parameters" + ) # render template to final modelica file self.to_modelica( output_dir=output_dir, From 475cae340d100b3dfcf27d523ac221f49a608d86 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Mon, 6 May 2024 09:37:23 -0600 Subject: [PATCH 26/35] skip separately building a steam model during testing --- tests/GMT_Lib/test_gmt_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/GMT_Lib/test_gmt_lib.py b/tests/GMT_Lib/test_gmt_lib.py index ecf633244..68ab57614 100644 --- a/tests/GMT_Lib/test_gmt_lib.py +++ b/tests/GMT_Lib/test_gmt_lib.py @@ -604,7 +604,7 @@ def test_simulate_transformer(): assert success is True -# @pytest.mark.skip(reason="This functionality is entirely captured by asdf") +@pytest.mark.skip(reason="This functionality is entirely captured by test_simulate_steam_example") def test_build_steam_example(): # -- Setup package_output_dir = PARENT_DIR / "output" / "SteamExample" From da28709223f64525b5a51e61cd4a42f2467433a1 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Thu, 13 Feb 2025 09:04:42 -0700 Subject: [PATCH 27/35] unskip simple steam build test --- tests/GMT_Lib/test_gmt_lib.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/GMT_Lib/test_gmt_lib.py b/tests/GMT_Lib/test_gmt_lib.py index a0e23f826..f3d4c82a0 100644 --- a/tests/GMT_Lib/test_gmt_lib.py +++ b/tests/GMT_Lib/test_gmt_lib.py @@ -596,7 +596,6 @@ def test_simulate_transformer(): assert success is True -@pytest.mark.skip(reason="This functionality is entirely captured by test_simulate_steam_example") def test_build_steam_example(): # -- Setup package_output_dir = PARENT_DIR / "output" / "SteamExample" From 9807705d9742fc4f581664964e00d69e99b7cca9 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Thu, 13 Feb 2025 10:31:35 -0700 Subject: [PATCH 28/35] add steam ets to schema --- .../system_parameter_properties.json | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/geojson_modelica_translator/system_parameters/system_parameter_properties.json b/geojson_modelica_translator/system_parameters/system_parameter_properties.json index a3906df56..f9b589dc9 100644 --- a/geojson_modelica_translator/system_parameters/system_parameter_properties.json +++ b/geojson_modelica_translator/system_parameters/system_parameter_properties.json @@ -266,9 +266,10 @@ ] }, "ets_model": { - "description": "Energy transfer station model. One side is connected to the district water loops and the other side is connected to the building water loops.", + "description": "Energy transfer station model. One side is connected to the district loop and the other side is connected to the building loops.", "type": "string", "enum": [ + "Steam", "Indirect Heating and Cooling", "Fifth Gen Heat Pump" ] @@ -282,6 +283,9 @@ }, { "$ref": "#/definitions/fifth_gen_ets_parameters" + }, + { + "$ref": "#/definitions/steam_ets_parameters" } ] }, @@ -334,6 +338,11 @@ "required": [ "fifth_gen_ets_parameters" ] + }, + { + "required": [ + "steam_ets_parameters" + ] } ], "required": [ @@ -406,6 +415,17 @@ }, "additionalProperties": false }, + "steam_ets_parameters": { + "description": "The parameters associated with the steam energy transfer station.", + "type": "object", + "properties": { + "heat_flow_nominal": { + "description": "Nominal heat flow rate. (W)", + "type": "number", + "default": 10000 + } + } + }, "ets_indirect_parameters": { "description": "The parameters associated with the indirect energy transfer station.", "type": "object", From bb572ce415cb3c5a6488060b341f12bdf38d7dda Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Thu, 13 Feb 2025 10:32:05 -0700 Subject: [PATCH 29/35] add steam ets to test sys-param file --- tests/data_shared/system_params_steam.json | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/data_shared/system_params_steam.json b/tests/data_shared/system_params_steam.json index 644192b2f..91e6cdac9 100644 --- a/tests/data_shared/system_params_steam.json +++ b/tests/data_shared/system_params_steam.json @@ -21,7 +21,10 @@ "temp_chw_return": 12 } }, - "ets_model": "None" + "ets_model": "Steam", + "steam_ets_parameters": { + "heat_flow_nominal": 12000 + } }, { "geojson_id": "7", @@ -43,7 +46,10 @@ "temp_chw_return": 12 } }, - "ets_model": "None" + "ets_model": "Steam", + "steam_ets_parameters": { + "heat_flow_nominal": 12000 + } }, { "geojson_id": "8", @@ -65,7 +71,10 @@ "temp_chw_return": 12 } }, - "ets_model": "None" + "ets_model": "Steam", + "steam_ets_parameters": { + "heat_flow_nominal": 12000 + } } ], "district_system": { From 8cb54dc3ce45f2a8ee3e58fab3c6ea76dc657826 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Thu, 13 Feb 2025 10:33:05 -0700 Subject: [PATCH 30/35] add steam building, ets, & plant to template sys-param file --- .../time_series_template.json | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/geojson_modelica_translator/system_parameters/time_series_template.json b/geojson_modelica_translator/system_parameters/time_series_template.json index a7e3f45b3..881c45446 100644 --- a/geojson_modelica_translator/system_parameters/time_series_template.json +++ b/geojson_modelica_translator/system_parameters/time_series_template.json @@ -1,5 +1,34 @@ { "buildings": [ + { + "geojson_id": "1G", + "load_model": "time_series", + "load_model_parameters": { + "time_series": { + "filepath": "To be populated", + "delta_temp_air_cooling": 10, + "delta_temp_air_heating": 18, + "has_liquid_cooling": true, + "has_liquid_heating": true, + "has_electric_cooling": false, + "has_electric_heating": false, + "max_electrical_load": 0, + "temp_chw_return": 12, + "temp_chw_supply": 7, + "temp_hw_return": 35, + "temp_hw_supply": 40, + "temp_setpoint_cooling": 24, + "temp_setpoint_heating": 20 + } + }, + "ets_model": "Steam", + "steam_ets_parameters": { + "heat_flow_nominal": 8000 + }, + "photovoltaic_panels": [], + "diesel_generators": [], + "battery_banks": [] + }, { "geojson_id": "4G", "load_model": "time_series", @@ -84,6 +113,14 @@ } ], "district_system": { + "first_generation": { + "central_steam_plant_parameters": { + "boiler_efficiency": 0.7, + "steam_pressure_setpoint": 500000, + "reduced_pressure_setpoint": 200000, + "condensate_pressure_drop_nominal": 6000 + } + }, "fourth_generation": { "central_cooling_plant_parameters": { "heat_flow_nominal": 7999, From 723c2864491bef308e2804f94fa467458e3e60e0 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Thu, 13 Feb 2025 10:36:21 -0700 Subject: [PATCH 31/35] allow more statements with ruff --- ruff.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruff.toml b/ruff.toml index f956a00c1..5cd32ef60 100644 --- a/ruff.toml +++ b/ruff.toml @@ -11,7 +11,7 @@ ignore = ["PLR0913", "PLR2004", "PLR0402", "COM812", "COM819", "SIM108", "ARG002 [lint.pylint] # system_parameters.py has many file lookups that necessitate nested statements & branches # Raise the allowed limits the least possible amount https://docs.astral.sh/ruff/settings/#pylint-max-branches -max-statements = 60 +max-statements = 65 max-branches = 24 [lint.per-file-ignores] From ba82bdaca9edea7d97aca61d43bfcc3ff38c7d9e Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Thu, 13 Feb 2025 10:36:34 -0700 Subject: [PATCH 32/35] handle steam components in sys-param code --- .../system_parameters/system_parameters.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/geojson_modelica_translator/system_parameters/system_parameters.py b/geojson_modelica_translator/system_parameters/system_parameters.py index 9686771b2..98456c678 100644 --- a/geojson_modelica_translator/system_parameters/system_parameters.py +++ b/geojson_modelica_translator/system_parameters/system_parameters.py @@ -799,10 +799,12 @@ def retrieve_building_data_from_sdk( # Make sys_param template entries for each feature_id building_list = [] for building in building_ids: - if "4G" in district_type: + if "steam" in district_type: building_params = deepcopy(self.param_template["buildings"][0]) - elif "5G" in district_type: + if "4G" in district_type: building_params = deepcopy(self.param_template["buildings"][1]) + elif "5G" in district_type: + building_params = deepcopy(self.param_template["buildings"][2]) building_params["geojson_id"] = str(building) building_list.append(building_params) @@ -995,12 +997,19 @@ def csv_to_sys_param( elif district_type == "5G": # Process waste-heat inputs del self.param_template["district_system"]["fifth_generation"]["ghe_parameters"] - # remove fourth generation district system type + # remove other district system types + del self.param_template["district_system"]["first_generation"] del self.param_template["district_system"]["fourth_generation"] - elif district_type in ["4G", "steam"]: + elif district_type in ["4G"]: # remove fifth generation district system type if it exists in template and ghe is not true with suppress(KeyError): del self.param_template["district_system"]["fifth_generation"] + del self.param_template["district_system"]["first_generation"] + elif district_type in ["steam"]: + # TODO: process steam inputs + # remove other district system types + del self.param_template["district_system"]["fourth_generation"] + del self.param_template["district_system"]["fifth_generation"] # save the file to disk self.save() From 9331826675231467a48ef4ad1c4a174b8e816584 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Thu, 13 Feb 2025 10:37:09 -0700 Subject: [PATCH 33/35] update sys-param test to include steam in error-catching --- tests/system_parameters/test_system_parameters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/system_parameters/test_system_parameters.py b/tests/system_parameters/test_system_parameters.py index cc96345e3..8f3384d7a 100644 --- a/tests/system_parameters/test_system_parameters.py +++ b/tests/system_parameters/test_system_parameters.py @@ -95,7 +95,7 @@ def test_errors(self): SystemParameters.loadd(incomplete_teaser_params) sp = SystemParameters.loadd(incomplete_teaser_params, validate_on_load=False) - assert "'None' is not one of ['Indirect Heating and Cooling', 'Fifth Gen Heat Pump']" in sp.validate() + assert "'None' is not one of ['Steam', 'Indirect Heating and Cooling', 'Fifth Gen Heat Pump']" in sp.validate() assert "'fraction_latent_person' is a required property" in sp.validate() assert "'temp_hw_supply' is a required property" in sp.validate() assert "'temp_setpoint_cooling' is a required property" in sp.validate() From 0ecac5505d08117cfbce37755d9e516a94877e5d Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Thu, 13 Feb 2025 10:38:06 -0700 Subject: [PATCH 34/35] ruff cleaning up a pytest mark --- tests/model_connectors/test_steam_boiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/model_connectors/test_steam_boiler.py b/tests/model_connectors/test_steam_boiler.py index 006f98f42..508716b5f 100644 --- a/tests/model_connectors/test_steam_boiler.py +++ b/tests/model_connectors/test_steam_boiler.py @@ -64,7 +64,7 @@ def test_build_steam_system(self): root_path = Path(self.district._scaffold.districts_path.files_dir).resolve() assert (root_path / "DistrictEnergySystem.mo").exists() - @pytest.mark.simulation() + @pytest.mark.simulation @pytest.mark.skip(reason="District steam systems have not been implemented yet.") def test_simulate_steam_system(self): self.run_and_assert_in_docker( From 8c938dcdb7b9563860744f87de9399e76f6780e2 Mon Sep 17 00:00:00 2001 From: Nathan Moore Date: Thu, 13 Feb 2025 11:20:29 -0700 Subject: [PATCH 35/35] use all params in steam template, and jinja length --- .../modelica/GMT_Lib/DHC/Steam.mot | 14 +++++++------- .../modelica/GMT_Lib/DHC/steam_example.py | 6 ++---- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/geojson_modelica_translator/modelica/GMT_Lib/DHC/Steam.mot b/geojson_modelica_translator/modelica/GMT_Lib/DHC/Steam.mot index 5c5e4a46a..d1f59e6f0 100644 --- a/geojson_modelica_translator/modelica/GMT_Lib/DHC/Steam.mot +++ b/geojson_modelica_translator/modelica/GMT_Lib/DHC/Steam.mot @@ -14,22 +14,22 @@ model Steam "Example model for a complete steam district heating system with a h_default=2738100) "Water medium"; - parameter Modelica.Units.SI.AbsolutePressure pSat={{ data["steam_pressure_setpoint"] }} + parameter Modelica.Units.SI.AbsolutePressure pSat={{ data["district_system"]["first_generation"]["central_steam_plant_parameters"]["steam_pressure_setpoint"] }} "Saturation pressure, high pressure"; - parameter Modelica.Units.SI.AbsolutePressure pLow={{ data["reduced_pressure_setpoint"] }} + parameter Modelica.Units.SI.AbsolutePressure pLow={{ data["district_system"]["first_generation"]["central_steam_plant_parameters"]["reduced_pressure_setpoint"] }} "Reduced pressure, after PRV"; parameter Modelica.Units.SI.Temperature TSat= MediumSte.saturationTemperature(pSat) "Saturation temperature, at high pressure"; - parameter Integer N = 3 "Number of buildings"; + parameter Integer N = {{ data["buildings"] | length }} "Number of buildings"; parameter Modelica.Units.SI.MassFlowRate mDis_flow_nominal=sum(bld.m_flow_nominal)*1.2 "Nominal mass flow rate of entire district"; parameter Modelica.Units.SI.HeatFlowRate QDis_flow_nominal=QBui_flow_nominal*N "Nominal heat flow rate of entire district"; parameter Modelica.Units.SI.HeatFlowRate QBui_flow_nominal=20000 "Nominal heat flow rate of each building"; - parameter Modelica.Units.SI.PressureDifference dpPip={{ data["condensate_pressure_drop_nominal"] }} + parameter Modelica.Units.SI.PressureDifference dpPip={{ data["district_system"]["first_generation"]["central_steam_plant_parameters"]["condensate_pressure_drop_nominal"] }} "Pressure drop in the condensate return pipe"; parameter Buildings.Fluid.Movers.Data.Generic perPumFW( @@ -40,7 +40,7 @@ model Steam "Example model for a complete steam district heating system with a parameter Modelica.Units.SI.PressureDifference dp_nominal=6000 "Pressure drop of distribution at nominal mass flow rate"; - Buildings.Experimental.DHC.Loads.Steam.BuildingTimeSeriesAtETS bld[N]( + Buildings.DHC.Loads.Steam.BuildingTimeSeriesAtETS bld[N]( redeclare final package MediumSte = MediumSte, redeclare final package MediumWat = MediumWat, each have_prv=true, @@ -57,7 +57,7 @@ model Steam "Example model for a complete steam district heating system with a each show_T=true) "Buildings" {% raw %}annotation (Placement(transformation(extent={{60,20},{40,40}}))); - Buildings.Experimental.DHC.Networks.Steam.DistributionCondensatePipe dis( + Buildings.DHC.Networks.Steam.DistributionCondensatePipe dis( redeclare final package MediumSup = MediumSte, redeclare final package MediumRet = MediumWat, final dp_nominal=dp_nominal, @@ -66,7 +66,7 @@ model Steam "Example model for a complete steam district heating system with a final mCon_flow_nominal=bld.m_flow_nominal) "Distribution network" annotation (Placement(transformation(extent={{0,-20},{40,0}}))); - Buildings.Experimental.DHC.Plants.Steam.SingleBoiler pla( + Buildings.DHC.Plants.Steam.SingleBoiler pla( redeclare final package Medium = MediumWat, redeclare final package MediumHea_b = MediumSte, final m_flow_nominal=mDis_flow_nominal, diff --git a/geojson_modelica_translator/modelica/GMT_Lib/DHC/steam_example.py b/geojson_modelica_translator/modelica/GMT_Lib/DHC/steam_example.py index 122a819d8..67de8471b 100644 --- a/geojson_modelica_translator/modelica/GMT_Lib/DHC/steam_example.py +++ b/geojson_modelica_translator/modelica/GMT_Lib/DHC/steam_example.py @@ -13,13 +13,11 @@ def __init__(self, system_parameters): super().__init__(self.system_parameters, self.template_dir) def build_from_template(self, output_dir: Path): - steam_params = self.system_parameters.get_param( - "$.district_system.first_generation.central_steam_plant_parameters" - ) + all_params = self.system_parameters.get_param("$") # render template to final modelica file self.to_modelica( output_dir=output_dir, model_name="Steam", - param_data=steam_params, + param_data=all_params, ) # If the sys-param file is missing an entry, it will show up as a jinja2.exceptions.UndefinedError