Пример #1
0
    def post_process(self, scaffold, building_names):
        """
        Cleanup the export of Spawn files into a format suitable for the district-based analysis. This includes
        the following:

            * Add a Loads project
            * Add a project level project

        :param scaffold: Scaffold object, Scaffold of the entire directory of the project.
        :param building_names: list, names of the buildings that need to be cleaned up after export
        :return: None
        """
        for b in building_names:
            b_modelica_path = os.path.join(scaffold.loads_path.files_dir, b)
            new_package = PackageParser.new_from_template(
                b_modelica_path,
                b, ["building", "coupling"],
                within=f"{scaffold.project_name}.Loads")
            new_package.save()

        # now create the Loads level package. This (for now) will create the package without considering any existing
        # files in the Loads directory.
        package = PackageParser.new_from_template(
            scaffold.loads_path.files_dir,
            "Loads",
            building_names,
            within=f"{scaffold.project_name}")
        package.save()

        # now create the Package level package. This really needs to happen at the GeoJSON to modelica stage, but
        # do it here for now to aid in testing.
        pp = PackageParser.new_from_template(scaffold.project_path,
                                             scaffold.project_name, ["Loads"])
        pp.save()
    def test_heating_indirect(self):
        project_name = "heating_indirect"
        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, "time_series_ex1.json")
        self.gj = UrbanOptGeoJson(filename)
        # scaffold the project ourselves
        scaffold = Scaffold(self.output_dir, project_name)
        scaffold.create()

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

        # currently we must setup the root project before we can run to_modelica
        package = PackageParser.new_from_template(scaffold.project_path,
                                                  scaffold.project_name,
                                                  order=[])
        package.save()
        # now test the connector (independent of the larger geojson translator)
        geojson_load_id = self.gj.buildings[0].feature.properties["id"]
        self.heating_indirect = HeatingIndirect(sys_params, geojson_load_id)
        self.heating_indirect.to_modelica(scaffold)

        root_path = os.path.abspath(
            os.path.join(scaffold.substations_path.files_dir))
        geojson_id = self.gj.buildings[0].feature.properties["id"]
        model_filepath = os.path.join(root_path,
                                      f'HeatingIndirect_{geojson_id}.mo')
        self.assertTrue(os.path.exists(model_filepath),
                        f"File does not exist: {model_filepath}")
    def test_new_from_template(self):
        package = PackageParser.new_from_template(self.output_dir,
                                                  'new_model_name',
                                                  ["model_a", "model_b"],
                                                  within="SomeWithin")
        package.save()

        self.assertTrue(
            os.path.exists(os.path.join(self.output_dir, 'package.mo')))
        self.assertTrue(
            os.path.exists(os.path.join(self.output_dir, 'package.order')))

        # check for strings in files
        with open(os.path.join(self.output_dir, 'package.mo')) as f:
            file_data = f.read()
            self.assertTrue('within SomeWithin;' in file_data,
                            'Incorrect within clause')
            self.assertTrue('package new_model_name' in file_data,
                            'Incorrect package definition')
            self.assertTrue('end new_model_name;' in file_data,
                            'Incorrect package ending')

        with open(os.path.join(self.output_dir, 'package.order')) as f:
            self.assertTrue('model_a\nmodel_b' in f.read(),
                            'Incorrect package order')
    def test_rename_model(self):
        package = PackageParser.new_from_template(self.output_dir,
                                                  'rename_model',
                                                  ["model_1", "model_2"],
                                                  within="RenameWithin")
        package.save()

        package.rename_model('model_1', 'my_super_new_model')
        self.assertEqual(len(package.order), 2)
        self.assertIn('my_super_new_model', package.order)
    def test_round_trip(self):
        package = PackageParser.new_from_template(self.output_dir,
                                                  'another_model',
                                                  ["model_x", "model_y"],
                                                  within="DifferentWithin")
        package.save()

        # Read in the package
        package = PackageParser(self.output_dir)
        self.assertListEqual(package.order, ["model_x", "model_y"])
    def post_process(self, scaffold):
        """
        Cleanup the export of TimeSeries files into a format suitable for the district-based analysis. This includes
        the following:
            * Add a Loads project
            * Add a project level project

        :param scaffold: Scaffold object, Scaffold of the entire directory of the project.
        :return: None
        """
        order_files = [Path(mo).stem for mo in self.required_mo_files]
        order_files += self.template_files_to_include
        b_modelica_path = Path(
            scaffold.loads_path.files_dir) / self.building_name
        new_package = PackageParser.new_from_template(
            path=b_modelica_path,
            name=self.building_name,
            order=order_files,
            within=f"{scaffold.project_name}.Loads")
        new_package.save()

        # now create the Loads level package and package.order.
        if not os.path.exists(
                os.path.join(scaffold.loads_path.files_dir, 'package.mo')):
            load_package = PackageParser.new_from_template(
                scaffold.loads_path.files_dir,
                "Loads", [self.building_name],
                within=f"{scaffold.project_name}")
            load_package.save()
        else:
            load_package = PackageParser(
                os.path.join(scaffold.loads_path.files_dir))
            load_package.add_model(self.building_name)
            load_package.save()

        # now create the Package level package. This really needs to happen at the GeoJSON to modelica stage, but
        # do it here for now to aid in testing.
        pp = PackageParser.new_from_template(scaffold.project_path,
                                             scaffold.project_name, ["Loads"])
        pp.save()
    def test_add_model(self):
        package = PackageParser.new_from_template(
            self.output_dir,
            'so_many_models', ["model_beta", "model_gamma"],
            within="SoMany")
        package.save()

        package.add_model('model_delta')
        package.add_model('model_alpha', 0)
        self.assertEqual(len(package.order), 4)
        self.assertListEqual(
            ['model_alpha', 'model_beta', 'model_gamma', 'model_delta'],
            package.order)
