Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
    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
Ejemplo n.º 4
0
    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)
Ejemplo n.º 5
0
    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.')