ecoengine 3.3.0__tar.gz → 3.3.1__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 (82) hide show
  1. {ecoengine-3.3.0 → ecoengine-3.3.1}/PKG-INFO +1 -1
  2. {ecoengine-3.3.0 → ecoengine-3.3.1}/pyproject.toml +1 -1
  3. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/components/heating/WaterHeater.py +20 -9
  4. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/dhwsystems/DHWSystem.py +1 -1
  5. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/dhwsystems/recirc_systems/ParallelLoopSystem.py +1 -1
  6. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/dhwsystems/recirc_systems/SwingDualFuelSystem.py +3 -3
  7. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/dhwsystems/recirc_systems/SwingSystem.py +2 -2
  8. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/dhwsystems/rtp_systems/MP_RTPInSeriesSystem.py +2 -2
  9. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/dhwsystems/rtp_systems/MultiPassRTPSystem.py +1 -1
  10. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/dhwsystems/rtp_systems/SP_RTPInParallelSystem.py +2 -2
  11. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/dhwsystems/rtp_systems/SP_RTPInSeriesSystem.py +2 -2
  12. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/dhwsystems/rtp_systems/SinglePassRTPSystem.py +1 -1
  13. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine.egg-info/PKG-INFO +1 -1
  14. {ecoengine-3.3.0 → ecoengine-3.3.1}/README.md +0 -0
  15. {ecoengine-3.3.0 → ecoengine-3.3.1}/setup.cfg +0 -0
  16. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/__init__.py +0 -0
  17. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/constants/__init__.py +0 -0
  18. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/constants/constants.py +0 -0
  19. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/__init__.py +0 -0
  20. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/climate_data/DryBulbTemperatures_ByClimateZone.csv +0 -0
  21. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/climate_data/InletWaterTemperatures_ByClimateZone.csv +0 -0
  22. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/climate_data/WeatherStation_ClimateZone_Lookup.csv +0 -0
  23. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/climate_data/ZipCode_ClimateZone_Lookup.csv +0 -0
  24. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/climate_data/__init__.py +0 -0
  25. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/climate_data/kGperkWh_ByClimateZone.csv +0 -0
  26. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/load_shapes/__init__.py +0 -0
  27. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/load_shapes/apartment.json +0 -0
  28. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/load_shapes/elementary_school.json +0 -0
  29. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/load_shapes/food_service_a.json +0 -0
  30. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/load_shapes/food_service_b.json +0 -0
  31. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/load_shapes/junior_high.json +0 -0
  32. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/load_shapes/mens_dorm.json +0 -0
  33. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/load_shapes/motel.json +0 -0
  34. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/load_shapes/multi_family.json +0 -0
  35. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/load_shapes/nursing_home.json +0 -0
  36. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/load_shapes/office_building.json +0 -0
  37. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/load_shapes/senior_high.json +0 -0
  38. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/load_shapes/womens_dorm.json +0 -0
  39. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/preformanceMaps/__init__.py +0 -0
  40. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/preformanceMaps/maps.json +0 -0
  41. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/preformanceMaps/pkls/GenericHPWH3_SP_bounds.pkl +0 -0
  42. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/preformanceMaps/pkls/GenericHPWH3_SP_capacity_interpolator.pkl +0 -0
  43. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/preformanceMaps/pkls/GenericHPWH3_SP_power_in_interpolator.pkl +0 -0
  44. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/preformanceMaps/pkls/GenericHPWH4_MP_bounds.pkl +0 -0
  45. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/preformanceMaps/pkls/GenericHPWH4_MP_capacity_interpolator.pkl +0 -0
  46. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/preformanceMaps/pkls/GenericHPWH4_MP_power_in_interpolator.pkl +0 -0
  47. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/data/preformanceMaps/pkls/__init__.py +0 -0
  48. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/interfaces/EcoBuilder.py +0 -0
  49. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/interfaces/EcosizerEngine.py +0 -0
  50. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/interfaces/Simulator.py +0 -0
  51. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/interfaces/__init__.py +0 -0
  52. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/interfaces/system_assembly_utils.py +0 -0
  53. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/__init__.py +0 -0
  54. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/building/Building.py +0 -0
  55. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/building/ClimateZone.py +0 -0
  56. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/building/UtilityCostTracker.py +0 -0
  57. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/building/__init__.py +0 -0
  58. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/components/__init__.py +0 -0
  59. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/components/heating/Controls.py +0 -0
  60. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/components/heating/PerformanceMap.py +0 -0
  61. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/components/heating/__init__.py +0 -0
  62. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/components/storage/EnergyTank.py +0 -0
  63. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/components/storage/MixedStorageTank.py +0 -0
  64. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/components/storage/SlugOverlayTank.py +0 -0
  65. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/components/storage/StorageTank.py +0 -0
  66. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/components/storage/StratifiedTank.py +0 -0
  67. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/components/storage/__init__.py +0 -0
  68. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/dhwsystems/InstantWHSystem.py +0 -0
  69. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/dhwsystems/MPNoRecircSystem.py +0 -0
  70. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/dhwsystems/__init__.py +0 -0
  71. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/dhwsystems/recirc_systems/RecircSystem.py +0 -0
  72. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/dhwsystems/recirc_systems/SwingERTrdOffSystem.py +0 -0
  73. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/dhwsystems/recirc_systems/__init__.py +0 -0
  74. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/dhwsystems/rtp_systems/RTPSystem.py +0 -0
  75. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/dhwsystems/rtp_systems/__init__.py +0 -0
  76. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/dhwsystems/utils.py +0 -0
  77. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/simulation/SimulationRun.py +0 -0
  78. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine/objects/simulation/__init__.py +0 -0
  79. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine.egg-info/SOURCES.txt +0 -0
  80. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine.egg-info/dependency_links.txt +0 -0
  81. {ecoengine-3.3.0 → ecoengine-3.3.1}/src/ecoengine.egg-info/requires.txt +0 -0
  82. {ecoengine-3.3.0 → ecoengine-3.3.1}/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.3.0