Пример #8
0
    def test_no_ets_and_run(self):
        project_name = "time_series_no_ets"
        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, "time_series_ex1.json")
        self.gj = UrbanOptGeoJson(filename)
        # scaffold the project ourselves
        scaffold = Scaffold(self.output_dir, project_name)
        scaffold.create()

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

        # now test the connector (independent of the larger geojson translator)
        self.time_series = TimeSeries(sys_params, self.gj.buildings[0])

        self.assertIsNotNone(self.time_series)
        self.assertIsNotNone(self.time_series.building)
        self.assertEqual(
            "time_series",
            self.time_series.system_parameters.get_param("buildings.custom")[0]
            ["load_model"])

        # currently we must setup the root project before we can run to_modelica
        package = PackageParser.new_from_template(scaffold.project_path,
                                                  scaffold.project_name,
                                                  order=[])
        package.save()
        self.time_series.to_modelica(scaffold)

        root_path = os.path.abspath(
            os.path.join(scaffold.loads_path.files_dir,
                         'B5a6b99ec37f4de7f94020090'))
        files = [
            os.path.join(root_path, 'building.mo'),
        ]

        # verify that there are only 2 files that matter (coupling and building)
        for file in files:
            self.assertTrue(os.path.exists(file),
                            f"File does not exist: {file}")
Пример #9
0
    def to_modelica(self,
                    project_name,
                    save_dir,
                    model_connector_str="TeaserConnector"):
        """
        Convert the data in the GeoJSON to modelica based-objects

        :param save_dir: str, directory where the exported project will be stored. The name of the project will be
                              {save_dir}/{project_name}
        :param model_connector_str: str, which model_connector to use
        """
        self.scaffold_directory(save_dir, project_name)

        # TODO: Create a map to load the required model_connectors
        import geojson_modelica_translator.model_connectors.teaser
        import geojson_modelica_translator.model_connectors.spawn
        mc_klass = getattr(geojson_modelica_translator.model_connectors.teaser,
                           model_connector_str)

        model_connector = mc_klass(self.system_parameters)

        _log.info("Exporting to Modelica")
        for building in self.buildings:
            # print("Jing2: ", building.feature.properties["type"])
            _log.info(
                f"Adding building to model connector: {mc_klass.__class__}")

            model_connector.add_building(building)

            _log.info(f"Translating building to model {building}")
            model_connector.to_modelica(self.scaffold,
                                        keep_original_models=False)

        # add in Substations
        # TODO: YL, where are the substations/ETSs?

        # add in Districts
        # add in Plants

        # now add in the top level package.
        pp = PackageParser.new_from_template(self.scaffold.project_path,
                                             project_name, ["Loads"])
        pp.save()
    def to_modelica(self, scaffold):
        """
        Create indirect cooling models based on the data in the buildings and geojsons

        :param scaffold: Scaffold object, Scaffold of the entire directory of the project.
        """
        cooling_indirect_template = self.template_env.get_template(
            "CoolingIndirect.mot")

        ets_data = self.system_parameters.get_param_by_building_id(
            self._geojson_load_id, 'ets_model_parameters.indirect')

        combined_template_data = {**ets_data, **self.district_template_data}

        self.run_template(
            template=cooling_indirect_template,
            save_file_name=os.path.join(scaffold.project_path, 'Substations',
                                        f'{self._model_filename}.mo'),
            project_name=scaffold.project_name,
            model_filename=self._model_filename,
            ets_data=combined_template_data,
        )

        # now create the Package level package. This really needs to happen at the GeoJSON to modelica stage, but
        # do it here for now to aid in testing.
        package = PackageParser(scaffold.project_path)
        if 'Substations' not in package.order:
            package.add_model('Substations')
            package.save()

        package_models = [self._model_filename]
        ets_package = PackageParser(scaffold.substations_path.files_dir)
        if ets_package.order_data is None:
            ets_package = PackageParser.new_from_template(
                path=scaffold.substations_path.files_dir,
                name="Substations",
                order=package_models,
                within=scaffold.project_name)
        else:
            for model_name in package_models:
                if model_name not in ets_package.order:
                    ets_package.add_model(model_name)
        ets_package.save()
