class GeoJsonModelicaTranslator(object): """ Main class for using the GeoJSON to Modelica Translator. """ def __init__( self, geojson_filepath, sys_params_filepath, root_dir, project_name, ): """Create an instance of this class :param geojson_filepath: str, path to GeoJSON file :param sys_params_filepath: str, path to system parameters file :param root_dir: str, where to create the package :project_name: str, name of the package """ if not Path(geojson_filepath).exists(): raise FileNotFoundError( f'GeoJSON file path does not exist: {geojson_filepath}') if not Path(sys_params_filepath).exists(): raise FileNotFoundError( f'System parameters file path does not exist: {sys_params_filepath}' ) self._system_parameters = SystemParameters(sys_params_filepath) geojson_ids = self._system_parameters.get_default( '$.buildings.custom[*].geojson_id', []) self._geojson = UrbanOptGeoJson(geojson_filepath, geojson_ids) self._root_dir = root_dir self._project_name = project_name self._couplings = _parse_couplings(self._geojson, self._system_parameters) self._coupling_graph = CouplingGraph(self._couplings) self._district = District(self._root_dir, self._project_name, self._system_parameters, self._coupling_graph) self._package_created = False def to_modelica(self): """Generate the modelica package. Call `simulate` method on the result to run the package :return: ModelicaPackage """ self._district.to_modelica() return ModelicaPackage(self._district.district_model_filepath, self._root_dir, self._project_name)
def test_to_modelica_defaults(self): feature_json_file = self.data_dir / f"{self.project_name}.json" gj = GeoJsonModelicaTranslator.from_geojson(feature_json_file) sys_params_json_file = self.data_dir / 'geojson_8_system_params.json' sys_params = SystemParameters(sys_params_json_file) gj.set_system_parameters(sys_params) gj.process_loads() self.assertEqual(len(gj.loads), 8) all_couplings = [] for geojson_load in gj.json_loads: teaser_load = Teaser(sys_params, geojson_load) hot_stub = EtsHotWaterStub(sys_params) cold_stub = EtsColdWaterStub(sys_params) all_couplings.append(Coupling(teaser_load, hot_stub)) all_couplings.append(Coupling(teaser_load, cold_stub)) graph = CouplingGraph(all_couplings) district = District( root_dir=self.output_dir, project_name=self.project_name, system_parameters=sys_params, coupling_graph=graph ) district.to_modelica() # run a single file to make sure it simulates mr = ModelicaRunner() root_path = os.path.abspath(os.path.join(district._scaffold.districts_path.files_dir)) exitcode = mr.run_in_docker(os.path.join(root_path, 'DistrictEnergySystem.mo'), run_path=Path(district._scaffold.project_path).resolve().parent, project_name=district._scaffold.project_name) self.assertEqual(0, exitcode)
def __init__( self, geojson_filepath, sys_params_filepath, root_dir, project_name, ): """Create an instance of this class :param geojson_filepath: str, path to GeoJSON file :param sys_params_filepath: str, path to system parameters file :param root_dir: str, where to create the package :project_name: str, name of the package """ if not Path(geojson_filepath).exists(): raise FileNotFoundError( f'GeoJSON file path does not exist: {geojson_filepath}') if not Path(sys_params_filepath).exists(): raise FileNotFoundError( f'System parameters file path does not exist: {sys_params_filepath}' ) self._system_parameters = SystemParameters(sys_params_filepath) geojson_ids = self._system_parameters.get_default( '$.buildings.custom[*].geojson_id', []) self._geojson = UrbanOptGeoJson(geojson_filepath, geojson_ids) self._root_dir = root_dir self._project_name = project_name self._couplings = _parse_couplings(self._geojson, self._system_parameters) self._coupling_graph = CouplingGraph(self._couplings) self._district = District(self._root_dir, self._project_name, self._system_parameters, self._coupling_graph) self._package_created = False
def test_mixed_loads_district_energy_system(self): # create cooling network and plant cooling_network = Network2Pipe(self.sys_params) cooling_plant = CoolingPlant(self.sys_params) # create heating network and plant heating_network = Network2Pipe(self.sys_params) heating_plant = HeatingPlantWithOptionalCHP(self.sys_params) # store all couplings to construct the District system all_couplings = [ Coupling(cooling_network, cooling_plant), Coupling(heating_network, heating_plant), ] # keep track of separate loads and etses for testing purposes loads = [] heat_etses = [] cool_etses = [] load_model_map = { "spawn": Spawn, "rc": Teaser, "time_series": TimeSeries } for geojson_load in self.gj.buildings: load_model_name = self.sys_params.get_param_by_building_id( geojson_load.id, "load_model") load_model = load_model_map[load_model_name] load = load_model(self.sys_params, geojson_load) loads.append(load) geojson_load_id = geojson_load.feature.properties["id"] cooling_indirect = CoolingIndirect(self.sys_params, geojson_load_id) cool_etses.append(cooling_indirect) all_couplings.append(Coupling(load, cooling_indirect)) all_couplings.append(Coupling(cooling_indirect, cooling_network)) heating_indirect = HeatingIndirect(self.sys_params, geojson_load_id) heat_etses.append(heating_indirect) all_couplings.append(Coupling(load, heating_indirect)) all_couplings.append(Coupling(heating_indirect, heating_network)) # verify we used all load types in the model used_classes = [type(x) for x in loads] for expected_load in load_model_map.values(): assert expected_load in used_classes # create the couplings and graph graph = CouplingGraph(all_couplings) district = District(root_dir=self.output_dir, project_name=self.project_name, system_parameters=self.sys_params, coupling_graph=graph) district.to_modelica() root_path = Path(district._scaffold.districts_path.files_dir).resolve() self.run_and_assert_in_docker( Path(root_path) / 'DistrictEnergySystem.mo', project_path=district._scaffold.project_path, project_name=district._scaffold.project_name)
def test_teaser_district_heating_and_cooling_systems(self): # create cooling network and plant cooling_network = Network2Pipe(self.sys_params) cooling_plant = CoolingPlant(self.sys_params) # create heating network and plant heating_network = Network2Pipe(self.sys_params) heating_plant = HeatingPlantWithOptionalCHP(self.sys_params) # create our load/ets/stubs # store all couplings to construct the District system all_couplings = [ Coupling(cooling_network, cooling_plant), Coupling(heating_network, heating_plant), ] # keep track of separate loads and etses for testing purposes loads = [] heat_etses = [] cool_etses = [] for geojson_load in self.gj.buildings: teaser_load = Teaser(self.sys_params, geojson_load) loads.append(teaser_load) geojson_load_id = geojson_load.feature.properties["id"] cooling_indirect = CoolingIndirect(self.sys_params, geojson_load_id) cool_etses.append(cooling_indirect) all_couplings.append(Coupling(teaser_load, cooling_indirect)) all_couplings.append(Coupling(cooling_indirect, cooling_network)) heating_indirect = HeatingIndirect(self.sys_params, geojson_load_id) heat_etses.append(heating_indirect) all_couplings.append(Coupling(teaser_load, heating_indirect)) all_couplings.append(Coupling(heating_indirect, heating_network)) # create the couplings and graph graph = CouplingGraph(all_couplings) district = District(root_dir=self.output_dir, project_name=self.project_name, system_parameters=self.sys_params, coupling_graph=graph) district.to_modelica() root_path = Path(district._scaffold.districts_path.files_dir).resolve() self.run_and_assert_in_docker( Path(root_path) / 'DistrictEnergySystem.mo', project_path=district._scaffold.project_path, project_name=district._scaffold.project_name) # # Validate model outputs # results_dir = f'{district._scaffold.project_path}_results' mat_file = f'{results_dir}/{self.project_name}_Districts_DistrictEnergySystem_result.mat' mat_results = Reader(mat_file, 'dymola') # check the mass flow rates of the first load are in the expected range load = loads[0] (_, heat_m_flow ) = mat_results.values(f'{load.id}.ports_aHeaWat[1].m_flow') (_, cool_m_flow ) = mat_results.values(f'{load.id}.ports_aChiWat[1].m_flow') self.assertTrue( (heat_m_flow >= 0).all(), 'Heating mass flow rate must be greater than or equal to zero') self.assertTrue( (cool_m_flow >= 0).all(), 'Cooling mass flow rate must be greater than or equal to zero') # this tolerance determines how much we allow the actual mass flow rate to exceed the nominal value M_FLOW_NOMINAL_TOLERANCE = 0.01 (_, heat_m_flow_nominal ) = mat_results.values(f'{load.id}.terUni[1].mLoaHea_flow_nominal') heat_m_flow_nominal = heat_m_flow_nominal[0] (_, cool_m_flow_nominal ) = mat_results.values(f'{load.id}.terUni[1].mLoaCoo_flow_nominal') cool_m_flow_nominal = cool_m_flow_nominal[0] # TODO: remove try/except this is fixed try: self.assertTrue( (heat_m_flow <= heat_m_flow_nominal + (heat_m_flow_nominal * M_FLOW_NOMINAL_TOLERANCE)).all(), f'Heating mass flow rate must be less than nominal mass flow rate ({heat_m_flow_nominal}) ' f'plus a tolerance ({M_FLOW_NOMINAL_TOLERANCE * 100}%)') except Exception as e: print(f'WARNING: assertion failed: {e}') try: self.assertTrue( (cool_m_flow <= cool_m_flow_nominal + (cool_m_flow_nominal * M_FLOW_NOMINAL_TOLERANCE)).all(), f'Cooling mass flow rate must be less than nominal mass flow rate ({cool_m_flow_nominal}) ' f'plus a tolerance ({M_FLOW_NOMINAL_TOLERANCE * 100}%)') except Exception as e: print(f'WARNING: assertion failed: {e}')
def test_district_heating_and_cooling_systems(self): # create cooling network and plant cooling_network = Network2Pipe(self.sys_params) cooling_plant = CoolingPlant(self.sys_params) # create heating network and plant heating_network = Network2Pipe(self.sys_params) heating_plant = HeatingPlantWithOptionalCHP(self.sys_params) # create our load/ets/stubs # store all couplings to construct the District system all_couplings = [ Coupling(cooling_network, cooling_plant), Coupling(heating_network, heating_plant), ] # keep track of separate loads and etses for testing purposes loads = [] heat_etses = [] cool_etses = [] for geojson_load in self.gj.buildings: time_series_load = TimeSeries(self.sys_params, geojson_load) loads.append(time_series_load) geojson_load_id = geojson_load.feature.properties["id"] cooling_indirect = CoolingIndirect(self.sys_params, geojson_load_id) cool_etses.append(cooling_indirect) all_couplings.append(Coupling(time_series_load, cooling_indirect)) all_couplings.append(Coupling(cooling_indirect, cooling_network)) heating_indirect = HeatingIndirect(self.sys_params, geojson_load_id) heat_etses.append(heating_indirect) all_couplings.append(Coupling(time_series_load, heating_indirect)) all_couplings.append(Coupling(heating_indirect, heating_network)) # create the couplings and graph graph = CouplingGraph(all_couplings) district = District( root_dir=self.output_dir, project_name=self.project_name, system_parameters=self.sys_params, coupling_graph=graph ) district.to_modelica() root_path = Path(district._scaffold.districts_path.files_dir).resolve() self.run_and_assert_in_docker(Path(root_path) / 'DistrictEnergySystem.mo', project_path=district._scaffold.project_path, project_name=district._scaffold.project_name) # # Validate model outputs # results_dir = f'{district._scaffold.project_path}_results' mat_file = f'{results_dir}/{self.project_name}_Districts_DistrictEnergySystem_result.mat' mat_results = Reader(mat_file, 'dymola') # check the mass flow rates of the first load are in the expected range load = loads[0] (_, heat_m_flow) = mat_results.values(f'{load.id}.ports_aHeaWat[1].m_flow') # NL: changed the line below to be aChiWat (not repeating aHeaWat). (_, cool_m_flow) = mat_results.values(f'{load.id}.ports_aChiWat[1].m_flow') self.assertTrue((heat_m_flow >= 0).all(), 'Heating mass flow rate must be greater than or equal to zero') self.assertTrue((cool_m_flow >= 0).all(), 'Cooling mass flow rate must be greater than or equal to zero') # this tolerance determines how much we allow the actual mass flow rate to exceed the nominal value M_FLOW_NOMINAL_TOLERANCE = 0.01 (_, heat_m_flow_nominal) = mat_results.values(f'{load.id}.mHeaWat_flow_nominal') heat_m_flow_nominal = heat_m_flow_nominal[0] (_, cool_m_flow_nominal) = mat_results.values(f'{load.id}.mChiWat_flow_nominal') cool_m_flow_nominal = cool_m_flow_nominal[0] self.assertTrue( (heat_m_flow <= heat_m_flow_nominal + (heat_m_flow_nominal * M_FLOW_NOMINAL_TOLERANCE)).all(), f'Heating mass flow rate must be less than nominal mass flow rate ({heat_m_flow_nominal}) ' f'plus a tolerance ({M_FLOW_NOMINAL_TOLERANCE * 100}%)' ) self.assertTrue( (cool_m_flow <= cool_m_flow_nominal + (cool_m_flow_nominal * M_FLOW_NOMINAL_TOLERANCE)).all(), f'Cooling mass flow rate must be less than nominal mass flow rate ({cool_m_flow_nominal}) ' f'plus a tolerance ({M_FLOW_NOMINAL_TOLERANCE * 100}%)' ) # check the thermal load (_, load_q_req_hea_flow) = mat_results.values(f'{load.id}.QReqHea_flow') (_, load_q_req_coo_flow) = mat_results.values(f'{load.id}.QReqCoo_flow') (_, load_q_heat_flow) = mat_results.values(f'{load.id}.QHea_flow') (_, load_q_cool_flow) = mat_results.values(f'{load.id}.QCoo_flow') # make sure the q flow is positive load_q_req_hea_flow, load_q_req_coo_flow = np.abs(load_q_req_hea_flow), np.abs(load_q_req_coo_flow) load_q_heat_flow, load_q_cool_flow = np.abs(load_q_heat_flow), np.abs(load_q_cool_flow) cool_cvrmsd = self.cvrmsd(load_q_cool_flow, load_q_req_coo_flow) heat_cvrmsd = self.cvrmsd(load_q_heat_flow, load_q_req_hea_flow) CVRMSD_MAX = 0.3 # TODO: fix q flows to meet the CVRMSD maximum, then make these assertions rather than warnings if cool_cvrmsd >= CVRMSD_MAX: print(f'WARNING: The difference between the thermal cooling load of the load and ETS is too large (CVRMSD={cool_cvrmsd}). ' 'TODO: make this warning an assertion.') if heat_cvrmsd >= CVRMSD_MAX: print(f'WARNING: The difference between the thermal heating load of the load and ETS is too large (CVRMSD={heat_cvrmsd}). ' 'TODO: make this warning an assertion.')