ecoengine 3.2.6__tar.gz → 3.2.7__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.
Files changed (80) hide show
  1. {ecoengine-3.2.6 → ecoengine-3.2.7}/PKG-INFO +1 -1
  2. {ecoengine-3.2.6 → ecoengine-3.2.7}/pyproject.toml +1 -1
  3. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/components/heating/PerformanceMap.py +62 -34
  4. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine.egg-info/PKG-INFO +1 -1
  5. {ecoengine-3.2.6 → ecoengine-3.2.7}/README.md +0 -0
  6. {ecoengine-3.2.6 → ecoengine-3.2.7}/setup.cfg +0 -0
  7. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/__init__.py +0 -0
  8. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/constants/__init__.py +0 -0
  9. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/constants/constants.py +0 -0
  10. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/__init__.py +0 -0
  11. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/climate_data/DryBulbTemperatures_ByClimateZone.csv +0 -0
  12. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/climate_data/InletWaterTemperatures_ByClimateZone.csv +0 -0
  13. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/climate_data/WeatherStation_ClimateZone_Lookup.csv +0 -0
  14. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/climate_data/ZipCode_ClimateZone_Lookup.csv +0 -0
  15. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/climate_data/__init__.py +0 -0
  16. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/climate_data/kGperkWh_ByClimateZone.csv +0 -0
  17. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/load_shapes/__init__.py +0 -0
  18. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/load_shapes/apartment.json +0 -0
  19. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/load_shapes/elementary_school.json +0 -0
  20. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/load_shapes/food_service_a.json +0 -0
  21. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/load_shapes/food_service_b.json +0 -0
  22. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/load_shapes/junior_high.json +0 -0
  23. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/load_shapes/mens_dorm.json +0 -0
  24. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/load_shapes/motel.json +0 -0
  25. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/load_shapes/multi_family.json +0 -0
  26. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/load_shapes/nursing_home.json +0 -0
  27. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/load_shapes/office_building.json +0 -0
  28. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/load_shapes/senior_high.json +0 -0
  29. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/load_shapes/womens_dorm.json +0 -0
  30. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/preformanceMaps/__init__.py +0 -0
  31. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/preformanceMaps/maps.json +0 -0
  32. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/preformanceMaps/pkls/GenericHPWH3_SP_bounds.pkl +0 -0
  33. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/preformanceMaps/pkls/GenericHPWH3_SP_capacity_interpolator.pkl +0 -0
  34. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/preformanceMaps/pkls/GenericHPWH3_SP_power_in_interpolator.pkl +0 -0
  35. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/preformanceMaps/pkls/GenericHPWH4_MP_bounds.pkl +0 -0
  36. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/preformanceMaps/pkls/GenericHPWH4_MP_capacity_interpolator.pkl +0 -0
  37. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/preformanceMaps/pkls/GenericHPWH4_MP_power_in_interpolator.pkl +0 -0
  38. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/data/preformanceMaps/pkls/__init__.py +0 -0
  39. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/interfaces/EcosizerEngine.py +0 -0
  40. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/interfaces/Simulator.py +0 -0
  41. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/interfaces/__init__.py +0 -0
  42. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/__init__.py +0 -0
  43. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/building/Building.py +0 -0
  44. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/building/ClimateZone.py +0 -0
  45. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/building/UtilityCostTracker.py +0 -0
  46. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/building/__init__.py +0 -0
  47. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/components/__init__.py +0 -0
  48. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/components/heating/Controls.py +0 -0
  49. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/components/heating/WaterHeater.py +0 -0
  50. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/components/heating/__init__.py +0 -0
  51. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/components/storage/EnergyTank.py +0 -0
  52. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/components/storage/MixedStorageTank.py +0 -0
  53. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/components/storage/SlugOverlayTank.py +0 -0
  54. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/components/storage/StorageTank.py +0 -0
  55. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/components/storage/StratifiedTank.py +0 -0
  56. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/components/storage/__init__.py +0 -0
  57. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/dhwsystems/DHWSystem.py +0 -0
  58. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/dhwsystems/InstantWHSystem.py +0 -0
  59. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/dhwsystems/MPNoRecircSystem.py +0 -0
  60. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/dhwsystems/__init__.py +0 -0
  61. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/dhwsystems/recirc_systems/ParallelLoopSystem.py +0 -0
  62. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/dhwsystems/recirc_systems/RecircSystem.py +0 -0
  63. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/dhwsystems/recirc_systems/SwingDualFuelSystem.py +0 -0
  64. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/dhwsystems/recirc_systems/SwingERTrdOffSystem.py +0 -0
  65. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/dhwsystems/recirc_systems/SwingSystem.py +0 -0
  66. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/dhwsystems/recirc_systems/__init__.py +0 -0
  67. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/dhwsystems/rtp_systems/MP_RTPInSeriesSystem.py +0 -0
  68. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/dhwsystems/rtp_systems/MultiPassRTPSystem.py +0 -0
  69. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/dhwsystems/rtp_systems/RTPSystem.py +0 -0
  70. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/dhwsystems/rtp_systems/SP_RTPInParallelSystem.py +0 -0
  71. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/dhwsystems/rtp_systems/SP_RTPInSeriesSystem.py +0 -0
  72. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/dhwsystems/rtp_systems/SinglePassRTPSystem.py +0 -0
  73. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/dhwsystems/rtp_systems/__init__.py +0 -0
  74. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/dhwsystems/utils.py +0 -0
  75. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/simulation/SimulationRun.py +0 -0
  76. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine/objects/simulation/__init__.py +0 -0
  77. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine.egg-info/SOURCES.txt +0 -0
  78. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine.egg-info/dependency_links.txt +0 -0
  79. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine.egg-info/requires.txt +0 -0
  80. {ecoengine-3.2.6 → ecoengine-3.2.7}/src/ecoengine.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ecoengine