3
+ Version: 3.3.1
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.3.0"
7
+ version = "3.3.1"
8
8
  requires-python = ">=3.11"
9
9
 
10
10
  # Ranges are bounded on purpose, and the shared ones are kept in sync with
@@ -307,33 +307,44 @@ class WaterHeater:
307
307
  raise ValueError(f"No Controls configured for hour {hour_of_day}.")
308
308
  return controls.outlet_temp_f
309
309
 
310
- def update_state(self, storage_tank: StorageTank, hour_of_day: int) -> None:
310
+ def update_state(
311
+ self,
312
+ storage_tank: StorageTank,
313
+ timestep_interval: int,
314
+ interval_min: int = 1,
315
+ ) -> None:
311
316
  """
312
- Look up the active Controls for the given hour, then update
317
+ Look up the active Controls for this timestep's hour, then update
313
318
  active/inactive state based on current tank condition.
314
319
 
320
+ On the first interval after a mode change in the control schedule,
321
+ the heater is turned off before the control check.
315
322
  If the heater is currently ON, check should_turn_off().
316
323
  If the heater is currently OFF, check should_turn_on().
317
324
 
318
325
  Parameters
319
326
  ----------
320
327
  storage_tank : StorageTank
321
- hour_of_day : int
322
- Hour of the day (0-23), used to select the active Controls from
323
- the control schedule.
328
+ timestep_interval : int
329
+ Number of elapsed intervals since the start of the simulation.
330
+ interval_min : int
331
+ Length of each interval [minutes]. Default 1.
324
332
 
325
333
  Raises
326
334
  ------
327
335
  ValueError
328
- If no Controls are configured for the given hour.
336
+ If no Controls are configured for this timestep's hour.
329
337
  """