Пример #11
0
    def post_process(self, scaffold, building_names, keep_original_models=False):
        """
        Cleanup the export of the TEASER files into a format suitable for the district-based analysis. This includes
        the following:

            * Update the partial to inherit from the GeojsonExport class defined in MBL.
            * Rename the files to remove the names of the buildings
            * Move the files to the Loads level and remove the Project folder (default export method from TEASER)
            * Add heat port
            * Add return temperature
            * Remove weaDat and rely on weaBus

        :param project_name: string, name of the project which will be used to set the package.mo file
        :param root_building_dir: string, where the project will be ultimately saved
        :param building_names: list, names of the buildings that need to be cleaned up after export
        :return: None
        """
        for b in building_names:
            # create a list of strings that we need to replace in all the file as we go along
            string_replace_list = []

            # create a new modelica based path for the buildings # TODO: make this work at the toplevel, somehow.
            b_modelica_path = ModelicaPath(f"B{b}", scaffold.loads_path.files_dir, True)

            # copy over the entire model to the new location
            copytree(
                os.path.join(scaffold.loads_path.files_dir, f"Project/B{b}/B{b}_Models"),
                b_modelica_path.files_dir,
            )

            # read in the package to apply the changes as they other files are processed
            # TODO: these should be linked, so a rename method should act across the model and the package.order
            package = PackageParser(os.path.join(scaffold.loads_path.files_dir, f"B{b}"))

            # move the internal gains files to a new resources folder
            mat_files = glob.glob(os.path.join(scaffold.loads_path.files_dir, f"B{b}/*.txt"))
            for f in mat_files:
                new_file_name = os.path.basename(f).replace(f"B{b}", "")
                os.rename(f, f"{b_modelica_path.resources_dir}/{new_file_name}")
                string_replace_list.append(
                    (
                        f"Project/B{b}/B{b}_Models/{os.path.basename(f)}",
                        f"Loads/{b_modelica_path.resources_relative_dir}/{new_file_name}",
                    )
                )

            # process each of the building models
            mo_files = glob.glob(os.path.join(scaffold.loads_path.files_dir, f"B{b}/*.mo"))
            for f in mo_files:
                # ignore the package.mo file
                if os.path.basename(f) == "package.mo":
                    continue

                mofile = InputParser(f)

                # previous paths and replace with the new one.
                # Make sure to update the names of any resources as well.
                mofile.replace_within_string(f"{scaffold.project_name}.Loads.B{b}")

                # remove ReaderTMY3
                mofile.remove_object("ReaderTMY3")

                # updating path to internal loads
                for s in string_replace_list:
                    mofile.replace_model_string("Modelica.Blocks.Sources.CombiTimeTable", "internalGains", s[0], s[1])

                # add heat port
                data = [
                    "annotation (Placement(transformation(extent={{-10,90},{10,110}}), "
                    "iconTransformation(extent={{-10,90},{10,110}})));"
                ]
                mofile.add_model_object(
                    "Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a", "port_a", data,
                )

                # add TAir output
                # TODO: read in the object by name -- parse the parenthetic content
                instance = 'TAir(\n    quantity="ThermodynamicTemperature", unit="K", displayUnit="degC")'
                data = [
                    '"Room air temperature"',
                    "annotation (Placement(transformation(extent={{100,-10},{120,10}})));",
                ]
                mofile.add_model_object(
                    "Buildings.Controls.OBC.CDL.Interfaces.RealOutput", instance, data
                )

                # All existing weaDat.weaBus connections need to be updated to simply weaBus
                mofile.replace_connect_string('weaDat.weaBus', None, 'weaBus', None, True)
                # Now remove the redundant weaBus -> weaBus connection
                mofile.remove_connect_string('weaBus', 'weaBus')

                # add new port connections
                if self.system_parameters.get_param(
                        "buildings.default.load_model_parameters.rc.order", default=2) == 1:
                    data = "annotation (Line(points={{0,100},{96,100},{96,20},{92,20}}, color={191,0,0}))"
                    mofile.add_connect("port_a", "thermalZoneOneElement.intGainsConv", data)

                    data = "annotation (Line(points={{93,32},{98,32},{98,0},{110,0}}, color={0,0,127}))"
                    mofile.add_connect("thermalZoneOneElement.TAir", "TAir", data)
                elif self.system_parameters.get_param(
                        "buildings.default.load_model_parameters.rc.order", default=2) == 2:
                    data = "annotation (Line(points={{0,100},{96,100},{96,20},{92,20}}, color={191,0,0}))"
                    mofile.add_connect("port_a", "thermalZoneTwoElements.intGainsConv", data)

                    data = "annotation (Line(points={{93,32},{98,32},{98,0},{110,0}}, color={0,0,127}))"
                    mofile.add_connect("thermalZoneTwoElements.TAir", "TAir", data)
                elif self.system_parameters.get_param(
                        "buildings.default.load_model_parameters.rc.order", default=2) == 4:
                    data = "annotation (Line(points={{0,100},{96,100},{96,20},{92,20}}, color={191,0,0}))"
                    mofile.add_connect("port_a", "thermalZoneFourElements.intGainsConv", data)

                    data = "annotation (Line(points={{93,32},{98,32},{98,0},{110,0}}, color={0,0,127}))"
                    mofile.add_connect("thermalZoneFourElements.TAir", "TAir", data)

                # change the name of the modelica model to remove the building id, update in package too!
                new_model_name = mofile.model["name"].split("_")[1]
                package.rename_model(mofile.model["name"], new_model_name)
                mofile.model["name"] = new_model_name

                # Save as the new filename (without building ID)
                new_filename = os.path.join(
                    scaffold.loads_path.files_dir, f'B{b}/{os.path.basename(f).split("_")[1]}'
                )
                mofile.save_as(new_filename)
                os.remove(f)

            # save the updated package.mo and package.order.
            new_package = PackageParser.new_from_template(
                package.path, f"B{b}", package.order, within=f"{scaffold.project_name}.Loads"
            )
            new_package.save()

        # remaining clean up tasks across the entire exported project
        if not keep_original_models:
            shutil.rmtree(os.path.join(scaffold.loads_path.files_dir, "Project"))

        # now create the Loads level package. This (for now) will create the package without considering any existing
        # files in the Loads directory.
        # add in the silly 'B' before the building names
        package = PackageParser.new_from_template(
            scaffold.loads_path.files_dir,
            "Loads", ["B" + b for b in building_names],
            within=f"{scaffold.project_name}"
        )
        package.save()
    def post_process(self, scaffold, keep_original_models=False):
        """Cleanup the export of the TEASER files into a format suitable for the district-based analysis.
        This includes the following:

            * Update the partial to inherit from the GeojsonExport class defined in MBL.
            * Rename the files to remove the names of the buildings
            * Move the files to the Loads level and remove the Project folder (default export method from TEASER)
            * Add heat port
            * Add return temperature
            * Add fluid ports for the indoor air volume.
            * Remove weaDat and rely on weaBus
            * Add latent fraction multiplier
            * Add TAir output
            * Add TRad output
            * Add nPorts variable
            * Propagate use of moisture balance
            * Wrap the thermal zones into a single model

        :param scaffold: Scaffold object, Scaffold of the entire directory of the project.
        :param keep_original_models: boolean, do not delete the original models generated by TEASER
        :return: None
        """

        teaser_building = self.template_env.get_template("TeaserBuilding.mot")
        teaser_coupling = self.template_env.get_template(
            "TeaserCouplingBuilding.mot")
        run_coupling_template = self.template_env.get_template(
            "RunTeaserCouplingBuilding.most")

        # This for loop does *a lot* of work to make the models compatible for the project structure.
        # Need to investigate moving this into a more testable location.
        # create a list of strings that we need to replace in all the file as we go along
        string_replace_list = []
        mos_weather_filename = self.system_parameters.get_param_by_building_id(
            self.building_id, "load_model_parameters.rc.mos_weather_filename")
        # create a new modelica based path for the buildings # TODO: make this work at the toplevel, somehow.
        b_modelica_path = ModelicaPath(self.building_name,
                                       scaffold.loads_path.files_dir, True)

        # copy over the entire model to the new location
        copytree(
            os.path.join(
                scaffold.loads_path.files_dir,
                f"Project/{self.building_name}/{self.building_name}_Models"),
            b_modelica_path.files_dir,
        )

        # read in the package to apply the changes as they other files are processed
        # TODO: these should be linked, so a rename method should act across the model and the package.order
        package = PackageParser(
            os.path.join(scaffold.loads_path.files_dir, self.building_name))

        # move the internal gains files to a new resources folder
        mat_files = glob.glob(
            os.path.join(scaffold.loads_path.files_dir,
                         f"{self.building_name}/*.txt"))
        for f in mat_files:
            new_file_name = os.path.basename(f).replace(self.building_name, "")
            os.rename(f, f"{b_modelica_path.resources_dir}/{new_file_name}")

            # The main branch of teaser has yet to merge in the changes to support the fixes to the
            # internal gain files. The next method can be removed once the TEASER development branch is
            # merged into master/main and released.
            self.fix_gains_file(
                f"{b_modelica_path.resources_dir}/{new_file_name}")

            string_replace_list.append((
                f"Project/{self.building_name}/{self.building_name}_Models/{os.path.basename(f)}",
                f"{scaffold.project_name}/Loads/{b_modelica_path.resources_relative_dir}/{new_file_name}",
            ))

        # process each of the thermal zones
        thermal_zone_files = []
        mo_files = glob.glob(
            os.path.join(scaffold.loads_path.files_dir,
                         f"{self.building_name}/*.mo"))
        for f in mo_files:
            # ignore the package.mo file
            if os.path.basename(f) in ["package.mo"]:
                continue

            mofile = Model(f)

            # previous paths and replace with the new one.
            # Make sure to update the names of any resources as well.
            mofile.set_within_statement(
                f'{scaffold.project_name}.Loads.{self.building_name}')

            # remove ReaderTMY3
            mofile.remove_component(
                "Buildings.BoundaryConditions.WeatherData.ReaderTMY3",
                "weaDat")

            # updating path to internal loads
            for s in string_replace_list:
                new_file_path = s[1]
                new_resource_arg = f'''Modelica.Utilities.Files.loadResource("modelica://{new_file_path}")'''
                old_file_path = s[0]
                old_resource_arg = f'''Modelica.Utilities.Files.loadResource("modelica://{old_file_path}")'''

                mofile.update_component_modification(
                    "Modelica.Blocks.Sources.CombiTimeTable",
                    "internalGains",
                    "fileName",
                    new_resource_arg,
                    if_value=old_resource_arg)

            # add heat port convective heat flow.
            mofile.insert_component(
                "Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a",
                "port_a",
                string_comment='Heat port for convective heat flow.',
                annotations=[
                    "Placement(transformation(extent={{-10,90},{10,110}}), "
                    "iconTransformation(extent={{-10,90},{10,110}}))"
                ])
            # add heat port radiative heat flow.
            mofile.insert_component(
                "Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a",
                "port_b",
                string_comment='Heat port for radiative heat flow.',
                annotations=[
                    "Placement(transformation(extent={{30,-110},{50,-90}}, "
                    "iconTransformation(extent={{40,-112},{60,-92}})))"
                ])
            # add fluid ports for the indoor air volume.
            mofile.insert_component(
                "Modelica.Fluid.Vessels.BaseClasses.VesselFluidPorts_b",
                "ports[nPorts]",
                string_comment=
                'Auxiliary fluid inlets and outlets to indoor air volume.',
                modifications={
                    'redeclare each final package Medium':
                    'Buildings.Media.Air'
                },
                annotations=[
                    "Placement(transformation(extent={{-30, -8}, {30, 8}},origin={0, -100}), "
                    "iconTransformation(extent={{-23.25, -7.25}, {23.25, 7.25}},"
                    "origin={-0.75, -98.75}))"
                ])

            fraction_latent_person = self.system_parameters.get_param(
                "buildings.default.load_model_parameters.rc.fraction_latent_person",
                default=1.25)

            use_moisture_balance = self.system_parameters.get_param(
                "buildings.default.load_model_parameters.rc.use_moisture_balance",
                default='false')

            n_ports = self.system_parameters.get_param(
                "buildings.default.load_model_parameters.rc.nPorts", default=0)

            # create a new parameter for fraction latent person
            mofile.add_parameter(
                'Real',
                'fraLat',
                assigned_value=fraction_latent_person,
                string_comment=
                'Fraction latent of sensible persons load = 0.8 for home, 1.25 for office.'
            )
            # create a new Boolean parameter to evaluate the persons latent loads.
            mofile.add_parameter(
                'Boolean',
                'use_moisture_balance',
                assigned_value=use_moisture_balance,
                string_comment=
                'If true, input connector QLat_flow is enabled and room air computes'
                ' moisture balance.')
            # create a integer parameter to evaluate number of connected ports.
            mofile.add_parameter('Integer',
                                 'nPorts',
                                 assigned_value=n_ports,
                                 string_comment='Number of fluid ports.',
                                 annotations=['connectorSizing=true'])
            # Set the fraction latent person in the template by simply replacing the value
            mofile.insert_component(
                'Modelica.Blocks.Sources.RealExpression',
                'perLatLoa',
                modifications={
                    'y': 'internalGains.y[2]*fraLat',
                },
                conditional='if use_moisture_balance',
                string_comment='Latent person loads',
                annotations=[
                    'Placement(transformation(extent={{-80,-60},{-60,-40}}))'
                ])

            # add TRad output
            mofile.insert_component(
                'Buildings.Controls.OBC.CDL.Interfaces.RealOutput',
                'TRad',
                modifications={
                    'quantity': '"ThermodynamicTemperature"',
                    'unit': '"K"',
                    'displayUnit': '"degC"',
                },
                string_comment='Mean indoor radiation temperature',
                annotations=[
                    'Placement(transformation(extent={{100,-10},{120,10}}))'
                ])

            # All existing weaDat.weaBus connections need to be updated to simply weaBus
            # (except for the connections where 'weaBus' is port_b, we will just delete these)
            mofile.edit_connect('weaDat.weaBus',
                                '!weaBus',
                                new_port_a='weaBus')
            # Now remove the unnecessary weaDat.weaBus -> weaBus connection
            mofile.remove_connect('weaDat.weaBus', 'weaBus')

            # add new port connections
            rc_order = self.system_parameters.get_param_by_building_id(
                self.building_id, "load_model_parameters.rc.order", default=2)
            thermal_zone_name = None
            thermal_zone_type = None
            if rc_order == 1:
                thermal_zone_type = 'OneElement'
                thermal_zone_name = 'thermalZoneOneElement'
            elif rc_order == 2:
                thermal_zone_type = 'TwoElements'
                thermal_zone_name = 'thermalZoneTwoElements'
            elif rc_order == 3:
                thermal_zone_type = 'ThreeElements'
                thermal_zone_name = 'thermalZoneThreeElements'
            elif rc_order == 4:
                thermal_zone_type = 'FourElements'
                thermal_zone_name = 'thermalZoneFourElements'

            if thermal_zone_name is not None and thermal_zone_type is not None:
                # add TAir output - This has been moved away from the other insert_component blocks
                # to use thermal_zone_name
                mofile.insert_component(
                    'Buildings.Controls.OBC.CDL.Interfaces.RealOutput',
                    'TAir',
                    modifications={
                        'quantity': '"ThermodynamicTemperature"',
                        'unit': '"K"',
                        'displayUnit': '"degC"',
                    },
                    conditional=
                    f'if {thermal_zone_name}.ATot > 0 or {thermal_zone_name}.VAir > 0',
                    string_comment='Room air temperature',
                    annotations=[
                        'Placement(transformation(extent={{100,38},{120,58}}))'
                    ])

                # In TEASER 0.7.5 the hConvWinOut, hConvExt, hConvWin, hConvInt, hConvFloor, hConvRoof in various of
                # the ReducedOrder models should be hCon* not hConv*. This has been fixed on the development branch
                # of TEASER, but the team doesn't appear to be releasing nor merging the development branch (yet).
                mofile.rename_component_argument(
                    "Buildings.ThermalZones.ReducedOrder.EquivalentAirTemperature.VDI6007WithWindow",
                    "eqAirTemp", "hConvWallOut", "hConWallOut")
                mofile.rename_component_argument(
                    "Buildings.ThermalZones.ReducedOrder.EquivalentAirTemperature.VDI6007WithWindow",
                    "eqAirTemp", "hConvWinOut", "hConWinOut")

                mofile.rename_component_argument(
                    "Buildings.ThermalZones.ReducedOrder.EquivalentAirTemperature.VDI6007",
                    "eqAirTempVDI", "hConvWallOut", "hConWallOut")

                renames = {
                    "hConvExt": "hConExt",
                    "hConvFloor": "hConFloor",
                    "hConvRoof": "hConRoof",
                    "hConvWinOut": "hConWinOut",
                    "hConvWin": "hConWin",
                    "hConvInt": "hConInt",
                }
                for from_, to_ in renames.items():
                    mofile.rename_component_argument(
                        f"Buildings.ThermalZones.ReducedOrder.RC.{thermal_zone_type}",
                        thermal_zone_name, from_, to_)

                mofile.update_component_modifications(
                    f"Buildings.ThermalZones.ReducedOrder.RC.{thermal_zone_type}",
                    thermal_zone_name,
                    {"use_moisture_balance": "use_moisture_balance"})

                mofile.update_component_modifications(
                    f"Buildings.ThermalZones.ReducedOrder.RC.{thermal_zone_type}",
                    thermal_zone_name, {"nPorts": "nPorts"})

                mofile.add_connect(
                    'port_a',
                    f'{thermal_zone_name}.intGainsConv',
                    annotations=[
                        'Line(points={{0,100},{96,100},{96,20},{92,20}}, color={191,0,0})'
                    ])

                mofile.add_connect(
                    f'{thermal_zone_name}.TAir',
                    'TAir',
                    annotations=[
                        'Line(points={{93,32},{98,32},{98,48},{110,48}}, color={0,0,127})'
                    ])
                mofile.add_connect(
                    f'{thermal_zone_name}.TRad',
                    'TRad',
                    annotations=[
                        'Line(points={{93,28},{98,28},{98,-20},{110,-20}}, color={0,0,127})'
                    ])
                mofile.add_connect(
                    f'{thermal_zone_name}.QLat_flow',
                    'perLatLoa.y',
                    annotations=[
                        'Line(points={{43,4},{40,4},{40,-28},{-40,-28},{-40,-50},{-59,-50}}, color={0, 0,127})'
                    ])

                mofile.add_connect(
                    f'{thermal_zone_name}.intGainsRad',
                    'port_b',
                    annotations=[
                        'Line(points={{92, 24}, {98, 24}, {98, -100}, {40, -100}}, color={191, 0, 0})'
                    ])
                mofile.insert_equation_for_loop(
                    index_identifier="i",
                    expression_raw="1:nPorts",
                    loop_body_raw_list=[
                        "connect(ports[i], thermalZoneFourElements.ports[i])",
                        "\tannotation (Line(",
                        "\tpoints={{-18,-102},{-18,-84},{83,-84},{83,-1.95}},",
                        "\tcolor={0,127,255},",
                        "\tsmooth=Smooth.None));",
                    ],
                )

            # change the name of the modelica model to remove the building id, update in package too!
            original_model_name = mofile.get_name()
            new_model_name = original_model_name.split("_")[1]
            thermal_zone_files.append(new_model_name)
            package.rename_model(original_model_name, new_model_name)
            mofile.set_name(new_model_name)

            # Save as the new filename (without building ID)
            new_filename = os.path.join(
                scaffold.loads_path.files_dir,
                f'{self.building_name}/{os.path.basename(f).split("_")[1]}')
            mofile.save_as(new_filename)
            os.remove(f)

        # Now connect all the thermal zone files into the teaser building
        # 1. Need to a map of thermal zone names and instances
        zone_list = []
        for index, tz in enumerate(thermal_zone_files):
            # take /a/file/Meeting.mo -> zone_map["Meeting"] = "meeting"
            tz_process = os.path.splitext(os.path.basename(tz))[0]
            zone_list.append({
                "index":
                index,
                "model_name":
                tz_process,
                "instance_name":
                tz_process.lower(),
                # process where this will be stored in python otherwise too many {{}}, yes ridiculous.
                # This needs to result in {{a,b},{x,y}}
                "placement":
                f"{{{{{-160 + index * 40},-20}},{{{-140 + index * 40},0}}}}"
            })

        # Handle setting nominal load for IT room zone
        nom_cool_flow = np.array([-10000] * len(zone_list))
        for i, dic in enumerate(zone_list):
            if dic["instance_name"] == "ict":
                print("setting coo flow")
                nom_cool_flow[
                    i - 1] = -50000  # Need to offset for different indexing
        nom_heat_flow = np.array([10000] * len(zone_list))
        building_template_data = {
            "thermal_zones":
            zone_list,
            "nominal_heat_flow":
            str(repr(nom_heat_flow))[1:-1].replace("[", "{").replace(
                "]", "}").split("rray(", 1)[-1],
            "nominal_cool_flow":
            str(repr(nom_cool_flow))[1:-1].replace("[", "{").replace(
                "]", "}").split("rray(", 1)[-1],
            "load_resources_path":
            b_modelica_path.resources_relative_dir,
            "mos_weather": {
                "mos_weather_filename": mos_weather_filename,
                "filename": os.path.basename(mos_weather_filename),
                "path": os.path.dirname(mos_weather_filename),
            },
            "nominal_values": {
                # Adding 273.15 to convert from C to K (for absolute temps, not relative temps)
                "chw_supply_temp":
                self.system_parameters.get_param_by_building_id(
                    self.building_id,
                    "load_model_parameters.rc.temp_chw_supply") + 273.15,
                "chw_return_temp":
                self.system_parameters.get_param_by_building_id(
                    self.building_id,
                    "load_model_parameters.rc.temp_chw_return") + 273.15,
                "hhw_supply_temp":
                self.system_parameters.get_param_by_building_id(
                    self.building_id,
                    "load_model_parameters.rc.temp_hw_supply") + 273.15,
                "hhw_return_temp":
                self.system_parameters.get_param_by_building_id(
                    self.building_id,
                    "load_model_parameters.rc.temp_hw_return") + 273.15,
                "temp_setpoint_heating":
                self.system_parameters.get_param_by_building_id(
                    self.building_id,
                    "load_model_parameters.rc.temp_setpoint_heating") + 273.15,
                "temp_setpoint_cooling":
                self.system_parameters.get_param_by_building_id(
                    self.building_id,
                    "load_model_parameters.rc.temp_setpoint_cooling") + 273.15
            }
        }

        # merge ets template values from load_base.py into the building nominal values
        # If there is no ets defined in sys-param file, use the building template data alone
        try:
            nominal_values = {
                **building_template_data['nominal_values'],
                **self.ets_template_data
            }
            combined_template_data = {
                **building_template_data,
                **nominal_values
            }
        except AttributeError:
            combined_template_data = building_template_data

        self.run_template(teaser_building,
                          os.path.join(b_modelica_path.files_dir,
                                       "building.mo"),
                          project_name=scaffold.project_name,
                          model_name=self.building_name,
                          data=combined_template_data)

        self.run_template(teaser_coupling,
                          os.path.join(
                              os.path.join(b_modelica_path.files_dir,
                                           "coupling.mo")),
                          project_name=scaffold.project_name,
                          model_name=self.building_name,
                          data=combined_template_data)

        full_model_name = os.path.join(scaffold.project_name,
                                       scaffold.loads_path.files_relative_dir,
                                       self.building_name,
                                       "coupling").replace(os.path.sep, '.')

        self.run_template(
            run_coupling_template,
            os.path.join(
                os.path.join(b_modelica_path.scripts_dir,
                             "RunTeaserCouplingBuilding.mos")),
            full_model_name=full_model_name,
            model_name="coupling",
        )

        # copy over the required mo files and add the other models to the package order
        mo_files = self.copy_required_mo_files(
            b_modelica_path.files_dir, within=f'{scaffold.project_name}.Loads')
        for f in mo_files:
            package.add_model(os.path.splitext(os.path.basename(f))[0])
        package.add_model('building')
        package.add_model('coupling')

        # save the updated package.mo and package.order in the Loads.B{} folder
        new_package = PackageParser.new_from_template(
            package.path,
            self.building_name,
            package.order,
            within=f"{scaffold.project_name}.Loads")
        new_package.save()
        # AA added this 9/24
        if os.path.exists(
                building_template_data["mos_weather"]["mos_weather_filename"]):
            shutil.copy(
                building_template_data["mos_weather"]["mos_weather_filename"],
                os.path.join(
                    b_modelica_path.resources_dir,
                    building_template_data["mos_weather"]["filename"]))
        else:
            raise Exception(
                f"Missing MOS weather file for Spawn: {building_template_data['mos_weather']['mos_weather_filename']}"
            )
        # end of what AA added 9/24

        # remaining clean up tasks across the entire exported project
        if not keep_original_models:
            shutil.rmtree(
                os.path.join(scaffold.loads_path.files_dir, "Project"))

        # now create the Loads level package and package.order.
        if not os.path.exists(
                os.path.join(scaffold.loads_path.files_dir, 'package.mo')):
            load_package = PackageParser.new_from_template(
                scaffold.loads_path.files_dir,
                "Loads", [self.building_name],
                within=f"{scaffold.project_name}")
            load_package.save()
        else:
            load_package = PackageParser(
                os.path.join(scaffold.loads_path.files_dir))
            load_package.add_model(self.building_name)
            load_package.save()

        # now create the Package level package. This really needs to happen at the GeoJSON to modelica stage, but
        # do it here for now to aid in testing.
        package = PackageParser(scaffold.project_path)
        if 'Loads' not in package.order:
            package.add_model('Loads')
            package.save()
