OpenDHW 0.2.7__tar.gz → 0.2.9__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.
@@ -624,8 +624,8 @@
624
624
  0.169014085
625
625
  ],
626
626
  [
627
- 3,
628
- 0.105633803
627
+ 4,
628
+ 0.14084507
629
629
  ],
630
630
  [
631
631
  2,
@@ -162,7 +162,7 @@ def import_from_dhwcalc(s_step, daylight_saving, categories,occupancy,
162
162
  return timeseries_df
163
163
 
164
164
 
165
- def generate_dhw_profile(s_step, categories, mean_drawoff_vol_per_day, occupancy, holidays, building_type, weekend_weekday_factor, initial_day=0):
165
+ def generate_dhw_profile(s_step, categories, mean_drawoff_vol_per_day, occupancy, holidays, building_type, weekend_weekday_factor, initial_day=0, occupancy_profile=None):
166
166
  """
167
167
  Generates a DHW profile. The generation is split up in different
168
168
  functions and generally follows the methodology described in the DHWcalc
@@ -178,7 +178,12 @@ def generate_dhw_profile(s_step, categories, mean_drawoff_vol_per_day, occupancy
178
178
  :param categories: int: 1 or 4 (see DHWcalc)
179
179
  :param weekend_weekday_factor: int: taken from DHWcalc
180
180
  :param mean_drawoff_vol_per_day: int: daily water demand in Liters
181
+ :param occupancy: float: occupancy of the building, between 0 and 1, multiplies the water demand
182
+ :param holidays: list: list of holidays (day of year, 1-365)
183
+ :param building_type: string: type of building, determines which days are off days
181
184
  :param initial_day: int: 0:Mon - 1:Tues ... 6:Sun
185
+ :param occupancy_profile: list: optional profile to adjust the probabilities, has to fit in length to p_final (s_step)
186
+
182
187
  :return: timeseries_df df: dataframe with all timeseries
183
188
  """
184
189
 
@@ -199,7 +204,8 @@ def generate_dhw_profile(s_step, categories, mean_drawoff_vol_per_day, occupancy
199
204
  weekend_weekday_factor=weekend_weekday_factor,
200
205
  holidays = holidays,
201
206
  initial_day=initial_day,
202
- building_type=building_type
207
+ building_type=building_type,
208
+ occupancy_profile=occupancy_profile
203
209
  )
204
210
 
205
211
  # --- empty drawoffs list, will be filled afterwards
@@ -379,7 +385,7 @@ def generate_daily_probability_step_function(mode, s_step,building_type, save_fi
379
385
 
380
386
 
381
387
  def generate_yearly_probability_profile(s_step, weekend_weekday_factor,building_type, holidays,
382
- initial_day=0):
388
+ initial_day=0, occupancy_profile=None):
383
389
  """
384
390
  generate a summed yearly probability profile. The whole function is
385
391
  deterministic. The same inputs always produce the same outputs.
@@ -394,6 +400,10 @@ def generate_yearly_probability_profile(s_step, weekend_weekday_factor,building_
394
400
  :param s_step: int: seconds in a timestep
395
401
  :param weekend_weekday_factor: float: shift probabilities towards weekend
396
402
  :param initial_day: int: Mon: 0 ... Sun: 6
403
+ :param holidays: list: list of holidays (day of year, 1-365)
404
+ :param building_type: string: type of building, determines which days are off days
405
+ :param occupancy_profile: list: optional profile to adjust the probabilities, has to fit in length to p_final
406
+
397
407
  :return: timeseries_df: df: df that holds the yearly profile
398
408
  """
399
409
 
@@ -424,7 +434,8 @@ def generate_yearly_probability_profile(s_step, weekend_weekday_factor,building_
424
434
  p_work_day=p_wd_weighted,
425
435
  s_step=s_step,
426
436
  holidays=holidays,
427
- building_type=building_type
437
+ building_type=building_type,
438
+ occupancy_profile=occupancy_profile
428
439
  )
429
440
 
430
441
  # sum and normalize to range between 0 and 1.
@@ -460,7 +471,7 @@ def shift_weekend_weekday(p_work_day, p_off_day, factor):
460
471
 
461
472
  assert p_wd_factor * 5 / 7 + p_we_factor * 2 / 7 == 1
462
473
 
463
- p_wd_weighted = [p * p_we_factor for p in p_work_day]
474
+ p_wd_weighted = [p * p_wd_factor for p in p_work_day]
464
475
  p_we_weighted = [p * p_we_factor for p in p_off_day]
465
476
 
466
477
  av_p_wd_weighted = statistics.mean(p_wd_weighted)
@@ -472,18 +483,20 @@ def shift_weekend_weekday(p_work_day, p_off_day, factor):
472
483
 
473
484
 
474
485
  def generate_yearly_probabilities(initial_day, p_off_day, p_work_day,
475
- s_step, holidays,building_type, plot_p_yearly=False):
486
+ s_step, holidays,building_type, plot_p_yearly=False, occupancy_profile=None):
476
487
  """
477
488
  Takes the probabilities of a working days and a off days and generates a
478
489
  list of yearly probabilities by adding a seasonal probability factor.
479
490
  The seasonal factor is a sine-function, like in DHWcalc.
480
491
 
481
- :param initial_day: int: 0: Mon, 1: Tue, 2: Wed, 3: Thur, 4: Fri,
482
- 5 : Sat, 6 : Sun
492
+ :param initial_day: int: 0: Mon, 1: Tue, 2: Wed, 3: Thur, 4: Fri, 5 : Sat, 6 : Sun
483
493
  :param p_off_day: list: probabilities of an off day
484
494
  :param p_work_day: list: probabilities of a working day
485
495
  :param s_step: int: seconds within a timestep
486
496
  :param plot_p_yearly: bool: plot the yearly probabilities
497
+ :param holidays: list: list of holidays (day of year, 1-365)
498
+ :param building_type: string: type of building, determines which days are off days
499
+ :param occupancy_profile: list: optional profile to adjust the probabilities, has to fit in length to p_final
487
500
 
488
501
  :return: p_final: list: probabilities of a full year
489
502
  """
@@ -517,6 +530,40 @@ def generate_yearly_probabilities(initial_day, p_off_day, p_work_day,
517
530
  probability = p_day[step] * probability_season
518
531
  p_final.append(probability)
519
532
 
533
+ p_final = np.array(p_final)
534
+
535
+ if occupancy_profile is not None:
536
+ occupancy_profile = np.asarray(occupancy_profile, dtype=float)
537
+
538
+ if occupancy_profile.ndim != 1 or occupancy_profile.size == 0:
539
+ raise ValueError("occupancy_profile must be a non-empty 1-D array-like of numbers.")
540
+ if not np.all(np.isfinite(occupancy_profile)):
541
+ raise ValueError("occupancy_profile must not contain NaN or infinite values.")
542
+ if np.any(occupancy_profile < 0):
543
+ raise ValueError("occupancy_profile must not contain negative values.")
544
+ if not np.any(occupancy_profile):
545
+ raise ValueError(
546
+ "occupancy_profile is all zeros; this would zero out the entire "
547
+ "probability distribution and cause a division by zero downstream."
548
+ )
549
+
550
+ len_p = len(p_final)
551
+ len_occ = len(occupancy_profile)
552
+ if len_occ != len_p:
553
+ ratio = len_p / len_occ
554
+ if ratio.is_integer() and ratio > 1:
555
+ occupancy_profile = np.repeat(occupancy_profile, int(ratio))
556
+ else:
557
+ direction = "longer than" if len_occ > len_p else "not an integer divisor of"
558
+ raise ValueError(
559
+ f"occupancy_profile length ({len_occ}) is {direction} the target length "
560
+ f"({len_p}, from s_step={s_step}). occupancy_profile must either match "
561
+ f"the target length exactly, or be a shorter list whose length divides "
562
+ f"evenly into it (it will be upsampled via np.repeat); downsampling a "
563
+ f"longer profile is not supported."
564
+ )
565
+ p_final *= occupancy_profile
566
+
520
567
  if plot_p_yearly:
521
568
  fig, ax = plt.subplots()
522
569
  plt.plot(p_final)
@@ -781,7 +828,7 @@ def draw_lineplot(timeseries_df, plot_var='water', start_plot='2019-02-01',
781
828
  # compute some stats for figure title.
782
829
  max_water_flow = timeseries_df['Water_LperH'].max() # in L/h
783
830
  s_step = timeseries_df.index.freqstr
784
- method = timeseries_df['method'][0]
831
+ method = timeseries_df['method'].iloc[0]
785
832
 
786
833
  plt.title('Heat Time-series from {}, timestep = {}\n'
787
834
  'with a Peak of {:.1f} L/h'.format(method, s_step,
@@ -797,10 +844,10 @@ def draw_lineplot(timeseries_df, plot_var='water', start_plot='2019-02-01',
797
844
  plt.show()
798
845
 
799
846
  if save_fig:
800
- method = timeseries_df['method'][0]
847
+ method = timeseries_df['method'].iloc[0]
801
848
  s_step = get_s_step(timeseries_df)
802
- vol_per_day = timeseries_df['mean_drawoff_vol_per_day'][0]
803
- cats = timeseries_df['categories'][0]
849
+ vol_per_day = timeseries_df['mean_drawoff_vol_per_day'].iloc[0]
850
+ cats = timeseries_df['categories'].iloc[0]
804
851
 
805
852
  dir_output = Path.cwd() / "plots"
806
853
  dir_output.mkdir(exist_ok=True)
@@ -825,7 +872,7 @@ def draw_histplot(timeseries_df, extra_kde=False, save_fig=False):
825
872
  # get non-zero values of the profile
826
873
  drawoffs_df = get_drawoffs(timeseries_df=timeseries_df, remove_cats=False)
827
874
 
828
- cats = timeseries_df['categories'][0]
875
+ cats = timeseries_df['categories'].iloc[0]
829
876
  if cats == 1:
830
877
  drawoffs_df = drawoffs_df['Water_LperH']
831
878
 
@@ -848,10 +895,10 @@ def draw_histplot(timeseries_df, extra_kde=False, save_fig=False):
848
895
  plt.show()
849
896
 
850
897
  if save_fig:
851
- method = timeseries_df['method'][0]
898
+ method = timeseries_df['method'].iloc[0]
852
899
  s_step = get_s_step(timeseries_df)
853
- vol_per_day = timeseries_df['mean_drawoff_vol_per_day'][0]
854
- cats = timeseries_df['categories'][0]
900
+ vol_per_day = timeseries_df['mean_drawoff_vol_per_day'].iloc[0]
901
+ cats = timeseries_df['categories'].iloc[0]
855
902
 
856
903
  dir_output = Path.cwd() / "plots"
857
904
  dir_output.mkdir(exist_ok=True)
@@ -868,14 +915,14 @@ def draw_detailed_histplot(timeseries_df):
868
915
  https://towardsdatascience.com/advanced-histogram-using-python-bceae288e715
869
916
  plot to further analyse timeseries with 1 drawoff category.
870
917
  """
871
- cats = timeseries_df['categories'][0]
872
- method = timeseries_df['method'][0]
918
+ cats = timeseries_df['categories'].iloc[0]
919
+ method = timeseries_df['method'].iloc[0]
873
920
 
874
921
  if cats == 1 and method == 'DHWcalc':
875
922
 
876
923
  # create bin values
877
- mean = timeseries_df['mean_drawoff_flow_rate_LperH'][0]
878
- sdtdev = timeseries_df['sdtdev_drawoff_flow_rate_LperH'][0]
924
+ mean = timeseries_df['mean_drawoff_flow_rate_LperH'].iloc[0]
925
+ sdtdev = timeseries_df['sdtdev_drawoff_flow_rate_LperH'].iloc[0]
879
926
  non_zero_min = timeseries_df[timeseries_df['Water_LperH'] > 0][
880
927
  'Water_LperH'].min() # smallest entry that is not 0.
881
928
 
@@ -943,11 +990,11 @@ def add_additional_runs(timeseries_df, holidays, occupancy, building_type, total
943
990
  added_runs = total_runs - 1
944
991
 
945
992
  s_step = int(timeseries_df.index.freqstr[:-1])
946
- mean_drawoff_vol_per_day = timeseries_df['mean_drawoff_vol_per_day'][0]
947
- weekend_weekday_factor = timeseries_df['weekend_weekday_factor'][0]
948
- initial_day = timeseries_df['initial_day'][0]
949
- method = timeseries_df['method'][0]
950
- categories = timeseries_df['categories'][0]
993
+ mean_drawoff_vol_per_day = timeseries_df['mean_drawoff_vol_per_day'].iloc[0]
994
+ weekend_weekday_factor = timeseries_df['weekend_weekday_factor'].iloc[0]
995
+ initial_day = timeseries_df['initial_day'].iloc[0]
996
+ method = timeseries_df['method'].iloc[0]
997
+ categories = timeseries_df['categories'].iloc[0]
951
998
 
952
999
  if method == 'OpenDHW':
953
1000
 
@@ -1165,8 +1212,8 @@ def compare_generators(timeseries_df_1, timeseries_df_2,
1165
1212
  :param save_fig: bool: save the plot
1166
1213
  """
1167
1214
 
1168
- cats_1 = timeseries_df_1['categories'][0]
1169
- cats_2 = timeseries_df_2['categories'][0]
1215
+ cats_1 = timeseries_df_1['categories'].iloc[0]
1216
+ cats_2 = timeseries_df_2['categories'].iloc[0]
1170
1217
  if cats_1 or cats_2 == 1:
1171
1218
  print("detailed distribution is designed to compare timeseries with "
1172
1219
  "one drawoff category")
@@ -1307,8 +1354,8 @@ def compare_generators(timeseries_df_1, timeseries_df_2,
1307
1354
  drawoffs_lst = [drawoffs_1, drawoffs_2]
1308
1355
 
1309
1356
  # create bin values
1310
- mean1 = timeseries_df_1['mean_drawoff_flow_rate_LperH'][0]
1311
- sdtdev1 = timeseries_df_1['sdtdev_drawoff_flow_rate_LperH'][0]
1357
+ mean1 = timeseries_df_1['mean_drawoff_flow_rate_LperH'].iloc[0]
1358
+ sdtdev1 = timeseries_df_1['sdtdev_drawoff_flow_rate_LperH'].iloc[0]
1312
1359
  non_zero_min1 = timeseries_df_1[timeseries_df_1['Water_LperH'] > 0][
1313
1360
  'Water_LperH'].min() # smallest entry that is not 0.
1314
1361
 
@@ -1322,8 +1369,8 @@ def compare_generators(timeseries_df_1, timeseries_df_2,
1322
1369
  bin_values1 = list(set(bin_values1)) # remove double entries
1323
1370
  bin_values1.sort() # bins have to be sorted
1324
1371
 
1325
- mean2 = timeseries_df_2['mean_drawoff_flow_rate_LperH'][0]
1326
- sdtdev2 = timeseries_df_2['sdtdev_drawoff_flow_rate_LperH'][0]
1372
+ mean2 = timeseries_df_2['mean_drawoff_flow_rate_LperH'].iloc[0]
1373
+ sdtdev2 = timeseries_df_2['sdtdev_drawoff_flow_rate_LperH'].iloc[0]
1327
1374
  non_zero_min2 = timeseries_df_2[timeseries_df_2['Water_LperH'] > 0][
1328
1375
  'Water_LperH'].min() # smallest entry that is not 0.
1329
1376
 
@@ -1510,8 +1557,8 @@ def make_title_str(timeseries_df):
1510
1557
  yearly_water_demand = timeseries_df['Water_L'].sum() # in L
1511
1558
  drawoffs = timeseries_df[timeseries_df['Water_LperH'] != 0]['Water_LperH']
1512
1559
  max_water_flow = timeseries_df['Water_LperH'].max()
1513
- method = timeseries_df['method'][0]
1514
- cats = timeseries_df['categories'][0]
1560
+ method = timeseries_df['method'].iloc[0]
1561
+ cats = timeseries_df['categories'].iloc[0]
1515
1562
 
1516
1563
  if cats == 1:
1517
1564
  method = "{} ({} cat)".format(method, cats)
@@ -1642,7 +1689,7 @@ def reduce_no_drawoffs(timeseries_df):
1642
1689
  """
1643
1690
 
1644
1691
  # get the expected yearly water demand
1645
- expected_yearly_water = timeseries_df['mean_drawoff_vol_per_day'][0] * 365
1692
+ expected_yearly_water = timeseries_df['mean_drawoff_vol_per_day'].iloc[0] * 365
1646
1693
  actual_yearly_water = timeseries_df['Water_L'].sum()
1647
1694
 
1648
1695
  if expected_yearly_water < actual_yearly_water:
@@ -5,4 +5,4 @@ as well as a utils package.
5
5
  """
6
6
 
7
7
  from OpenDHW.OpenDHW import *
8
- __version__ = '0.2.7'
8
+ __version__ = '0.2.9'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OpenDHW
3
- Version: 0.2.7
3
+ Version: 0.2.9
4
4
  Summary: Tool for generating domestic hot water profiles
5
5
  Home-page: https://github.com/RWTH-EBC/OpenDHW
6
6
  Author: RWTH Aachen University, E.ON Energy Research Center, Institute of Energy Efficient Buildings and Indoor Climate
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OpenDHW
3
- Version: 0.2.7
3
+ Version: 0.2.9
4
4
  Summary: Tool for generating domestic hot water profiles
5
5
  Home-page: https://github.com/RWTH-EBC/OpenDHW
6
6
  Author: RWTH Aachen University, E.ON Energy Research Center, Institute of Energy Efficient Buildings and Indoor Climate
File without changes
File without changes
File without changes
File without changes