338
+ hour_of_day = (timestep_interval * interval_min // 60) % 24
330
339
  controls = self.get_controls_for_hour(hour_of_day)
331
340
  if controls is None:
332
341
  raise ValueError(f"No Controls configured for hour {hour_of_day}.")
333
-
342
+
334
343
  # QPL Alignment block : QPL states Upon change of load shift operation, disable all heaters if “on sensor” is warmer than “on temperature”.
335
- # To do this in EcosizerEngine, we simply turn off water heaters at the beginning of a mode change
336
- previous_hour = 23 if hour_of_day == 0 else hour_of_day - 1
344
+ # To do this in EcosizerEngine, we turn off water heaters on the first interval of a mode change.
345
+ # previous_hour is the hour of the previous timestep, so it differs from hour_of_day
346
+ # only on the first interval of an hour (timestep 0 → hour 23).
347
+ previous_hour = ((timestep_interval - 1) * interval_min // 60) % 24
337
348
  if self.control_schedule[hour_of_day] != self.control_schedule[previous_hour]:
338
349
  self.turn_off()
339
350
 
@@ -1632,7 +1632,7 @@ class DHWSystem:
1632
1632
  if self.storage_tank is not None:
1633
1633
  # Update on/off state for each heater based on current tank condition
1634
1634
  for wh in self.water_heaters:
1635
- wh.update_state(self.storage_tank, hour_of_day)
1635
+ wh.update_state(self.storage_tank, timestep_interval, interval_min)
1636
1636
 
1637
1637
  # Apply heating from all active heaters to the tank
1638
1638
  total_kbtuh = sum(
@@ -388,7 +388,7 @@ class ParallelLoopSystem(RecircSystem):
388
388
  self.return_flow_gpm, self.return_temp_f, interval_min
389
389
  )
390
390
 
391
- self.tm_water_heater.update_state(self.tm_storage_tank, hour_of_day)
391
+ self.tm_water_heater.update_state(self.tm_storage_tank, timestep_interval, interval_min)
392
392
 
393
393
  tm_top_temp_f = self.tm_storage_tank.get_temperature_at_fraction(1.0)
394
394
  tm_inlet_temp_f = (self.tm_off_temp_f + self.tm_on_temp_f) / 2.0
@@ -84,7 +84,7 @@ class _SwingDualFuelPrimary(DHWSystem):
84
84
  outlet_temp_f = self._get_outlet_temp_f(hour_of_day)
85
85
 
86
86
  for wh in self.water_heaters:
87
- wh.update_state(self.storage_tank, hour_of_day)
87
+ wh.update_state(self.storage_tank, timestep_interval, interval_min)
88
88
 
89
89
  total_kbtuh = sum(
90
90
  wh.get_output_kbtuh(oat_f, wh.get_outlet_temp_f(hour_of_day), inlet_temp_f)
@@ -420,7 +420,7 @@ class SwingDualFuelSystem(SwingSystem):
420
420
 
421
421
  # --- 2. Primary heater: update state and heat primary tank ---
422
422
  for wh in self.water_heaters:
423
- wh.update_state(self.storage_tank, hour_of_day)
423
+ wh.update_state(self.storage_tank, timestep_interval, interval_min)
424
424
 
425
425
  primary_kbtuh = sum(
426
426
  wh.get_output_kbtuh(oat_f, wh.get_outlet_temp_f(hour_of_day), inlet_temp_f)
@@ -463,7 +463,7 @@ class SwingDualFuelSystem(SwingSystem):
463
463
  self.tm_storage_tank.mix_primary_inflow(result["storage_draw_gal"], result["inlet_temp_f"])
464
464
 
465
465
  # --- 5. TM element: update state and heat swing tank ---
466
- self.tm_water_heater.update_state(self.tm_storage_tank, hour_of_day)
466
+ self.tm_water_heater.update_state(self.tm_storage_tank, timestep_interval, interval_min)
467
467
  tm_ctrl = self.tm_water_heater.get_controls_for_hour(hour_of_day)
468
468
  tm_outlet_f = tm_ctrl.off_trigger_t_f
469
469
  tm_kbtuh = self.tm_water_heater.get_output_kbtuh(oat_f, tm_outlet_f)
@@ -953,7 +953,7 @@ class SwingSystem(RecircSystem):
953
953
  )
954
954
  # --- 2. Primary heater: update state and heat primary tank ---
955
955
  for wh in self.water_heaters:
956
- wh.update_state(self.storage_tank, hour_of_day)
956
+ wh.update_state(self.storage_tank, timestep_interval, interval_min)
957
957
 
958
958
  top_temp_f = self.storage_tank.get_temperature_at_fraction(1.0)
959
959
  primary_kbtuh = sum(
@@ -992,7 +992,7 @@ class SwingSystem(RecircSystem):
992
992
  self.get_recirc_loss_kbtuh(), interval_min
993
993
  )
994
994
  # --- 7. TM element: update state and heat swing tank ---
995
- self.tm_water_heater.update_state(self.tm_storage_tank, hour_of_day)
995
+ self.tm_water_heater.update_state(self.tm_storage_tank, timestep_interval, interval_min)
996
996
  tm_ctrl = self.tm_water_heater.get_controls_for_hour(hour_of_day)
997
997
  tm_outlet_f = tm_ctrl.off_trigger_t_f
998
998
  tm_kbtuh = self.tm_water_heater.get_output_kbtuh(oat_f, tm_outlet_f)
@@ -347,7 +347,7 @@ class MP_RTPInSeriesSystem(MultiPassRTPSystem):
347
347
  # the fully-mixed slug zone, so get_temperature_at_fraction returns
348
348
  # slug_temp_f. The heater turns off when slug_temp_f >= off_trigger_t_f.
349
349
  for wh in self.water_heaters:
350
- wh.update_state(tank, hour_of_day)
350
+ wh.update_state(tank, timestep_interval, interval_min)
351
351
 
352
352
  is_heating = any(wh.is_active() for wh in self.water_heaters)
353
353
 
@@ -401,7 +401,7 @@ class MP_RTPInSeriesSystem(MultiPassRTPSystem):
401
401
  self.gas_storage_tank.mix_primary_inflow(draw_gal, primary_draw_temp_f)
402
402
 
403
403
  # Gas water heater heating
404
- self.gas_water_heater.update_state(self.gas_storage_tank, hour_of_day)
404
+ self.gas_water_heater.update_state(self.gas_storage_tank, timestep_interval, interval_min)
405
405
  gas_ctrl = self.gas_water_heater.get_controls_for_hour(hour_of_day)
406
406
  gas_outlet_f = gas_ctrl.outlet_temp_f
407
407
  gas_kbtuh = self.gas_water_heater.get_output_kbtuh(oat_f, gas_outlet_f)
@@ -590,7 +590,7 @@ class MultiPassRTPSystem(RTPSystem):
590
590
  # the fully-mixed slug zone, so get_temperature_at_fraction returns
591
591
  # slug_temp_f. The heater turns off when slug_temp_f >= off_trigger_t_f.
592
592
  for wh in self.water_heaters:
593
- wh.update_state(tank, hour_of_day)
593
+ wh.update_state(tank, timestep_interval, interval_min)
594
594
 
595
595
  is_heating = any(wh.is_active() for wh in self.water_heaters)
596
596
 
@@ -432,8 +432,8 @@ class SP_RTPInParallelSystem(SinglePassRTPSystem):
432
432
  self.storage_tank.update_cold_temp_f(inlet_temp_f)
433
433
  # Update on/off state for each heater based on current tank condition
434
434
  for wh in self.water_heaters:
435
- wh.update_state(self.storage_tank, hour_of_day)
436
- self.gas_water_heater.update_state(self.storage_tank, hour_of_day)
435
+ wh.update_state(self.storage_tank, timestep_interval, interval_min)
436
+ self.gas_water_heater.update_state(self.storage_tank, timestep_interval, interval_min)
437
437
  # Apply heating from all active heaters to the tank
438
438
  total_kbtuh = sum(
439
439
  wh.get_output_kbtuh(oat_f, wh.get_outlet_temp_f(hour_of_day), inlet_temp_f)
@@ -354,7 +354,7 @@ class SP_RTPInSeriesSystem(SinglePassRTPSystem):
354
354
  self.storage_tank.update_cold_temp_f(inlet_temp_f)
355
355
  # Update on/off state for each heater based on current tank condition
356
356
  for wh in self.water_heaters:
357
- wh.update_state(self.storage_tank, hour_of_day)
357
+ wh.update_state(self.storage_tank, timestep_interval, interval_min)
358
358
 
359
359
  # Apply heating from all active heaters to the tank
360
360
  total_kbtuh = sum(
@@ -391,7 +391,7 @@ class SP_RTPInSeriesSystem(SinglePassRTPSystem):
391
391
 
392
392
  # Gas water heater heating
393
393
  self.gas_storage_tank.mix_primary_inflow(draw_gal, primary_draw_temp_f)
394
- self.gas_water_heater.update_state(self.gas_storage_tank, hour_of_day)
394
+ self.gas_water_heater.update_state(self.gas_storage_tank, timestep_interval, interval_min)
395
395
  gas_ctrl = self.gas_water_heater.get_controls_for_hour(hour_of_day)
396
396
  gas_outlet_f = gas_ctrl.outlet_temp_f if gas_ctrl is not None else self.supply_temp_f + _GAS_DEADBAND_F
397
397
  gas_kbtuh = self.gas_water_heater.get_output_kbtuh(oat_f, gas_outlet_f)
@@ -376,7 +376,7 @@ class SinglePassRTPSystem(RTPSystem):
376
376
  self.storage_tank.update_cold_temp_f(inlet_temp_f)
377
377
  # Update on/off state for each heater based on current tank condition
378
378
  for wh in self.water_heaters:
379
- wh.update_state(self.storage_tank, hour_of_day)
379
+ wh.update_state(self.storage_tank, timestep_interval, interval_min)
380
380
 
381
381
  # Apply heating from all active heaters to the tank
382
382
  total_kbtuh = sum(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ecoengine
3
- Version: 3.3.0
3
+ Version: 3.3.1
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