3
- Version: 3.2.6
3
+ Version: 3.2.7
4
4
  Requires-Python: >=3.11
5
5
  Requires-Dist: numpy<3.0,>=2.4
6
6
  Requires-Dist: scipy<2.0,>=1.17
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ecoengine"
7
- version = "3.2.6"
7
+ version = "3.2.7"
8
8
  requires-python = ">=3.11"
9
9
 
10
10
  # Ranges are bounded on purpose, and the shared ones are kept in sync with
@@ -25,6 +25,13 @@ _TWO_INPUT_PKL_NAMES = frozenset({
25
25
  "MODELS_Droplet_C_SP",
26
26
  })
27
27
 
28
+ # Snap tolerances for PklPerformanceMap._force_closest [°F]. A grid point in the
29
+ # conservative direction (lower OAT, higher inlet, higher outlet) is chosen over
30
+ # the overall nearest point if it is no more than this much further away.
31
+ _SNAP_TOL_OAT_F = 10.0
32
+ _SNAP_TOL_INLET_F = 15.0
33
+ _SNAP_TOL_OUTLET_F = 20.0
34
+
28
35
  # Per-directory cache so that different perf_map_dir values can coexist in one process.
29
36
  _maps_json_cache: dict[str, dict] = {}
30
37
 
@@ -276,8 +283,10 @@ class PklPerformanceMap(PerformanceMap):
276
283
  * OAT ≥ oat_max → use stored default high-OAT capacity/power values.
277
284
  * OAT < oat_min → Electric Resistance fallback: output = input =
278
285
  ``nominal_capacity_kbtuh / num_units`` (COP = 1).
