ecoengine 3.3.1__tar.gz → 3.3.2__tar.gz
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.
- {ecoengine-3.3.1 → ecoengine-3.3.2}/PKG-INFO +1 -1
- {ecoengine-3.3.1 → ecoengine-3.3.2}/pyproject.toml +1 -1
- ecoengine-3.3.2/src/ecoengine/constants/constants.py +18 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/interfaces/EcoBuilder.py +16 -13
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/interfaces/EcosizerEngine.py +72 -4
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/components/heating/PerformanceMap.py +37 -7
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/components/heating/WaterHeater.py +44 -19
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/dhwsystems/DHWSystem.py +2 -6
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/dhwsystems/recirc_systems/ParallelLoopSystem.py +8 -13
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/dhwsystems/recirc_systems/SwingDualFuelSystem.py +9 -16
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/dhwsystems/recirc_systems/SwingERTrdOffSystem.py +2 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/dhwsystems/recirc_systems/SwingSystem.py +11 -20
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/dhwsystems/rtp_systems/MP_RTPInSeriesSystem.py +6 -14
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/dhwsystems/rtp_systems/MultiPassRTPSystem.py +1 -7
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/dhwsystems/rtp_systems/SP_RTPInParallelSystem.py +8 -9
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/dhwsystems/rtp_systems/SP_RTPInSeriesSystem.py +8 -13
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/dhwsystems/rtp_systems/SinglePassRTPSystem.py +2 -6
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/simulation/SimulationRun.py +27 -13
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine.egg-info/PKG-INFO +1 -1
- ecoengine-3.3.1/src/ecoengine/constants/constants.py +0 -5
- {ecoengine-3.3.1 → ecoengine-3.3.2}/README.md +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/setup.cfg +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/__init__.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/constants/__init__.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/__init__.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/climate_data/DryBulbTemperatures_ByClimateZone.csv +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/climate_data/InletWaterTemperatures_ByClimateZone.csv +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/climate_data/WeatherStation_ClimateZone_Lookup.csv +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/climate_data/ZipCode_ClimateZone_Lookup.csv +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/climate_data/__init__.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/climate_data/kGperkWh_ByClimateZone.csv +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/load_shapes/__init__.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/load_shapes/apartment.json +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/load_shapes/elementary_school.json +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/load_shapes/food_service_a.json +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/load_shapes/food_service_b.json +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/load_shapes/junior_high.json +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/load_shapes/mens_dorm.json +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/load_shapes/motel.json +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/load_shapes/multi_family.json +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/load_shapes/nursing_home.json +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/load_shapes/office_building.json +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/load_shapes/senior_high.json +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/load_shapes/womens_dorm.json +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/preformanceMaps/__init__.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/preformanceMaps/maps.json +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/preformanceMaps/pkls/GenericHPWH3_SP_bounds.pkl +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/preformanceMaps/pkls/GenericHPWH3_SP_capacity_interpolator.pkl +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/preformanceMaps/pkls/GenericHPWH3_SP_power_in_interpolator.pkl +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/preformanceMaps/pkls/GenericHPWH4_MP_bounds.pkl +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/preformanceMaps/pkls/GenericHPWH4_MP_capacity_interpolator.pkl +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/preformanceMaps/pkls/GenericHPWH4_MP_power_in_interpolator.pkl +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/data/preformanceMaps/pkls/__init__.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/interfaces/Simulator.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/interfaces/__init__.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/interfaces/system_assembly_utils.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/__init__.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/building/Building.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/building/ClimateZone.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/building/UtilityCostTracker.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/building/__init__.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/components/__init__.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/components/heating/Controls.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/components/heating/__init__.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/components/storage/EnergyTank.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/components/storage/MixedStorageTank.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/components/storage/SlugOverlayTank.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/components/storage/StorageTank.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/components/storage/StratifiedTank.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/components/storage/__init__.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/dhwsystems/InstantWHSystem.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/dhwsystems/MPNoRecircSystem.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/dhwsystems/__init__.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/dhwsystems/recirc_systems/RecircSystem.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/dhwsystems/recirc_systems/__init__.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/dhwsystems/rtp_systems/RTPSystem.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/dhwsystems/rtp_systems/__init__.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/dhwsystems/utils.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/simulation/__init__.py +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine.egg-info/SOURCES.txt +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine.egg-info/dependency_links.txt +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine.egg-info/requires.txt +0 -0
- {ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Volumetric heat capacity of water [BTU / (gallon · °F)]
|
|
2
|
+
_RHO_CP: float = 8.353535
|
|
3
|
+
|
|
4
|
+
# Unit conversion: 1 W = 3.412142 BTU/hr (equivalently, 1 kW = 3.412142 kBTU/hr)
|
|
5
|
+
_BTUH_PER_W: float = 3.412142
|
|
6
|
+
|
|
7
|
+
# Default Nominal COPs
|
|
8
|
+
_ELECTRIC_RESISTANCE_COP = 0.95
|
|
9
|
+
_GAS_COP = 0.95 # ER equivellant
|
|
10
|
+
_HEAT_PUMP_COP = 2.5
|
|
11
|
+
|
|
12
|
+
# Nominal COP by water heater heating type (used by NominalPerformanceMap)
|
|
13
|
+
_NOMINAL_COP_BY_HEATING_TYPE: dict[str, float] = {
|
|
14
|
+
"electric_resistance": _ELECTRIC_RESISTANCE_COP,
|
|
15
|
+
"gas": _GAS_COP,
|
|
16
|
+
"heat_pump": _HEAT_PUMP_COP,
|
|
17
|
+
}
|
|
18
|
+
_HEATING_TYPES: tuple[str, ...] = tuple(_NOMINAL_COP_BY_HEATING_TYPE)
|
|
@@ -26,6 +26,7 @@ from __future__ import annotations
|
|
|
26
26
|
|
|
27
27
|
import warnings
|
|
28
28
|
|
|
29
|
+
from ecoengine.constants.constants import _HEATING_TYPES
|
|
29
30
|
from ecoengine.objects.building.Building import Building
|
|
30
31
|
from ecoengine.objects.components.heating.Controls import AlwaysOffControls, Controls
|
|
31
32
|
from ecoengine.objects.components.heating.PerformanceMap import (
|
|
@@ -46,7 +47,6 @@ from .system_assembly_utils import (
|
|
|
46
47
|
)
|
|
47
48
|
|
|
48
49
|
_SYSTEM_SLOTS = ("primary", "tm", "in_series")
|
|
49
|
-
_FUEL_TYPES = ("electric", "gas")
|
|
50
50
|
_MP_SCHEMATICS = frozenset({"multi_pass_rtp", "mprtp_in_series"})
|
|
51
51
|
|
|
52
52
|
# assemble_dhw_system argument name → EcoBuilder field name (for error messages)
|
|
@@ -76,7 +76,7 @@ class EcoBuilder:
|
|
|
76
76
|
water_heaters : list[WaterHeater]
|
|
77
77
|
Heaters heating the primary storage tank. For ``sprtp_in_parallel``
|
|
78
78
|
this list also holds the single gas backup heater
|
|
79
|
-
(``
|
|
79
|
+
(``heating_type='gas'``).
|
|
80
80
|
tm_water_heaters : list[WaterHeater]
|
|
81
81
|
Heaters for the temperature-maintenance tank, if applicable.
|
|
82
82
|
in_series_water_heaters : list[WaterHeater]
|
|
@@ -217,7 +217,8 @@ class EcoBuilder:
|
|
|
217
217
|
minimum OAT.
|
|
218
218
|
nominal_capacity_kbtuh : float, optional
|
|
219
219
|
Without ``model_name``: the constant total capacity [kBTU/hr] of a
|
|
220
|
-
``NominalPerformanceMap``.
|
|
220
|
+
``NominalPerformanceMap``. Its power input uses the nominal COP
|
|
221
|
+
of the heater's ``heating_type``, set in ``build_water_heater()``.
|
|
221
222
|
num_units : int
|
|
222
223
|
Number of identical units (model maps only). Default 1.
|
|
223
224
|
design_inlet_temp_f : float
|
|
@@ -337,7 +338,7 @@ class EcoBuilder:
|
|
|
337
338
|
performance_map: PerformanceMap,
|
|
338
339
|
control_schedule: list[str],
|
|
339
340
|
control_map: dict[str, Controls],
|
|
340
|
-
|
|
341
|
+
heating_type: str = "heat_pump",
|
|
341
342
|
system: str | None = "primary",
|
|
342
343
|
) -> WaterHeater:
|
|
343
344
|
"""
|
|
@@ -351,8 +352,10 @@ class EcoBuilder:
|
|
|
351
352
|
24-element schedule, e.g. from ``build_control_schedule()``.
|
|
352
353
|
control_map : dict[str, Controls]
|
|
353
354
|
Controls for every mode in ``control_schedule``.
|
|
354
|
-
|
|
355
|
-
``'
|
|
355
|
+
heating_type : str
|
|
356
|
+
``'heat_pump'`` (default), ``'electric_resistance'`` or ``'gas'``.
|
|
357
|
+
With a nominal performance map this sets the nominal COP used
|
|
358
|
+
for power input.
|
|
356
359
|
system : str | None
|
|
357
360
|
``'primary'`` → ``water_heaters``, ``'tm'`` → ``tm_water_heaters``,
|
|
358
361
|
``'in_series'`` → ``in_series_water_heaters``, None → not added
|
|
@@ -361,15 +364,15 @@ class EcoBuilder:
|
|
|
361
364
|
Raises
|
|
362
365
|
------
|
|
363
366
|
ValueError
|
|
364
|
-
On an unknown ``system`` or ``
|
|
367
|
+
On an unknown ``system`` or ``heating_type``, a schedule that is not
|
|
365
368
|
24 entries long, or schedule modes missing from ``control_map``.
|
|
366
369
|
TypeError
|
|
367
370
|
If ``performance_map`` is not a PerformanceMap.
|
|
368
371
|
"""
|
|
369
372
|
if system is not None and system not in _SYSTEM_SLOTS:
|
|
370
373
|
raise ValueError(f"system must be one of {_SYSTEM_SLOTS} or None, got {system!r}.")
|
|
371
|
-
if
|
|
372
|
-
raise ValueError(f"
|
|
374
|
+
if heating_type not in _HEATING_TYPES:
|
|
375
|
+
raise ValueError(f"heating_type must be one of {_HEATING_TYPES}, got {heating_type!r}.")
|
|
373
376
|
if not isinstance(performance_map, PerformanceMap):
|
|
374
377
|
raise TypeError(
|
|
375
378
|
"performance_map must be a PerformanceMap; use build_performance_map()."
|
|
@@ -386,7 +389,7 @@ class EcoBuilder:
|
|
|
386
389
|
performance_map = performance_map,
|
|
387
390
|
control_schedule = list(control_schedule),
|
|
388
391
|
control_map = dict(control_map),
|
|
389
|
-
|
|
392
|
+
heating_type = heating_type,
|
|
390
393
|
)
|
|
391
394
|
if system is not None:
|
|
392
395
|
getattr(self, "water_heaters" if system == "primary" else f"{system}_water_heaters").append(heater)
|
|
@@ -435,7 +438,7 @@ class EcoBuilder:
|
|
|
435
438
|
Schematic notes
|
|
436
439
|
---------------
|
|
437
440
|
* ``sprtp_in_parallel``: exactly one primary heater must have
|
|
438
|
-
``
|
|
441
|
+
``heating_type='gas'``; it becomes the gas backup heater.
|
|
439
442
|
* ``parallel_loop``: the TM tank's cycling temperatures are taken
|
|
440
443
|
from the TM heater's hour-0 controls (``on_trigger_t_f`` /
|
|
441
444
|
``off_trigger_t_f``).
|
|
@@ -456,11 +459,11 @@ class EcoBuilder:
|
|
|
456
459
|
primary_heaters = list(self.water_heaters)
|
|
457
460
|
gas_heater = None
|
|
458
461
|
if name == "sprtp_in_parallel":
|
|
459
|
-
gas_heaters = [wh for wh in primary_heaters if wh.
|
|
462
|
+
gas_heaters = [wh for wh in primary_heaters if wh.heating_type == "gas"]
|
|
460
463
|
if len(gas_heaters) != 1:
|
|
461
464
|
raise ValueError(
|
|
462
465
|
"sprtp_in_parallel requires exactly one primary heater with "
|
|
463
|
-
f"
|
|
466
|
+
f"heating_type='gas'; found {len(gas_heaters)}."
|
|
464
467
|
)
|
|
465
468
|
gas_heater = gas_heaters[0]
|
|
466
469
|
primary_heaters = [wh for wh in primary_heaters if wh is not gas_heater]
|
|
@@ -1265,8 +1265,19 @@ class EcosizerEngine:
|
|
|
1265
1265
|
f"Schematic '{self.schematic}' requires gas_storage_vol_gal in pre-sized mode."
|
|
1266
1266
|
)
|
|
1267
1267
|
|
|
1268
|
-
def _build_tm_water_heater(
|
|
1269
|
-
|
|
1268
|
+
def _build_tm_water_heater(
|
|
1269
|
+
self,
|
|
1270
|
+
tm_controls,
|
|
1271
|
+
force_electric_resistance: bool = False,
|
|
1272
|
+
nominal_heating_type: str = "electric_resistance",
|
|
1273
|
+
):
|
|
1274
|
+
"""
|
|
1275
|
+
Build the TM WaterHeater (single unit) from tm_model or tm_capacity_kbtuh.
|
|
1276
|
+
|
|
1277
|
+
``nominal_heating_type`` sets the heating type of a nominal-capacity TM
|
|
1278
|
+
heater: ``'electric_resistance'`` for TM elements, ``'gas'`` for the
|
|
1279
|
+
``swing_dual_fuel`` gas swing tank.
|
|
1280
|
+
"""
|
|
1270
1281
|
from ecoengine.objects.components.heating.WaterHeater import WaterHeater
|
|
1271
1282
|
if force_electric_resistance:
|
|
1272
1283
|
self.num_tm_heaters = 1
|
|
@@ -1288,6 +1299,7 @@ class EcosizerEngine:
|
|
|
1288
1299
|
nominal_capacity_kbtuh=per_unit_kbtuh,
|
|
1289
1300
|
control_schedule=["normal"] * 24,
|
|
1290
1301
|
control_map={"normal": tm_controls},
|
|
1302
|
+
heating_type=nominal_heating_type,
|
|
1291
1303
|
)
|
|
1292
1304
|
|
|
1293
1305
|
def _build_presized_dhw_system(self):
|
|
@@ -1369,9 +1381,12 @@ class EcosizerEngine:
|
|
|
1369
1381
|
tm_controls = default_tm_controls(
|
|
1370
1382
|
schematic, self.supply_temp_f, self.tm_on_temp_f, self.tm_off_temp_f
|
|
1371
1383
|
)
|
|
1372
|
-
# Swing variants always use
|
|
1384
|
+
# Swing variants always use a nominal TM element: electric
|
|
1385
|
+
# resistance for swing_tank, gas for the swing_dual_fuel swing tank.
|
|
1373
1386
|
tm_water_heater = self._build_tm_water_heater(
|
|
1374
|
-
tm_controls,
|
|
1387
|
+
tm_controls,
|
|
1388
|
+
force_electric_resistance=(schematic != "parallel_loop"),
|
|
1389
|
+
nominal_heating_type="gas" if schematic == "swing_dual_fuel" else "electric_resistance",
|
|
1375
1390
|
)
|
|
1376
1391
|
|
|
1377
1392
|
gas_water_heater = None
|
|
@@ -1385,6 +1400,7 @@ class EcosizerEngine:
|
|
|
1385
1400
|
nominal_capacity_kbtuh=self.gas_capacity_kbtuh,
|
|
1386
1401
|
control_schedule=["normal"] * 24,
|
|
1387
1402
|
control_map={"normal": gas_controls},
|
|
1403
|
+
heating_type="gas",
|
|
1388
1404
|
)
|
|
1389
1405
|
|
|
1390
1406
|
system = assemble_dhw_system(
|
|
@@ -1536,6 +1552,58 @@ class EcosizerEngine:
|
|
|
1536
1552
|
"""
|
|
1537
1553
|
return {"monthly_energy_kwh": simulation_run.get_monthly_energy_kwh()}
|
|
1538
1554
|
|
|
1555
|
+
def get_parameters(self) -> dict:
|
|
1556
|
+
"""
|
|
1557
|
+
Return every public engine field and its current value.
|
|
1558
|
+
|
|
1559
|
+
Private fields (``_building``, ``_dhw_system``) are left out; use
|
|
1560
|
+
``print_parameters()`` to also see the built system's heaters and tanks.
|
|
1561
|
+
|
|
1562
|
+
Returns
|
|
1563
|
+
-------
|
|
1564
|
+
dict
|
|
1565
|
+
``{field_name: value}`` in the order the fields were set.
|
|
1566
|
+
"""
|
|
1567
|
+
return {k: v for k, v in vars(self).items() if not k.startswith("_")}
|
|
1568
|
+
|
|
1569
|
+
def print_parameters(self, include_system: bool = True) -> None:
|
|
1570
|
+
"""
|
|
1571
|
+
Print every public engine field and its current value, for debugging.
|
|
1572
|
+
|
|
1573
|
+
Parameters
|
|
1574
|
+
----------
|
|
1575
|
+
include_system : bool
|
|
1576
|
+
Also print the built DHWSystem: class, storage tanks, and for each
|
|
1577
|
+
heater its role, heating type, performance map class, model name
|
|
1578
|
+
and nominal capacity. Default True.
|
|
1579
|
+
"""
|
|
1580
|
+
params = self.get_parameters()
|
|
1581
|
+
width = max(len(k) for k in params)
|
|
1582
|
+
print("EcosizerEngine parameters")
|
|
1583
|
+
for k, v in params.items():
|
|
1584
|
+
print(f" {k:<{width}} = {v!r}")
|
|
1585
|
+
|
|
1586
|
+
if not include_system or self._dhw_system is None:
|
|
1587
|
+
return
|
|
1588
|
+
system = self._dhw_system
|
|
1589
|
+
print(f"Built system: {type(system).__name__}")
|
|
1590
|
+
for attr in ("storage_tank", "tm_storage_tank", "gas_storage_tank"):
|
|
1591
|
+
tank = getattr(system, attr, None)
|
|
1592
|
+
if tank is not None:
|
|
1593
|
+
print(f" {attr}: {type(tank).__name__}, "
|
|
1594
|
+
f"{getattr(tank, 'total_volume_gal', None)} gal")
|
|
1595
|
+
heaters = [("primary", wh) for wh in getattr(system, "water_heaters", None) or []]
|
|
1596
|
+
for role, attr in (("tm", "tm_water_heater"), ("gas", "gas_water_heater")):
|
|
1597
|
+
wh = getattr(system, attr, None)
|
|
1598
|
+
if wh is not None:
|
|
1599
|
+
heaters.append((role, wh))
|
|
1600
|
+
for role, wh in heaters:
|
|
1601
|
+
pm = wh.performance_map
|
|
1602
|
+
print(f" {role} heater: heating_type={wh.heating_type!r}, "
|
|
1603
|
+
f"map={type(pm).__name__}, model={getattr(pm, 'model_name', '') or None!r}, "
|
|
1604
|
+
f"nominal_capacity_kbtuh={getattr(pm, 'nominal_capacity_kbtuh', None)!r}, "
|
|
1605
|
+
f"num_units={getattr(pm, 'num_units', None)!r}")
|
|
1606
|
+
|
|
1539
1607
|
def get_hw_magnitude(self) -> float:
|
|
1540
1608
|
"""
|
|
1541
1609
|
Return the total daily DHW usage for the building [gallons at supply temperature].
|
{ecoengine-3.3.1 → ecoengine-3.3.2}/src/ecoengine/objects/components/heating/PerformanceMap.py
RENAMED
|
@@ -4,8 +4,9 @@ import json
|
|
|
4
4
|
import math
|
|
5
5
|
import os
|
|
6
6
|
import pickle
|
|
7
|
+
from typing import Literal
|
|
7
8
|
|
|
8
|
-
from ecoengine.constants.constants import _BTUH_PER_W
|
|
9
|
+
from ecoengine.constants.constants import _BTUH_PER_W, _NOMINAL_COP_BY_HEATING_TYPE
|
|
9
10
|
|
|
10
11
|
# Bundled data directory — used as a fallback when perf_map_dir is not supplied.
|
|
11
12
|
# Will be removed once the data is fully externalised.
|
|
@@ -60,9 +61,9 @@ class PerformanceMap:
|
|
|
60
61
|
- ``PklPerformanceMap`` — pickle-based LinearND interpolator (lab data)
|
|
61
62
|
- ``HPWHsimPerformanceMap``— coefficient-based polynomial (HPWHsim model)
|
|
62
63
|
|
|
63
|
-
* ``NominalPerformanceMap(nominal_capacity_kbtuh)``
|
|
64
|
-
Constant-output placeholder for use during preliminary sizing.
|
|
65
|
-
|
|
64
|
+
* ``NominalPerformanceMap(nominal_capacity_kbtuh, heating_type)``
|
|
65
|
+
Constant-output placeholder for use during preliminary sizing. Power
|
|
66
|
+
input uses a fixed nominal COP for the heating type.
|
|
66
67
|
|
|
67
68
|
API notes
|
|
68
69
|
---------
|
|
@@ -236,7 +237,7 @@ class PerformanceMap:
|
|
|
236
237
|
|
|
237
238
|
|
|
238
239
|
# ---------------------------------------------------------------------------
|
|
239
|
-
# NominalPerformanceMap — constant-output placeholder
|
|
240
|
+
# NominalPerformanceMap — constant-output placeholder
|
|
240
241
|
# ---------------------------------------------------------------------------
|
|
241
242
|
|
|
242
243
|
class NominalPerformanceMap(PerformanceMap):
|
|
@@ -247,10 +248,16 @@ class NominalPerformanceMap(PerformanceMap):
|
|
|
247
248
|
Every capacity query returns the fixed nominal_capacity_kbtuh regardless of
|
|
248
249
|
outdoor air temperature or water temperature — analogous to how
|
|
249
250
|
ClimateZone.from_design_conditions() returns a constant OAT for all timesteps.
|
|
250
|
-
Power
|
|
251
|
+
Power input is the nominal capacity divided by a fixed COP chosen by
|
|
252
|
+
``heating_type`` (see ``_NOMINAL_COP_BY_HEATING_TYPE`` in constants.py).
|
|
251
253
|
"""
|
|
252
254
|
|
|
253
|
-
def __init__(
|
|
255
|
+
def __init__(
|
|
256
|
+
self,
|
|
257
|
+
nominal_capacity_kbtuh: float,
|
|
258
|
+
model_name: str = "",
|
|
259
|
+
heating_type: Literal["electric_resistance", "gas", "heat_pump"] = "heat_pump",
|
|
260
|
+
) -> None:
|
|
254
261
|
"""
|
|
255
262
|
Parameters
|
|
256
263
|
----------
|
|
@@ -258,14 +265,37 @@ class NominalPerformanceMap(PerformanceMap):
|
|
|
258
265
|
Fixed heating output capacity [kBTU/hr] returned for all conditions.
|
|
259
266
|
model_name : str
|
|
260
267
|
Optional human-readable identifier.
|
|
268
|
+
heating_type : "electric_resistance" | "gas" | "heat_pump"
|
|
269
|
+
Selects the nominal COP used by ``get_power_in_kw()``.
|
|
270
|
+
Default "heat_pump".
|
|
271
|
+
|
|
272
|
+
Raises
|
|
273
|
+
------
|
|
274
|
+
ValueError
|
|
275
|
+
If ``heating_type`` is not a known heating type.
|
|
261
276
|
"""
|
|
277
|
+
if heating_type not in _NOMINAL_COP_BY_HEATING_TYPE:
|
|
278
|
+
raise ValueError(
|
|
279
|
+
f"heating_type must be one of {tuple(_NOMINAL_COP_BY_HEATING_TYPE)}, "
|
|
280
|
+
f"got {heating_type!r}."
|
|
281
|
+
)
|
|
262
282
|
super().__init__(map_data=None, model_name=model_name)
|
|
263
283
|
self.nominal_capacity_kbtuh = nominal_capacity_kbtuh
|
|
284
|
+
self.heating_type = heating_type
|
|
285
|
+
self.cop = _NOMINAL_COP_BY_HEATING_TYPE[heating_type]
|
|
264
286
|
|
|
265
287
|
def get_capacity_kbtuh(self, oat_f: float, outlet_temp_f: float, inlet_temp_f: float | None = None) -> float:
|
|
266
288
|
"""Return the fixed nominal capacity [kBTU/hr] regardless of conditions."""
|
|
267
289
|
return self.nominal_capacity_kbtuh
|
|
268
290
|
|
|
291
|
+
def get_power_in_kw(self, oat_f: float, outlet_temp_f: float, inlet_temp_f: float | None = None) -> float:
|
|
292
|
+
"""Return power input [kW]: nominal capacity / nominal COP for the heating type."""
|
|
293
|
+
return self.nominal_capacity_kbtuh / _BTUH_PER_W / self.cop
|
|
294
|
+
|
|
295
|
+
def is_within_operating_bounds(self, oat_f: float) -> bool:
|
|
296
|
+
"""A nominal map has no operating limits."""
|
|
297
|
+
return True
|
|
298
|
+
|
|
269
299
|
|
|
270
300
|
# ---------------------------------------------------------------------------
|
|
271
301
|
# PklPerformanceMap — pickle-based LinearND interpolator
|
|
@@ -2,8 +2,11 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING, Literal
|
|
4
4
|
|
|
5
|
+
from ecoengine.constants.constants import _HEATING_TYPES
|
|
5
6
|
from ecoengine.objects.components.heating.PerformanceMap import NominalPerformanceMap, PerformanceMap
|
|
6
7
|
|
|
8
|
+
HeatingType = Literal["electric_resistance", "gas", "heat_pump"]
|
|
9
|
+
|
|
7
10
|
if TYPE_CHECKING:
|
|
8
11
|
from ecoengine.objects.components.heating.Controls import Controls
|
|
9
12
|
from ecoengine.objects.components.storage.StorageTank import StorageTank
|
|
@@ -49,7 +52,7 @@ class WaterHeater:
|
|
|
49
52
|
performance_map: PerformanceMap | None,
|
|
50
53
|
control_schedule: list[str] | None,
|
|
51
54
|
control_map: dict[str, Controls] | None,
|
|
52
|
-
|
|
55
|
+
heating_type: HeatingType = "heat_pump",
|
|
53
56
|
) -> None:
|
|
54
57
|
"""
|
|
55
58
|
Parameters
|
|
@@ -63,13 +66,29 @@ class WaterHeater:
|
|
|
63
66
|
control_map : dict[str, Controls] | None
|
|
64
67
|
Maps schedule strings to Controls objects.
|
|
65
68
|
None when no control logic is configured.
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
heating_type : "electric_resistance" | "gas" | "heat_pump"
|
|
70
|
+
Heating technology. Default is "heat_pump". When
|
|
71
|
+
``performance_map`` is a ``NominalPerformanceMap``, the heater
|
|
72
|
+
stores a copy of it with this heating type, so its power uses the
|
|
73
|
+
matching nominal COP. The passed map is not modified.
|
|
74
|
+
|
|
75
|
+
Raises
|
|
76
|
+
------
|
|
77
|
+
ValueError
|
|
78
|
+
If ``heating_type`` is not a known heating type.
|
|
68
79
|
"""
|
|
80
|
+
if heating_type not in _HEATING_TYPES:
|
|
81
|
+
raise ValueError(f"heating_type must be one of {_HEATING_TYPES}, got {heating_type!r}.")
|
|
82
|
+
if isinstance(performance_map, NominalPerformanceMap) and performance_map.heating_type != heating_type:
|
|
83
|
+
performance_map = NominalPerformanceMap(
|
|
84
|
+
performance_map.nominal_capacity_kbtuh,
|
|
85
|
+
model_name=performance_map.model_name,
|
|
86
|
+
heating_type=heating_type,
|
|
87
|
+
)
|
|
69
88
|
self.performance_map = performance_map
|
|
70
89
|
self.control_schedule = control_schedule
|
|
71
90
|
self.control_map = control_map
|
|
72
|
-
self.
|
|
91
|
+
self.heating_type = heating_type
|
|
73
92
|
self._active = False
|
|
74
93
|
|
|
75
94
|
# ------------------------------------------------------------------
|
|
@@ -82,7 +101,7 @@ class WaterHeater:
|
|
|
82
101
|
nominal_capacity_kbtuh: float,
|
|
83
102
|
control_schedule: list[str] | None,
|
|
84
103
|
control_map: dict[str, Controls] | None,
|
|
85
|
-
|
|
104
|
+
heating_type: HeatingType = "heat_pump",
|
|
86
105
|
) -> WaterHeater:
|
|
87
106
|
"""
|
|
88
107
|
Create a WaterHeater with a constant-capacity placeholder performance map.
|
|
@@ -99,18 +118,19 @@ class WaterHeater:
|
|
|
99
118
|
24-element list mapping each hour to a key in control_map.
|
|
100
119
|
control_map : dict[str, Controls] | None
|
|
101
120
|
Maps schedule strings to Controls objects.
|
|
102
|
-
|
|
103
|
-
|
|
121
|
+
heating_type : "electric_resistance" | "gas" | "heat_pump"
|
|
122
|
+
Heating technology; selects the nominal COP used for power input.
|
|
123
|
+
Default is "heat_pump".
|
|
104
124
|
|
|
105
125
|
Returns
|
|
106
126
|
-------
|
|
107
127
|
WaterHeater
|
|
108
128
|
"""
|
|
109
129
|
return cls(
|
|
110
|
-
performance_map=NominalPerformanceMap(nominal_capacity_kbtuh),
|
|
130
|
+
performance_map=NominalPerformanceMap(nominal_capacity_kbtuh, heating_type=heating_type),
|
|
111
131
|
control_schedule=control_schedule,
|
|
112
132
|
control_map=control_map,
|
|
113
|
-
|
|
133
|
+
heating_type=heating_type,
|
|
114
134
|
)
|
|
115
135
|
|
|
116
136
|
@classmethod
|
|
@@ -122,7 +142,7 @@ class WaterHeater:
|
|
|
122
142
|
num_units: int = 1,
|
|
123
143
|
design_inlet_temp_f: float = 50.0,
|
|
124
144
|
nominal_capacity_kbtuh: float | None = None,
|
|
125
|
-
|
|
145
|
+
heating_type: HeatingType = "heat_pump",
|
|
126
146
|
perf_map_dir: str | None = None,
|
|
127
147
|
) -> WaterHeater:
|
|
128
148
|
"""
|
|
@@ -144,8 +164,9 @@ class WaterHeater:
|
|
|
144
164
|
Cold-water inlet temperature used when not supplied per-call. Default 50 °F.
|
|
145
165
|
nominal_capacity_kbtuh : float | None
|
|
146
166
|
Total system capacity for ER fallback sizing. Default None.
|
|
147
|
-
|
|
148
|
-
|
|
167
|
+
heating_type : "electric_resistance" | "gas" | "heat_pump"
|
|
168
|
+
Heating technology label. Default is "heat_pump". Does not change
|
|
169
|
+
the model map, which has its own power data.
|
|
149
170
|
perf_map_dir : str | None
|
|
150
171
|
Path to directory containing ``maps.json`` and ``pkls/``.
|
|
151
172
|
Passed through to ``PerformanceMap.from_model_name()``.
|
|
@@ -164,7 +185,7 @@ class WaterHeater:
|
|
|
164
185
|
),
|
|
165
186
|
control_schedule=control_schedule,
|
|
166
187
|
control_map=control_map,
|
|
167
|
-
|
|
188
|
+
heating_type=heating_type,
|
|
168
189
|
)
|
|
169
190
|
|
|
170
191
|
# ------------------------------------------------------------------
|
|
@@ -242,9 +263,10 @@ class WaterHeater:
|
|
|
242
263
|
oat_f: float,
|
|
243
264
|
outlet_temp_f: float,
|
|
244
265
|
inlet_temp_f: float | None = None,
|
|
245
|
-
) -> float
|
|
266
|
+
) -> float:
|
|
246
267
|
"""
|
|
247
|
-
Return
|
|
268
|
+
Return actual power input this timestep [kW]: the performance map's
|
|
269
|
+
power if active, 0 if not.
|
|
248
270
|
|
|
249
271
|
Parameters
|
|
250
272
|
----------
|
|
@@ -256,11 +278,14 @@ class WaterHeater:
|
|
|
256
278
|
|
|
257
279
|
Returns
|
|
258
280
|
-------
|
|
259
|
-
float
|
|
281
|
+
float
|
|
282
|
+
0.0 when the heater is inactive, has no performance map, or the
|
|
283
|
+
map has no power data.
|
|
260
284
|
"""
|
|
261
|
-
if self.performance_map is
|
|
262
|
-
return
|
|
263
|
-
|
|
285
|
+
if not self._active or self.performance_map is None:
|
|
286
|
+
return 0.0
|
|
287
|
+
kw = self.performance_map.get_power_in_kw(oat_f, outlet_temp_f, inlet_temp_f)
|
|
288
|
+
return kw if kw is not None else 0.0
|
|
264
289
|
|
|
265
290
|
def get_output_kbtuh(
|
|
266
291
|
self,
|
|
@@ -1639,14 +1639,10 @@ class DHWSystem:
|
|
|
1639
1639
|
wh.get_output_kbtuh(oat_f, wh.get_outlet_temp_f(hour_of_day), inlet_temp_f)
|
|
1640
1640
|
for wh in self.water_heaters
|
|
1641
1641
|
)
|
|
1642
|
-
total_kw
|
|
1643
|
-
active_kws = [
|
|
1642
|
+
total_kw = sum(
|
|
1644
1643
|
wh.get_power_in_kw(oat_f, wh.get_outlet_temp_f(hour_of_day), inlet_temp_f)
|
|
1645
1644
|
for wh in self.water_heaters
|
|
1646
|
-
|
|
1647
|
-
]
|
|
1648
|
-
if any(kw is not None for kw in active_kws):
|
|
1649
|
-
total_kw = sum(kw or 0.0 for kw in active_kws)
|
|
1645
|
+
)
|
|
1650
1646
|
|
|
1651
1647
|
self.storage_tank.heat(total_kbtuh, interval_min, outlet_temp_f)
|
|
1652
1648
|
top_temp_f = self.storage_tank.get_temperature_at_fraction(1.0)
|
|
@@ -228,6 +228,7 @@ class ParallelLoopSystem(RecircSystem):
|
|
|
228
228
|
nominal_capacity_kbtuh=system._minimum_tm_capacity_kbtuh / system.num_tm_heaters,
|
|
229
229
|
control_schedule=["normal"] * 24,
|
|
230
230
|
control_map={"normal": tm_controls},
|
|
231
|
+
heating_type="electric_resistance",
|
|
231
232
|
)
|
|
232
233
|
|
|
233
234
|
return system
|
|
@@ -373,7 +374,9 @@ class ParallelLoopSystem(RecircSystem):
|
|
|
373
374
|
-------
|
|
374
375
|
dict
|
|
375
376
|
Same keys as DHWSystem.simulate_step(). heater_output_kbtuh and
|
|
376
|
-
heater_power_in_kw
|
|
377
|
+
heater_power_in_kw are primary-only; TM heater output and power are
|
|
378
|
+
in tm_heater_output_kbtuh / tm_heater_input_kw. SimulationRun
|
|
379
|
+
combines the power series.
|
|
377
380
|
"""
|
|
378
381
|
# Run primary system: super() → RecircSystem → DHWSystem
|
|
379
382
|
step = super().simulate_step(building, timestep_interval, interval_min, mode)
|
|
@@ -393,22 +396,14 @@ class ParallelLoopSystem(RecircSystem):
|
|
|
393
396
|
tm_top_temp_f = self.tm_storage_tank.get_temperature_at_fraction(1.0)
|
|
394
397
|
tm_inlet_temp_f = (self.tm_off_temp_f + self.tm_on_temp_f) / 2.0
|
|
395
398
|
tm_kbtuh = self.tm_water_heater.get_output_kbtuh(oat_f, tm_top_temp_f, tm_inlet_temp_f) * self.num_tm_heaters
|
|
396
|
-
|
|
397
|
-
self.tm_water_heater.get_power_in_kw(oat_f, tm_top_temp_f, tm_inlet_temp_f)
|
|
398
|
-
if self.tm_water_heater.is_active()
|
|
399
|
-
else None
|
|
400
|
-
)
|
|
401
|
-
tm_kw = tm_kw_per_unit * self.num_tm_heaters if tm_kw_per_unit is not None else None
|
|
399
|
+
tm_kw = self.tm_water_heater.get_power_in_kw(oat_f, tm_top_temp_f, tm_inlet_temp_f) * self.num_tm_heaters
|
|
402
400
|
self.tm_storage_tank.heat(tm_kbtuh, interval_min, self.tm_off_temp_f)
|
|
403
401
|
|
|
404
402
|
# ------------------------------------------------------------------
|
|
405
|
-
#
|
|
403
|
+
# Add TM outputs to the step dict
|
|
406
404
|
# ------------------------------------------------------------------
|
|
407
|
-
# heater_output_kbtuh
|
|
408
|
-
#
|
|
409
|
-
# heater_power_in_kw merges both so get_total_energy_kwh() is accurate.
|
|
410
|
-
# if tm_kw is not None:
|
|
411
|
-
# step["heater_power_in_kw"] = (step["heater_power_in_kw"] or 0.0) + tm_kw
|
|
405
|
+
# heater_output_kbtuh and heater_power_in_kw stay PRIMARY-ONLY. TM output
|
|
406
|
+
# and power are recorded separately; SimulationRun combines the power.
|
|
412
407
|
|
|
413
408
|
# TM panel data (consumed by SimulationRun for the TM subplot)
|
|
414
409
|
step["tm_tank_temp_f"] = self.tm_storage_tank.get_temperature_at_fraction(0.5)
|
|
@@ -7,7 +7,7 @@ from ecoengine.objects.components.heating.WaterHeater import WaterHeater
|
|
|
7
7
|
from ecoengine.objects.components.storage.MixedStorageTank import MixedStorageTank
|
|
8
8
|
from ecoengine.objects.components.storage.StratifiedTank import StratifiedTank
|
|
9
9
|
from ecoengine.objects.dhwsystems.DHWSystem import DHWSystem
|
|
10
|
-
from ecoengine.constants.constants import _RHO_CP
|
|
10
|
+
from ecoengine.constants.constants import _RHO_CP
|
|
11
11
|
from ecoengine.objects.dhwsystems.utils import (
|
|
12
12
|
mixing_valve_behavior,
|
|
13
13
|
mixing_valve_behavior_swing,
|
|
@@ -322,6 +322,7 @@ class SwingDualFuelSystem(SwingSystem):
|
|
|
322
322
|
nominal_capacity_kbtuh=self._minimum_tm_capacity_kbtuh,
|
|
323
323
|
control_schedule=["normal"] * 24,
|
|
324
324
|
control_map={"normal": tm_controls},
|
|
325
|
+
heating_type="gas",
|
|
325
326
|
)
|
|
326
327
|
|
|
327
328
|
# ------------------------------------------------------------------
|
|
@@ -426,13 +427,9 @@ class SwingDualFuelSystem(SwingSystem):
|
|
|
426
427
|
wh.get_output_kbtuh(oat_f, wh.get_outlet_temp_f(hour_of_day), inlet_temp_f)
|
|
427
428
|
for wh in self.water_heaters
|
|
428
429
|
)
|
|
429
|
-
|
|
430
|
+
primary_kw = sum(
|
|
430
431
|
wh.get_power_in_kw(oat_f, wh.get_outlet_temp_f(hour_of_day), inlet_temp_f)
|
|
431
432
|
for wh in self.water_heaters
|
|
432
|
-
if wh.is_active()
|
|
433
|
-
]
|
|
434
|
-
primary_kw: float | None = (
|
|
435
|
-
sum(kw or 0.0 for kw in primary_kw_list) if primary_kw_list else None
|
|
436
433
|
)
|
|
437
434
|
self.storage_tank.heat(primary_kbtuh, interval_min, outlet_temp_f)
|
|
438
435
|
|
|
@@ -467,11 +464,7 @@ class SwingDualFuelSystem(SwingSystem):
|
|
|
467
464
|
tm_ctrl = self.tm_water_heater.get_controls_for_hour(hour_of_day)
|
|
468
465
|
tm_outlet_f = tm_ctrl.off_trigger_t_f
|
|
469
466
|
tm_kbtuh = self.tm_water_heater.get_output_kbtuh(oat_f, tm_outlet_f)
|
|
470
|
-
tm_kw_val = (
|
|
471
|
-
self.tm_water_heater.get_power_in_kw(oat_f, tm_outlet_f)
|
|
472
|
-
if self.tm_water_heater.is_active()
|
|
473
|
-
else None
|
|
474
|
-
)
|
|
467
|
+
tm_kw_val = self.tm_water_heater.get_power_in_kw(oat_f, tm_outlet_f)
|
|
475
468
|
self.tm_storage_tank.heat(tm_kbtuh, interval_min, tm_outlet_f)
|
|
476
469
|
|
|
477
470
|
# --- 6. Draw computed volume from primary storage ---
|
|
@@ -490,15 +483,15 @@ class SwingDualFuelSystem(SwingSystem):
|
|
|
490
483
|
for f in (0.0, 0.2, 0.4, 0.6, 0.8, 1.0)
|
|
491
484
|
]
|
|
492
485
|
|
|
493
|
-
# heater_output_kbtuh
|
|
494
|
-
#
|
|
495
|
-
|
|
486
|
+
# heater_output_kbtuh and heater_power_in_kw are PRIMARY-ONLY. Gas swing
|
|
487
|
+
# heater power is recorded separately in tm_heater_input_kw;
|
|
488
|
+
# SimulationRun combines them.
|
|
496
489
|
swing_mid_temp_f = self.tm_storage_tank.get_temperature_at_fraction(0.5)
|
|
497
490
|
return {
|
|
498
491
|
"demand_supplyT_gal": demand_supplyT_gal,
|
|
499
492
|
"usable_volume_supplyT_gal": usable_vol_gal,
|
|
500
493
|
"heater_output_kbtuh": primary_kbtuh,
|
|
501
|
-
"heater_power_in_kw":
|
|
494
|
+
"heater_power_in_kw": primary_kw,
|
|
502
495
|
"oat_f": oat_f,
|
|
503
496
|
"inlet_water_temp_f": inlet_temp_f,
|
|
504
497
|
"tank_temps_f": tank_temps_f,
|
|
@@ -509,5 +502,5 @@ class SwingDualFuelSystem(SwingSystem):
|
|
|
509
502
|
# TM panel data (consumed by SimulationRun for the swing-tank subplot)
|
|
510
503
|
"tm_tank_temp_f": swing_mid_temp_f,
|
|
511
504
|
"tm_heater_output_kbtuh": tm_kbtuh,
|
|
512
|
-
"tm_heater_input_kw":
|
|
505
|
+
"tm_heater_input_kw": tm_kw_val,
|
|
513
506
|
}
|
|
@@ -199,6 +199,7 @@ class SwingERTrdOffSystem(SwingSystem):
|
|
|
199
199
|
nominal_capacity_kbtuh=system._minimum_tm_capacity_kbtuh,
|
|
200
200
|
control_schedule=["normal"] * 24,
|
|
201
201
|
control_map={"normal": tm_controls},
|
|
202
|
+
heating_type="electric_resistance",
|
|
202
203
|
)
|
|
203
204
|
|
|
204
205
|
return system
|
|
@@ -266,6 +267,7 @@ class SwingERTrdOffSystem(SwingSystem):
|
|
|
266
267
|
nominal_capacity_kbtuh=system._minimum_tm_capacity_kbtuh,
|
|
267
268
|
control_schedule=["normal"] * 24,
|
|
268
269
|
control_map={"normal": tm_controls},
|
|
270
|
+
heating_type="electric_resistance",
|
|
269
271
|
)
|
|
270
272
|
|
|
271
273
|
return system
|