• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

urbanopt / geojson-modelica-translator / 8333659328

18 Mar 2024 09:09PM UTC coverage: 89.052% (-0.04%) from 89.093%
8333659328

push

github

vtnate
change from quit to warn when loads are not found

934 of 1117 branches covered (83.62%)

Branch coverage included in aggregate %.

3 of 3 new or added lines in 1 file covered. (100.0%)

9 existing lines in 2 files now uncovered.

2588 of 2838 relevant lines covered (91.19%)

1.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.55
/geojson_modelica_translator/model_connectors/districts/district.py
1
# :copyright (c) URBANopt, Alliance for Sustainable Energy, LLC, and other contributors.
2
# See also https://github.com/urbanopt/geojson-modelica-translator/blob/develop/LICENSE.md
3

4
import logging
2✔
5
from pathlib import Path
2✔
6

7
from jinja2 import Environment, FileSystemLoader, StrictUndefined
2✔
8
from modelica_builder.modelica_mos_file import ModelicaMOS
2✔
9
from modelica_builder.package_parser import PackageParser
2✔
10

11
from geojson_modelica_translator.jinja_filters import ALL_CUSTOM_FILTERS
2✔
12
from geojson_modelica_translator.model_connectors.couplings.diagram import Diagram
2✔
13
from geojson_modelica_translator.model_connectors.load_connectors.load_base import LoadBase
2✔
14
from geojson_modelica_translator.scaffold import Scaffold
2✔
15
from geojson_modelica_translator.utils import mbl_version
2✔
16

17
logger = logging.getLogger(__name__)
2✔
18

19

20
def render_template(template_name, template_params):
2✔
21
    """Helper for rendering a template
22

23
    :param template_name: string, name of template (relative to templates directory)
24
    :param template_params: dict, template parameters
25
    :return: string, templated result
26
    """
27
    template_dir = Path(__file__).parent / "templates"
2✔
28
    template_env = Environment(loader=FileSystemLoader(searchpath=template_dir), undefined=StrictUndefined)
2✔
29
    template_env.filters.update(ALL_CUSTOM_FILTERS)
2✔
30
    template = template_env.get_template(template_name)
2✔
31
    return template.render(template_params)
2✔
32

33

34
class District:
2✔
35
    """
36
    Class for modeling entire district energy systems
37
    """
38

39
    def __init__(self, root_dir, project_name, system_parameters, coupling_graph):
2✔
40
        self._scaffold = Scaffold(root_dir, project_name)
2✔
41
        self.system_parameters = system_parameters
2✔
42
        self._coupling_graph = coupling_graph
2✔
43
        self.district_model_filepath = None
2✔
44
        # Modelica can't handle spaces in project name or path
45
        if (len(str(root_dir).split()) > 1) or (len(str(project_name).split()) > 1):
2!
UNCOV
46
            raise SystemExit(
×
47
                f"\nModelica does not support spaces in project names or paths. "
48
                f"You used '{root_dir}' for run path and {project_name} for model project name. "
49
                "Please update your directory path or model name to not include spaces anywhere."
50
            )
51

52
    def to_modelica(self):
2✔
53
        """Generate modelica files for the models as well as the modelica file for
54
        the entire district system.
55
        """
56
        # scaffold the project
57
        self._scaffold.create()
2✔
58
        self.district_model_filepath = Path(self._scaffold.districts_path.files_dir) / "DistrictEnergySystem.mo"
2✔
59

60
        # create the root package
61
        root_package = PackageParser.new_from_template(
2✔
62
            self._scaffold.project_path,
63
            self._scaffold.project_name,
64
            order=[],
65
            mbl_version=mbl_version(),
66
        )
67
        root_package.save()
2✔
68

69
        # generate model modelica files
70
        for model in self._coupling_graph.models:
2✔
71
            model.to_modelica(self._scaffold)
2✔
72

73
        # construct graph of visual components
74
        diagram = Diagram(self._coupling_graph)
2✔
75

76
        district_template_params = {
2✔
77
            "district_within_path": ".".join([self._scaffold.project_name, "Districts"]),
78
            "diagram": diagram,
79
            "couplings": [],
80
            "models": [],
81
            "is_ghe_district": self.system_parameters.get_param("$.district_system.fifth_generation.ghe_parameters"),
82
        }
83
        common_template_params = {
2✔
84
            "globals": {
85
                "medium_w": "MediumW",
86
                "delChiWatTemBui": "delChiWatTemBui",
87
                "delChiWatTemDis": "delChiWatTemDis",
88
                "delHeaWatTemBui": "delHeaWatTemBui",
89
                "delHeaWatTemDis": "delHeaWatTemDis",
90
            },
91
            "graph": self._coupling_graph,
92
            "sys_params": {
93
                "district_system": self.system_parameters.get_param("$.district_system"),
94
                # num_buildings counts the ports required for 5G systems
95
                "num_buildings": len(self.system_parameters.get_param("$.buildings")),
96
            },
97
        }
