def test_Teaser_heating(self):
        project_name = 'teaser_district_heating'
        self.data_dir, self.output_dir = self.set_up(os.path.dirname(__file__),
                                                     project_name)

        # load in the example geojson with a single office building
        filename = os.path.join(self.data_dir, "teaser_geojson_two_loads.json")
        self.gj = UrbanOptGeoJson(filename)

        # load system parameter data
        filename = os.path.join(self.data_dir,
                                "teaser_system_params_two_loads.json")
        sys_params = SystemParameters(filename)

        # create network and plant
        network = Network2Pipe(sys_params)
        heating_plant = HeatingPlantWithOptionalCHP(sys_params)

        # create our our load/ets/stubs
        all_couplings = [Coupling(network, heating_plant)]
        for geojson_load in self.gj.buildings:
            teaser_load = Teaser(sys_params, geojson_load)
            geojson_load_id = geojson_load.feature.properties["id"]
            heating_indirect_system = HeatingIndirect(sys_params,
                                                      geojson_load_id)
            cold_water_stub = EtsColdWaterStub(sys_params)
            all_couplings.append(Coupling(teaser_load,
                                          heating_indirect_system))
            all_couplings.append(Coupling(teaser_load, cold_water_stub))
            all_couplings.append(Coupling(heating_indirect_system, network))

        # create the couplings and graph
        graph = CouplingGraph(all_couplings)

        district = District(root_dir=self.output_dir,
                            project_name=project_name,
                            system_parameters=sys_params,
                            coupling_graph=graph)
        district.to_modelica()

        root_path = os.path.abspath(
            os.path.join(district._scaffold.districts_path.files_dir))
        self.run_and_assert_in_docker(
            os.path.join(root_path, 'DistrictEnergySystem.mo'),
            project_path=district._scaffold.project_path,
            project_name=district._scaffold.project_name)
Beispiel #2
0
def _parse_couplings(geojson, sys_params):
    """Given config files, construct the necessary models and their couplings which
    can then be passed to CouplingGraph.

    :param geojson: UrbanOptGeoJson
    :param sys_params: SystemParameters
    :return: list[Coupling], list of couplings to be passed to CouplingGraph
    """
    # Current implementation assumes that all generated district energy system models will have:
    #   - one heating plant
    #   - one cooling plant
    #   - one heating distribution network
    #   - one cooling distribution network
    #   - one heating and cooling ETS per load
    # NOTE: loads can be of any type/combination
    all_couplings = []

    # create the plants and networks
    cooling_network = Network2Pipe(sys_params)
    cooling_plant = CoolingPlant(sys_params)
    heating_network = Network2Pipe(sys_params)
    heating_plant = HeatingPlantWithOptionalCHP(sys_params)
    all_couplings += [
        Coupling(cooling_plant, cooling_network),
        Coupling(heating_plant, heating_network),
    ]

    # create the loads and their ETSes
    for building in geojson.buildings:
        load_model_type = sys_params.get_param_by_building_id(
            building.id, "load_model")
        load_class = LOAD_MODEL_TO_CLASS[load_model_type]
        load = load_class(sys_params, building)

        cooling_indirect = CoolingIndirect(sys_params, building.id)
        all_couplings.append(Coupling(load, cooling_indirect))
        all_couplings.append(Coupling(cooling_indirect, cooling_network))

        heating_indirect = HeatingIndirect(sys_params, building.id)
        all_couplings.append(Coupling(load, heating_indirect))
        all_couplings.append(Coupling(heating_indirect, heating_network))

    return all_couplings
Beispiel #3
0
    def test_chp_system(self):
        self.project_name = 'heat_with_chp'
        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_system_params_chp.json"
        self.sys_params = SystemParameters(filename)

        # create network and plant
        network = Network2Pipe(self.sys_params)
        heating_plant = HeatingPlantWithOptionalCHP(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)

        district = District(root_dir=self.output_dir,
                            project_name=self.project_name,
                            system_parameters=self.sys_params,
                            coupling_graph=graph)
        district.to_modelica()
Beispiel #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)
Beispiel #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.')