epinterface 1.0.3__py3-none-any.whl → 1.0.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- epinterface/sbem/builder.py +71 -17
- epinterface/sbem/components/schedules.py +20 -0
- {epinterface-1.0.3.dist-info → epinterface-1.0.5.dist-info}/METADATA +2 -1
- {epinterface-1.0.3.dist-info → epinterface-1.0.5.dist-info}/RECORD +7 -7
- {epinterface-1.0.3.dist-info → epinterface-1.0.5.dist-info}/WHEEL +0 -0
- {epinterface-1.0.3.dist-info → epinterface-1.0.5.dist-info}/entry_points.txt +0 -0
- {epinterface-1.0.3.dist-info → epinterface-1.0.5.dist-info}/licenses/LICENSE +0 -0
epinterface/sbem/builder.py
CHANGED
|
@@ -2,18 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import gc
|
|
5
|
+
import logging
|
|
5
6
|
import shutil
|
|
6
7
|
import tempfile
|
|
7
8
|
from collections.abc import Callable
|
|
8
9
|
from dataclasses import dataclass
|
|
9
10
|
from pathlib import Path
|
|
10
|
-
from typing import Literal, cast
|
|
11
|
+
from typing import Literal, cast, get_args
|
|
11
12
|
from uuid import uuid4
|
|
12
13
|
|
|
13
14
|
import numpy as np
|
|
14
15
|
import pandas as pd
|
|
15
16
|
from archetypal.idfclass import IDF
|
|
16
17
|
from archetypal.idfclass.sql import Sql
|
|
18
|
+
from ladybug.epw import EPW
|
|
17
19
|
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
18
20
|
|
|
19
21
|
from epinterface.constants import assumed_constants, physical_constants
|
|
@@ -37,6 +39,8 @@ from epinterface.sbem.components.zones import ZoneComponent
|
|
|
37
39
|
from epinterface.sbem.exceptions import NotImplementedParameter
|
|
38
40
|
from epinterface.weather import BaseWeather
|
|
39
41
|
|
|
42
|
+
logger = logging.getLogger(__name__)
|
|
43
|
+
|
|
40
44
|
DESIRED_METERS = (
|
|
41
45
|
"InteriorEquipment:Electricity",
|
|
42
46
|
"InteriorLights:Electricity",
|
|
@@ -46,11 +50,13 @@ DESIRED_METERS = (
|
|
|
46
50
|
)
|
|
47
51
|
|
|
48
52
|
# TODO: add the meters for HVAC systems
|
|
49
|
-
|
|
53
|
+
AvailableHourlyVariables = Literal[
|
|
50
54
|
"Zone Mean Air Temperature",
|
|
51
55
|
"Zone Air Relative Humidity",
|
|
52
56
|
"Site Outdoor Air Drybulb Temperature",
|
|
53
|
-
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
AVAILABLE_HOURLY_VARIABLES = get_args(AvailableHourlyVariables)
|
|
54
60
|
|
|
55
61
|
|
|
56
62
|
class SimulationPathConfig(BaseModel):
|
|
@@ -803,7 +809,7 @@ class Model(BaseWeather, validate_assignment=True):
|
|
|
803
809
|
|
|
804
810
|
return idf
|
|
805
811
|
|
|
806
|
-
def build(
|
|
812
|
+
def build( # noqa: C901
|
|
807
813
|
self,
|
|
808
814
|
config: SimulationPathConfig,
|
|
809
815
|
post_geometry_callback: Callable[[IDF], IDF] | None = None,
|
|
@@ -846,7 +852,7 @@ class Model(BaseWeather, validate_assignment=True):
|
|
|
846
852
|
"Variable_Name": variable,
|
|
847
853
|
"Reporting_Frequency": "Hourly",
|
|
848
854
|
}
|
|
849
|
-
for variable in
|
|
855
|
+
for variable in AVAILABLE_HOURLY_VARIABLES
|
|
850
856
|
]
|
|
851
857
|
)
|
|
852
858
|
idf = IDF(
|
|
@@ -870,10 +876,6 @@ class Model(BaseWeather, validate_assignment=True):
|
|
|
870
876
|
idf = add_default_sim_controls(idf)
|
|
871
877
|
idf, _scheds = add_default_schedules(idf)
|
|
872
878
|
|
|
873
|
-
idf = SiteGroundTemperature.FromValues(
|
|
874
|
-
assumed_constants.SiteGroundTemperature_degC
|
|
875
|
-
).add(idf)
|
|
876
|
-
|
|
877
879
|
idf = self.geometry.add(idf)
|
|
878
880
|
if post_geometry_callback is not None:
|
|
879
881
|
idf = post_geometry_callback(idf)
|
|
@@ -885,6 +887,44 @@ class Model(BaseWeather, validate_assignment=True):
|
|
|
885
887
|
for zone in added_zone_lists.main_zone_list.Names:
|
|
886
888
|
self.Zone.add_to_idf_zone(idf, zone)
|
|
887
889
|
|
|
890
|
+
# Handle setting ground temperature
|
|
891
|
+
subtractor = (
|
|
892
|
+
4 if (self.geometry.basement and not self.Basement.Conditioned) else 2
|
|
893
|
+
)
|
|
894
|
+
has_heating = self.Zone.Operations.HVAC.ConditioningSystems.Heating is not None
|
|
895
|
+
has_cooling = self.Zone.Operations.HVAC.ConditioningSystems.Cooling is not None
|
|
896
|
+
hsp = self.Zone.Operations.SpaceUse.Thermostat.HeatingSchedule
|
|
897
|
+
csp = self.Zone.Operations.SpaceUse.Thermostat.CoolingSchedule
|
|
898
|
+
epw = EPW(epw_path.as_posix())
|
|
899
|
+
epw_ground_vals_all = epw.monthly_ground_temperature
|
|
900
|
+
if self.geometry.basement:
|
|
901
|
+
# if there is a basement, we use the 2m depth to account for the basement depth.
|
|
902
|
+
epw_ground_vals = epw_ground_vals_all[4].values
|
|
903
|
+
else:
|
|
904
|
+
# if there is no basement, we use the 0.5m depth to account for the ground temperature.
|
|
905
|
+
epw_ground_vals = epw_ground_vals_all[0.5].values
|
|
906
|
+
low_ground_val = min(epw_ground_vals)
|
|
907
|
+
high_ground_val = max(epw_ground_vals)
|
|
908
|
+
phase = (np.array(epw_ground_vals) - low_ground_val) / (
|
|
909
|
+
high_ground_val - low_ground_val
|
|
910
|
+
)
|
|
911
|
+
if has_heating and has_cooling:
|
|
912
|
+
winter_line = np.array(hsp.MonthlyAverageValues) - subtractor
|
|
913
|
+
summer_line = np.array(csp.MonthlyAverageValues) - subtractor
|
|
914
|
+
elif has_heating:
|
|
915
|
+
winter_line = np.array(hsp.MonthlyAverageValues) - subtractor
|
|
916
|
+
summer_line = np.array(hsp.MonthlyAverageValues)
|
|
917
|
+
elif has_cooling:
|
|
918
|
+
winter_line = np.array(csp.MonthlyAverageValues) - subtractor
|
|
919
|
+
summer_line = np.array(csp.MonthlyAverageValues) - subtractor
|
|
920
|
+
else:
|
|
921
|
+
# No heating or cooling, so we use the default ground temperature, should not matter much.
|
|
922
|
+
winter_line = np.array(assumed_constants.SiteGroundTemperature_degC)
|
|
923
|
+
summer_line = np.array(assumed_constants.SiteGroundTemperature_degC)
|
|
924
|
+
interp_temp = phase * np.abs(summer_line - winter_line) + winter_line
|
|
925
|
+
ground_vals = [max(epw_ground_vals[i], interp_temp[i]) for i in range(12)]
|
|
926
|
+
idf = SiteGroundTemperature.FromValues(ground_vals).add(idf)
|
|
927
|
+
|
|
888
928
|
# handle basements
|
|
889
929
|
if self.Basement.UseFraction or self.Basement.Conditioned:
|
|
890
930
|
new_zone_def = self.Zone.model_copy(deep=True)
|
|
@@ -1310,9 +1350,15 @@ if __name__ == "__main__":
|
|
|
1310
1350
|
from epinterface.sbem.prisma.client import PrismaSettings
|
|
1311
1351
|
|
|
1312
1352
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
1313
|
-
database_path = Path("/Users/daryaguettler/globi/data/Brazil/components-lib.db")
|
|
1353
|
+
# database_path = Path("/Users/daryaguettler/globi/data/Brazil/components-lib.db")
|
|
1354
|
+
# component_map_path = Path(
|
|
1355
|
+
# "/Users/daryaguettler/globi/data/Brazil/component-map.yaml"
|
|
1356
|
+
# )
|
|
1357
|
+
database_path = Path(
|
|
1358
|
+
"/Users/daryaguettler/globi/data/Portugal/components-lib.db"
|
|
1359
|
+
)
|
|
1314
1360
|
component_map_path = Path(
|
|
1315
|
-
"/Users/daryaguettler/globi/data/
|
|
1361
|
+
"/Users/daryaguettler/globi/data/Portugal/component-map.yaml"
|
|
1316
1362
|
)
|
|
1317
1363
|
settings = PrismaSettings(
|
|
1318
1364
|
database_path=database_path,
|
|
@@ -1329,18 +1375,26 @@ if __name__ == "__main__":
|
|
|
1329
1375
|
component_map_yaml = yaml.safe_load(f)
|
|
1330
1376
|
selector = SelectorModel.model_validate(component_map_yaml)
|
|
1331
1377
|
db = settings.db
|
|
1378
|
+
# context = {
|
|
1379
|
+
# "region": "SP",
|
|
1380
|
+
# "income": "Low",
|
|
1381
|
+
# "typology": "Residential",
|
|
1382
|
+
# "scenario": "withAC",
|
|
1383
|
+
# }
|
|
1332
1384
|
context = {
|
|
1333
|
-
"
|
|
1334
|
-
"
|
|
1335
|
-
"
|
|
1336
|
-
"
|
|
1385
|
+
"Region": "I1_V2",
|
|
1386
|
+
"City": "LS",
|
|
1387
|
+
"Typology": "Single_Family_Residential",
|
|
1388
|
+
"Age_buckets": "1971_1980",
|
|
1389
|
+
"scenario": "Baseline",
|
|
1337
1390
|
}
|
|
1338
1391
|
with settings.db:
|
|
1339
1392
|
zone = cast(ZoneComponent, selector.get_component(context=context, db=db))
|
|
1340
1393
|
|
|
1341
1394
|
model = Model(
|
|
1342
1395
|
Weather=(
|
|
1343
|
-
"https://climate.onebuilding.org/WMO_Region_3_South_America/BRA_Brazil/SP_Sao_Paulo/BRA_SP_Guaratingueta.AP.837080_TMYx.2009-2023.zip"
|
|
1396
|
+
# "https://climate.onebuilding.org/WMO_Region_3_South_America/BRA_Brazil/SP_Sao_Paulo/BRA_SP_Guaratingueta.AP.837080_TMYx.2009-2023.zip"
|
|
1397
|
+
"https://climate.onebuilding.org/WMO_Region_6_Europe/PRT_Portugal/LB_Lisboa/PRT_LB_Lisboa.Portela.AP.085360_TMYx.2009-2023.zip"
|
|
1344
1398
|
), # pyright: ignore [reportArgumentType]
|
|
1345
1399
|
Zone=zone,
|
|
1346
1400
|
Attic=AtticAssumptions(
|
|
@@ -1358,7 +1412,7 @@ if __name__ == "__main__":
|
|
|
1358
1412
|
d=20,
|
|
1359
1413
|
h=3,
|
|
1360
1414
|
wwr=0.3,
|
|
1361
|
-
num_stories=
|
|
1415
|
+
num_stories=1,
|
|
1362
1416
|
basement=False,
|
|
1363
1417
|
zoning="by_storey",
|
|
1364
1418
|
roof_height=None,
|
|
@@ -70,6 +70,11 @@ class DayComponent(NamedObject, extra="forbid"):
|
|
|
70
70
|
Hour_22: float
|
|
71
71
|
Hour_23: float
|
|
72
72
|
|
|
73
|
+
@property
|
|
74
|
+
def AverageValue(self) -> float:
|
|
75
|
+
"""Get the average value of the day."""
|
|
76
|
+
return sum(self.Values) / len(self.Values)
|
|
77
|
+
|
|
73
78
|
@property
|
|
74
79
|
def bounds(self) -> tuple[float, float]:
|
|
75
80
|
"""Get the bounds of the day."""
|
|
@@ -200,6 +205,11 @@ class WeekComponent(NamedObject, extra="forbid"):
|
|
|
200
205
|
highs = [day.bounds[1] for day in self.Days]
|
|
201
206
|
return min(lows), max(highs)
|
|
202
207
|
|
|
208
|
+
@property
|
|
209
|
+
def AverageValue(self) -> float:
|
|
210
|
+
"""Get the average value of the week."""
|
|
211
|
+
return sum(day.AverageValue for day in self.Days) / len(self.Days)
|
|
212
|
+
|
|
203
213
|
@property
|
|
204
214
|
def Days(self) -> list[DayComponent]:
|
|
205
215
|
"""Get the days of the week as a list."""
|
|
@@ -338,6 +348,16 @@ class YearComponent(NamedObject, extra="forbid"):
|
|
|
338
348
|
November: WeekComponent
|
|
339
349
|
December: WeekComponent
|
|
340
350
|
|
|
351
|
+
@property
|
|
352
|
+
def AverageValue(self) -> float:
|
|
353
|
+
"""Get the average value of the year."""
|
|
354
|
+
return sum(week.AverageValue for week in self.Weeks) / len(self.Weeks)
|
|
355
|
+
|
|
356
|
+
@property
|
|
357
|
+
def MonthlyAverageValues(self) -> list[float]:
|
|
358
|
+
"""Get the average values of the year."""
|
|
359
|
+
return [week.AverageValue for week in self.Weeks]
|
|
360
|
+
|
|
341
361
|
@property
|
|
342
362
|
def bounds(self) -> tuple[float, float]:
|
|
343
363
|
"""Get the bounds of the year."""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: epinterface
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.5
|
|
4
4
|
Summary: This is a repository for dynamically generating energy models within Python, relying on Archetypal and Eppy for most of its functionality.
|
|
5
5
|
Project-URL: Homepage, https://github.com/szvsw/epinterface
|
|
6
6
|
Project-URL: Documentation, https://szvsw.github.io/epinterface
|
|
@@ -13,6 +13,7 @@ Requires-Dist: archetypal
|
|
|
13
13
|
Requires-Dist: click==8.1.7
|
|
14
14
|
Requires-Dist: geopandas~=1.0.1
|
|
15
15
|
Requires-Dist: httpx~=0.27.2
|
|
16
|
+
Requires-Dist: ladybug-core>=0.44.30
|
|
16
17
|
Requires-Dist: openpyxl~=3.1.5
|
|
17
18
|
Requires-Dist: pandas<2.3,>=2.2
|
|
18
19
|
Requires-Dist: prisma~=0.15.0
|
|
@@ -20,7 +20,7 @@ epinterface/data/__init__.py,sha256=r6Uju05rbG3hVVAvCuqJS5wI_zLwK3fU_M8nKMC5DEA,
|
|
|
20
20
|
epinterface/data/res_schedules.parquet,sha256=GjMaqb8uywr7FwR3rAByL72BKGfoZiVuSdAH2sAPFvw,4295
|
|
21
21
|
epinterface/sbem/__init__.py,sha256=aOEtaivujVzFDMFyLrLRx_Xmwv7_Y60MYqObBsn5OR8,48
|
|
22
22
|
epinterface/sbem/annotations.py,sha256=qnN0z7Suri5eHHPJNvXWUorYbEMHGajX2DPezCYcCSQ,1302
|
|
23
|
-
epinterface/sbem/builder.py,sha256=
|
|
23
|
+
epinterface/sbem/builder.py,sha256=hgXHtILjpT921uAaw5Ictv_wChKVYoFsrPljJtDERTg,58198
|
|
24
24
|
epinterface/sbem/common.py,sha256=s7DekSlosfM2mQUZGiIGEbbgiJfF7h6QsTvYbAPP3Ac,1317
|
|
25
25
|
epinterface/sbem/exceptions.py,sha256=4uOXlZgJyvhhRG2cbSua2InxxXHanGSe74h2ioiNEkw,3332
|
|
26
26
|
epinterface/sbem/flat_model.py,sha256=q-G23Rvw3GBDYRm7Oa6PItI8_Xp-FYieLZmLDR4mNyM,84602
|
|
@@ -32,7 +32,7 @@ epinterface/sbem/components/composer.py,sha256=Gs4v8-x8iqfCjaD28TREmqX0pHiRWwyoO
|
|
|
32
32
|
epinterface/sbem/components/envelope.py,sha256=ccmLr2dajgA8s2qBwZxMaj3leu4DETROm6t4N6Q9oSo,12950
|
|
33
33
|
epinterface/sbem/components/materials.py,sha256=MKewPewQEMim9kbLDdgbnW9mXkJh9Juxarf2O-jcW3I,3340
|
|
34
34
|
epinterface/sbem/components/operations.py,sha256=G8GedBR6evNq8ZQ0Ff004Av2Bcz-afCjVmJMC17r6mI,14924
|
|
35
|
-
epinterface/sbem/components/schedules.py,sha256=
|
|
35
|
+
epinterface/sbem/components/schedules.py,sha256=G6QTdM7hngDhtNygKipqVS_CXiXB5jXSk1Kr7D7pMlQ,25130
|
|
36
36
|
epinterface/sbem/components/space_use.py,sha256=CEWXZ47Etbn06zga88ypmlqwhXEIDSF6Wf4HpW8RFJM,10125
|
|
37
37
|
epinterface/sbem/components/systems.py,sha256=02C_HZd38mbE_4i0etxyS-py5y0UiZ9oXkkz_5hmqZ8,8945
|
|
38
38
|
epinterface/sbem/components/zones.py,sha256=ivilWtXkDqWPej4JJho-XAu5aW4NrRqeR7mZDek2yQ0,1050
|
|
@@ -50,8 +50,8 @@ epinterface/sbem/prisma/migrations/20250325185158_add_mutually_exclusive_ventila
|
|
|
50
50
|
epinterface/sbem/prisma/migrations/20250326141941_reduce_naming_complexity_ventilation/migration.sql,sha256=tslwxYOfpbfWCUPqnOadTGk-BzluLcly27MV-mZy2Sc,2289
|
|
51
51
|
epinterface/sbem/prisma/migrations/20250331141910_add_support_for_attic_and_basement_constructions/migration.sql,sha256=fvAzKga8qsMmVM3jLFDAlSbBkWbqRGrHu5Mf2doIEgs,6690
|
|
52
52
|
epinterface/sbem/prisma/migrations/20250919152559_decouple_basement_infiltration/migration.sql,sha256=YrQJxHcU1jrKb6AlBSdfuJKETKkkyxqdKb-X8krBH-8,1876
|
|
53
|
-
epinterface-1.0.
|
|
54
|
-
epinterface-1.0.
|
|
55
|
-
epinterface-1.0.
|
|
56
|
-
epinterface-1.0.
|
|
57
|
-
epinterface-1.0.
|
|
53
|
+
epinterface-1.0.5.dist-info/METADATA,sha256=2t5ecDAFk_97jAnOAvlvFwHDarCGPXZIAmxJnkpObHA,3463
|
|
54
|
+
epinterface-1.0.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
55
|
+
epinterface-1.0.5.dist-info/entry_points.txt,sha256=bjjYRuHWvWV0d-QUesH6prRqHqcppTJANSuAfg3h9j8,78
|
|
56
|
+
epinterface-1.0.5.dist-info/licenses/LICENSE,sha256=hNp6DmbGMuUcwlnpYS8E-ZHYU7kxfmRUP8pLGQaCnu8,1066
|
|
57
|
+
epinterface-1.0.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|