Пример #13
0
    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.
        """
        if self.chp_installed:
            template_data = {
                "nominal_values": {
                    "heat_flow_nominal":
                    self.system_parameters.get_param(
                        "$.district_system.default.central_heating_plant_parameters.heat_flow_nominal"
                    ),
                    "mass_hhw_flow_nominal":
                    self.system_parameters.get_param(
                        "$.district_system.default.central_heating_plant_parameters.mass_hhw_flow_nominal"
                    ),
                    "boiler_water_flow_minimum":
                    self.system_parameters.get_param(
                        "$.district_system.default.central_heating_plant_parameters.boiler_water_flow_minimum"
                    ),
                    "pressure_drop_hhw_nominal":
                    self.system_parameters.get_param(
                        "$.district_system.default.central_heating_plant_parameters.pressure_drop_hhw_nominal"
                    ),
                    "pressure_drop_setpoint":
                    self.system_parameters.get_param(
                        "$.district_system.default.central_heating_plant_parameters.pressure_drop_setpoint"
                    ),
                    "temp_setpoint_hhw":
                    self.system_parameters.get_param(
                        "$.district_system.default.central_heating_plant_parameters.temp_setpoint_hhw"
                    ) + 273.15,
                    "pressure_drop_hhw_valve_nominal":
                    self.system_parameters.get_param(
                        "$.district_system.default.central_heating_plant_parameters.pressure_drop_hhw_valve_nominal"
                    ),
                },
                "signals": {
                    "thermal_following":
                    str(
                        self.system_parameters.get_param(
                            "$.district_system.default.central_heating_plant_parameters.chp_thermal_following"
                        )).
                    lower(
                    ),  # Booleans in Python start with a capital letter. Modelica wants it lowercase, hence this.
                },
            }
            plant_template = self.template_env.get_template(
                "HeatingPlantWithCHP.mot")
            self.run_template(
                template=plant_template,
                save_file_name=Path(scaffold.plants_path.files_dir) /
                "CentralHeatingPlant.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 = ['CentralHeatingPlant'
                          ] + [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 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.
        """
        mos_wet_bulb_filename = self.system_parameters.get_param(
            "$.district_system.default.central_cooling_plant_parameters.mos_wet_bulb_filename"
        )

        if mos_wet_bulb_filename is None:
            raise FileNotFoundError(
                'Cooling plant\'s mos_wet_bulb_filename was not provided')

        template_data = {
            "nominal_values": {
                "delta_temp":
                self.system_parameters.get_param(
                    "$.district_system.default.central_cooling_plant_parameters.cooling_tower_water_temperature_difference_nominal"
                ),
                "fan_power":
                self.system_parameters.get_param(
                    "$.district_system.default.central_cooling_plant_parameters.cooling_tower_fan_power_nominal"
                ),
                "chilled_water_pump_pressure_head":
                self.system_parameters.get_param(
                    "$.district_system.default.central_cooling_plant_parameters.chw_pump_head"
                ),
                "condenser_water_pump_pressure_head":
                self.system_parameters.get_param(
                    "$.district_system.default.central_cooling_plant_parameters.cw_pump_head"
                ),
                "heat_flow_nominal":
                self.system_parameters.get_param(
                    "$.district_system.default.central_cooling_plant_parameters.heat_flow_nominal"
                ),
                "mass_chw_flow_nominal":
                self.system_parameters.get_param(
                    "$.district_system.default.central_cooling_plant_parameters.mass_chw_flow_nominal"
                ),
                "mass_cw_flow_nominal":
                self.system_parameters.get_param(
                    "$.district_system.default.central_cooling_plant_parameters.mass_cw_flow_nominal"
                ),
                "chiller_water_flow_minimum":
                self.system_parameters.get_param(
                    "$.district_system.default.central_cooling_plant_parameters.chiller_water_flow_minimum"
                ),
                "pressure_drop_chw_nominal":
                self.system_parameters.get_param(
                    "$.district_system.default.central_cooling_plant_parameters.pressure_drop_chw_nominal"
                ),
                "pressure_drop_cw_nominal":
                self.system_parameters.get_param(
                    "$.district_system.default.central_cooling_plant_parameters.pressure_drop_cw_nominal"
                ),
                "pressure_drop_setpoint":
                self.system_parameters.get_param(
                    "$.district_system.default.central_cooling_plant_parameters.pressure_drop_setpoint"
                ),
                "temp_setpoint_chw":
                self.system_parameters.get_param(
                    "$.district_system.default.central_cooling_plant_parameters.temp_setpoint_chw"
                ),
                "pressure_drop_chw_valve_nominal":
                self.system_parameters.get_param(
                    "$.district_system.default.central_cooling_plant_parameters.pressure_drop_chw_valve_nominal"
                ),
                "pressure_drop_cw_pum_nominal":
                self.system_parameters.get_param(
                    "$.district_system.default.central_cooling_plant_parameters.pressure_drop_cw_pum_nominal"
                ),
                "temp_air_wb_nominal":
                self.system_parameters.get_param(
                    "$.district_system.default.central_cooling_plant_parameters.temp_air_wb_nominal"
                ),
                "temp_cw_in_nominal":
                self.system_parameters.get_param(
                    "$.district_system.default.central_cooling_plant_parameters.temp_cw_in_nominal"
                ),
                "delta_temp_approach":
                self.system_parameters.get_param(
                    "$.district_system.default.central_cooling_plant_parameters.delta_temp_approach"
                ),
                "ratio_water_air_nominal":
                self.system_parameters.get_param(
                    "$.district_system.default.central_cooling_plant_parameters.ratio_water_air_nominal"
                ),
            },
            "wet_bulb_calc": {
                "mos_wet_bulb_filename": mos_wet_bulb_filename,
                "filename": Path(mos_wet_bulb_filename).name,
                "path": Path(mos_wet_bulb_filename).parent,
                "modelica_path": self.modelica_path(mos_wet_bulb_filename),
            },
        }

        plant_template = self.template_env.get_template(
            "CentralCoolingPlant.mot")
        self.run_template(plant_template,
                          os.path.join(scaffold.plants_path.files_dir,
                                       "CentralCoolingPlant.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 = ['CentralCoolingPlant'
                          ] + [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()
Пример #15
0
    def to_modelica(self):
        """Generate modelica files for the models as well as the modelica file for
        the entire district system.
        """
        # scaffold the project
        self._scaffold.create()
        self.district_model_filepath = Path(
            self._scaffold.districts_path.files_dir
        ) / 'DistrictEnergySystem.mo'

        # create the root package
        root_package = PackageParser.new_from_template(
            self._scaffold.project_path, self._scaffold.project_name, order=[])
        root_package.save()

        # generate model modelica files
        for model in self._coupling_graph.models:
            model.to_modelica(self._scaffold)

        # construct graph of visual components
        diagram = Diagram(self._coupling_graph)

        district_template_params = {
            "district_within_path":
            '.'.join([self._scaffold.project_name, 'Districts']),
            "diagram":
            diagram,
            "couplings": [],
            "models": []
        }
        common_template_params = {
            'globals': {
                'medium_w': 'MediumW',
                'delChiWatTemBui': 'delChiWatTemBui',
                'delChiWatTemDis': 'delChiWatTemDis',
                'delHeaWatTemBui': 'delHeaWatTemBui',
                'delHeaWatTemDis': 'delHeaWatTemDis',
            },
            'graph': self._coupling_graph,
            'sys_params': {
                'district_system':
                self.system_parameters.get_param('$.district_system')
            }
        }
        # render each coupling
        for coupling in self._coupling_graph.couplings:
            template_context = {
                'diagram': diagram.to_dict(coupling.id, is_coupling=True),
            }
            template_context.update(**common_template_params)

            coupling_load = coupling.get_load()
            if coupling_load is not None:
                building_sys_params = self.system_parameters.get_param_by_building_id(
                    coupling_load.building_id, '$')
                template_context['sys_params'][
                    'building'] = building_sys_params

            templated_result = coupling.render_templates(template_context)
            district_template_params['couplings'].append({
                'id':
                coupling.id,
                'component_definitions':
                templated_result['component_definitions'],
                'connect_statements':
                templated_result['connect_statements'],
                'coupling_definitions_template_path':
                templated_result['component_definitions_template_path'],
                'connect_statements_template_path':
                templated_result['connect_statements_template_path'],
            })

        # render each model instance
        for model in self._coupling_graph.models:
            template_params = {
                'model': model.to_dict(self._scaffold),
                'couplings': self._coupling_graph.couplings_by_type(model.id),
                'diagram': diagram.to_dict(model.id, is_coupling=False),
            }
            template_params.update(**common_template_params)

            if issubclass(type(model), LoadBase):
                building_sys_params = self.system_parameters.get_param_by_building_id(
                    model.building_id, '$')
                template_params['sys_params']['building'] = building_sys_params

            templated_instance, instance_template_path = model.render_instance(
                template_params)
            district_template_params['models'].append({
                'id':
                model.id,
                'instance_template_path':
                instance_template_path,
                'instance':
                templated_instance
            })

        # render the full district file
        final_result = render_template('DistrictEnergySystem.mot',
                                       district_template_params)
        with open(self.district_model_filepath, 'w') as f:
            f.write(final_result)

        districts_package = PackageParser.new_from_template(
            self._scaffold.districts_path.files_dir,
            "Districts", ['DistrictEnergySystem'],
            within=f"{self._scaffold.project_name}")
        districts_package.save()
        root_package = PackageParser(self._scaffold.project_path)
        root_package.add_model('Districts')
        root_package.save()