279
- * NaN in range → attempt to snap inlet/outlet to nearest valid grid
280
- point (``_force_closest``); if still NaN, assume COP = 1.5.
286
+ * NaN in range → snap to a nearby valid grid point, preferring the
287
+ conservative direction (lower OAT, higher inlet, higher outlet) within
288
+ ``_SNAP_TOL_*`` of the nearest point (``_force_closest``); if still
289
+ NaN, assume COP = 1.5.
281
290
  """
282
291
 
283
292
  def __init__(
@@ -357,51 +366,68 @@ class PklPerformanceMap(PerformanceMap):
357
366
  float(self._input_interp(arr)[0][0]),
358
367
  )
359
368
 
369
+ @staticmethod
370
+ def _pick_biased(
371
+ values: list[float], target: float, prefer_above: bool, tol: float
372
+ ) -> int:
373
+ """
374
+ Return the index of the grid value to snap ``target`` to.
375
+
376
+ An exact match wins. Otherwise the nearest value in the preferred
377
+ direction (above when ``prefer_above``, else below) is chosen if it is
378
+ no more than ``tol`` further away than the overall nearest value;
379
+ failing that, the overall nearest value is chosen.
380
+ """
381
+ nearest_i = min(range(len(values)), key=lambda i: abs(values[i] - target))
382
+ if values[nearest_i] == target:
383
+ return nearest_i
384
+
385
+ if prefer_above:
386
+ preferred = [i for i, v in enumerate(values) if v > target]
387
+ else:
388
+ preferred = [i for i, v in enumerate(values) if v < target]
389
+ if not preferred:
390
+ return nearest_i
391
+
392
+ pref_i = min(preferred, key=lambda i: abs(values[i] - target))
393
+ if abs(values[pref_i] - target) <= abs(values[nearest_i] - target) + tol:
394
+ return pref_i
395
+ return nearest_i
396
+
360
397
  def _idx_nearest_oat(self, oat_f: float) -> int:
361
- best, idx = abs(self._unique_oats[0] - oat_f), 0
362
- for i in range(1, len(self._unique_oats)):
363
- d = abs(self._unique_oats[i] - oat_f)
364
- if d < best:
365
- best, idx = d, i
366
- return idx
398
+ """Index of the OAT to snap to, preferring lower OATs."""
399
+ return self._pick_biased(
400
+ self._unique_oats, oat_f, prefer_above=False, tol=_SNAP_TOL_OAT_F
401
+ )
367
402
 
368
403
  def _idx_nearest_inlet(self, oat_idx: int, inlet_t: float) -> int:
369
- """
370
- Find the index of the nearest available inlet temp that is >= inlet_t.
371
- Returns -1 (last element) when none qualifies.
372
- """
373
- entries = self._inTs_outTs[oat_idx]
374
- best = abs(entries[-1][0] - inlet_t)
375
- closest = -1
376
- for i, (t_in, _) in enumerate(entries):
377
- d = abs(t_in - inlet_t)
378
- if d < best and t_in >= inlet_t:
379
- best, closest = d, i
380
- return closest
404
+ """Index of the inlet temp (at oat_idx) to snap to, preferring higher inlets."""
405
+ inlets = [t_in for t_in, _ in self._inTs_outTs[oat_idx]]
406
+ return self._pick_biased(
407
+ inlets, inlet_t, prefer_above=True, tol=_SNAP_TOL_INLET_F
408
+ )
381
409
 
382
410
  def _nearest_outlet(self, oat_idx: int, inlet_idx: int, outlet_t: float) -> float:
383
- """Return the outlet temp in the bounds grid nearest to outlet_t."""
411
+ """Outlet temp (at oat_idx, inlet_idx) to snap to, preferring higher outlets."""
384
412
  outlets = self._inTs_outTs[oat_idx][inlet_idx][1]
385
- best = abs(self._unique_oats[0] - outlet_t) # mirrors original init
386
- best_i = 0
387
- for i, o in enumerate(outlets):
388
- d = abs(o - outlet_t)
389
- if d < best:
390
- best, best_i = d, i
391
- return outlets[best_i]
413
+ return outlets[self._pick_biased(
414
+ outlets, outlet_t, prefer_above=True, tol=_SNAP_TOL_OUTLET_F
415
+ )]
392
416
 
393
417
  def _force_closest(
394
418
  self, inlet_t: float, outlet_t: float, oat_f: float
395
419
  ) -> tuple[float, float] | None:
396
420
  """
397
- Attempt to recover a valid interpolation result by snapping inlet and
398
- outlet to the nearest valid grid points. Returns (out_kw, inp_kw) per
399
- unit, or None when the inputs are too far outside the map.
421
+ Attempt to recover a valid interpolation result by snapping OAT, inlet
422
+ and outlet to nearby valid grid points, preferring the conservative
423
+ direction (see ``_pick_biased``). Returns (out_kw, inp_kw) per unit,
424
+ or None when the inputs are too far outside the map.
400
425
  """
401
426
  oat_idx = self._idx_nearest_oat(oat_f)
402
427
  try:
403
428
  inlet_idx = self._idx_nearest_inlet(oat_idx, inlet_t)
404
- except Exception:
429
+ except Exception as e:
430
+ print(e)
405
431
  return None
406
432
 
407
433
  snapped_oat = self._unique_oats[oat_idx]
@@ -416,12 +442,14 @@ class PklPerformanceMap(PerformanceMap):
416
442
  out_kw, inp_kw = self._raw_query(snapped_inlet, snapped_outlet, snapped_oat)
417
443
  if not (math.isnan(out_kw) or math.isnan(inp_kw)):
418
444
  if snapped_outlet < outlet_t and outlet_t > snapped_inlet:
419
- # Proportional capacity adjustment for lower outlet temperature
445
+ # Proportional capacity adjustment for lower outlet temperature,
446
+ # holding the snapped point's COP constant
420
447
  adj = out_kw * (
421
448
  (outlet_t - snapped_inlet) - (snapped_outlet - snapped_inlet)
422
449
  ) / (outlet_t - snapped_inlet)
450
+ if out_kw > 0:
451
+ inp_kw *= (out_kw + adj) / out_kw
423
452
  out_kw += adj
424
- inp_kw += adj
425
453
  return out_kw, inp_kw
426
454
 
427
455
  return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ecoengine
3
- Version: 3.2.6
3
+ Version: 3.2.7
4
4
  Requires-Python: >=3.11
5
5
  Requires-Dist: numpy<3.0,>=2.4
6
6
  Requires-Dist: scipy<2.0,>=1.17
File without changes
File without changes