98

99
        # render each coupling
100
        load_num = 1
2✔
101
        for coupling in self._coupling_graph.couplings:
2✔
102
            template_context = {
2✔
103
                "diagram": diagram.to_dict(coupling.id, is_coupling=True),
104
            }
105
            template_context.update(**common_template_params)
2✔
106

107
            coupling_load = coupling.get_load()
2✔
108
            if coupling_load is not None:
2✔
109
                # read sys params file for the load
110
                building_sys_params = self.system_parameters.get_param_by_id(coupling_load.building_id, "$")
2✔
111
                template_context["sys_params"]["building"] = building_sys_params
2✔
112
                # Note which load is being used, so ports connect properly in couplings/5G_templates/ConnectStatements
113
                template_context["sys_params"]["load_num"] = load_num
2✔
114
                load_num += 1
2✔
115

116
            templated_result = coupling.render_templates(template_context)
2✔
117
            district_template_params["couplings"].append(
2✔
118
                {
119
                    "id": coupling.id,
120
                    "component_definitions": templated_result["component_definitions"],
121
                    "connect_statements": templated_result["connect_statements"],
122
                    "coupling_definitions_template_path": templated_result["component_definitions_template_path"],
123
                    "connect_statements_template_path": templated_result["connect_statements_template_path"],
124
                }
125
            )
126

127
        # render each model instance
128
        for model in self._coupling_graph.models:
2✔
129
            template_params = {
2✔
130
                "model": model.to_dict(self._scaffold),
131
                "couplings": self._coupling_graph.couplings_by_type(model.id),
132
                "diagram": diagram.to_dict(model.id, is_coupling=False),
133
            }
134
            template_params.update(**common_template_params)
2✔
135

136
            if issubclass(type(model), LoadBase):
2✔
137
                building_sys_params = self.system_parameters.get_param_by_id(model.building_id, "$")
2✔
138
                template_params["sys_params"]["building"] = building_sys_params
2✔
139

140
            templated_instance, instance_template_path = model.render_instance(template_params)
2✔
141
            district_template_params["models"].append(
2✔
142
                {"id": model.id, "instance_template_path": instance_template_path, "instance": templated_instance}
143
            )
144

145
        # render the full district file
146
        if "fifth_generation" in common_template_params["sys_params"]["district_system"]:
2✔
147
            final_result = render_template("DistrictEnergySystem5G.mot", district_template_params)
2✔
148
        elif "fourth_generation" in common_template_params["sys_params"]["district_system"]:
2!
149
            final_result = render_template("DistrictEnergySystem.mot", district_template_params)
2✔
150
        with open(self.district_model_filepath, "w") as f:
2✔
151
            f.write(final_result)
2✔
152

153
        districts_package = PackageParser.new_from_template(
2✔
154
            self._scaffold.districts_path.files_dir,
155
            "Districts",
156
            ["DistrictEnergySystem"],
157
            within=f"{self._scaffold.project_name}",
158
        )
159
        districts_package.save()
2✔
160

161
        root_package = PackageParser(self._scaffold.project_path)
2✔
162
        if "Districts" not in root_package.order:
2!
163
            root_package.add_model("Districts")
2✔
164
            root_package.save()
2✔
165

166
        # If the file is an MOS file and Peak water heating load is set to zero, then set it to a minimum value
167
        data_dir = Path(self._scaffold.project_path) / "Loads" / "Resources" / "Data"
2✔
168
        # Find the MOS data files in the Modelica package.
169
        if data_dir.is_dir():
2✔
170
            for bldg_dir in data_dir.iterdir():
2✔
171
                mo_load_file = data_dir / bldg_dir / "modelica.mos"
2✔
172
                # In case the modelica loads file isn't named modelica.mos:
173
                if not mo_load_file.is_file():
2✔
174
                    modelica_loads = list((data_dir / bldg_dir).rglob("*"))
2✔
175
                    if len(modelica_loads) == 1:
2✔
176
                        mo_load_file = modelica_loads[0]
2✔
177
                if mo_load_file.is_file():
2✔
178
                    mos_file = ModelicaMOS(mo_load_file)
2✔
179
                    # Force peak water heating load to be at least 5000W
180
                    peak_water = mos_file.retrieve_header_variable_value("Peak water heating load", cast_type=float)
2✔
181
                    if peak_water == 0:
2✔
182
                        peak_heat = mos_file.retrieve_header_variable_value("Peak space heating load", cast_type=float)
2✔
183
                        peak_swh = max(peak_heat / 10, 5000)
2✔
184

185
                        mos_file.replace_header_variable_value("Peak water heating load", peak_swh)
2✔
186
                        mos_file.save()
2✔
187
        else:
188
            # The scaffold didn't get built properly or there are no loads in the Modelica package.
189
            logger.warn(
2✔
190
                f"Could not find Modelica data directory {data_dir}. Perhaps there are no loads in the model,"
191
                " and perhaps that is intentional."
192
            )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc