def adjust(self, tariff, balance, diff=0): # Adjust values if self.gaps: raise Exception('Is not possible to adjust a profile with gaps') profile = Profile(self.start_date, self.end_date, self.measures) dragger = Dragger() energy_per_period = profile.get_consumption_per_period(tariff) for period_name, period_balance in balance.items(): period_profile = energy_per_period[period_name] margin_bottom = period_balance - diff margin_top = period_balance + diff if not margin_bottom <= period_profile <= margin_top: profile.adjusted_periods.append(period_name) for idx, measure in enumerate(profile.measures): period = tariff.get_period_by_date(measure.date).code if period != period_name: continue values = measure._asdict() values['valid'] = True if not energy_per_period[period]: values['measure'] = dragger.drag(measure.measure * 0) else: values['measure'] = dragger.drag(measure.measure * ( balance[period] / energy_per_period[period] )) profile.measures[idx] = measure._replace(**values) return profile
def adjust(self, tariff, balance, diff=0): # Adjust values if self.gaps: raise Exception('Is not possible to adjust a profile with gaps') profile = Profile(self.start_date, self.end_date, self.measures) dragger = Dragger() energy_per_period = profile.get_consumption_per_period(tariff) for period_name, period_balance in balance.items(): period_profile = energy_per_period[period_name] margin_bottom = period_balance - diff margin_top = period_balance + diff if not margin_bottom <= period_profile <= margin_top: profile.adjusted_periods.append(period_name) for idx, measure in enumerate(profile.measures): dt = measure.date - timedelta(minutes=1) period = tariff.get_period_by_date(dt).code if period != period_name: continue values = measure._asdict() values['valid'] = True if not energy_per_period[period]: values['measure'] = dragger.drag(measure.measure * 0) else: values['measure'] = dragger.drag( measure.measure * (balance[period] / energy_per_period[period])) profile.measures[idx] = measure._replace(**values) return profile
def simple_dragger(measures): dragger = Dragger() for idx, measure in enumerate(measures): values = measure._asdict() consumption = dragger.drag(measure.measure) values['measure'] = consumption measures[idx] = measure._replace(**values) return measures
def adjust(self, tariff, balance): # Adjust values if self.gaps: raise Exception('Is not possible to adjust a profile with gaps') profile = Profile(self.start_date, self.end_date, self.measures) dragger = Dragger() if profile.total_consumption != sum(balance.values()): energy_per_period = profile.get_consumption_per_period(tariff) for idx, measure in enumerate(profile.measures): period = tariff.get_period_by_date(measure.date).code values = measure._asdict() values['valid'] = True if not energy_per_period[period]: values['measure'] = dragger.drag(measure.measure * 0) else: values['measure'] = dragger.drag(measure.measure * ( balance[period] / energy_per_period[period] )) profile.measures[idx] = measure._make(values.values()) return profile
def estimate(self, tariff, balance): assert isinstance(tariff, Tariff) logger.debug('Estimating for tariff: {0}'.format( tariff.code )) measures = [x for x in self.measures if x.valid] start = self.start_date end = self.end_date cofs = self.profile_class.get_range(start, end) cofs = Coefficients(cofs) cofs_per_period = Counter() for gap in self.gaps: period = tariff.get_period_by_date(gap) gap_cof = cofs.get(gap) cofs_per_period[period.code] += gap_cof.cof[tariff.cof] logger.debug('Coefficients per period calculated: {0}'.format( cofs_per_period )) energy_per_period = self.get_estimable_consumption(tariff, balance) energy_per_period_rem = energy_per_period.copy() dragger = Dragger() for idx, gap in enumerate(self.gaps): logger.debug('Gap {0}/{1}'.format( idx + 1, len(self.gaps) )) drag_key = period.code period = tariff.get_period_by_date(gap) gap_cof = cofs.get(gap).cof[tariff.cof] energy = energy_per_period[period.code] # If the balance[period] < energy_profile[period] fill with 0 # the gaps if energy < 0: energy = 0 gap_energy = (energy * gap_cof) / cofs_per_period[period.code] aprox = dragger.drag(gap_energy, key=drag_key) energy_per_period_rem[period.code] -= gap_energy logger.debug( 'Energy for hour {0} is {1}. {2} Energy {3}/{4}'.format( gap, aprox, period.code, energy_per_period_rem[period.code], energy )) pos = bisect.bisect_left(measures, ProfileHour(gap, 0, True)) profile_hour = ProfileHour(TIMEZONE.normalize(gap), aprox, True) measures.insert(pos, profile_hour) profile = Profile(self.start_date, self.end_date, measures) return profile
def profile(self, tariff, measures, drag_method='hour'): """ :param tariff: :param measures: :param drag_method: 'hour' means drag is passed to the next hour 'period' means drag is passed to the next hour for the same period :return: """ # {'PX': [(date(XXXX-XX-XX), 100), (date(XXXX-XX-XX), 110)]} _measures = list(measures) measures = {} for m in sorted(_measures): measures.setdefault(m.period.code, []) measures[m.period.code].append(m) measures_intervals = EnergyMeasure.intervals(_measures) logger.debug('Profiling {0} intervals'.format(len(measures_intervals))) for idx, measure_date in enumerate(measures_intervals): if idx + 1 == len(measures_intervals): break start = measure_date if idx > 0: start += timedelta(days=1) end = measures_intervals[idx + 1] logger.debug('Getting coeffs from {0} to {1}'.format( start, end )) sum_cofs = self.coefficient.get_coefs_by_tariff(tariff, start, end) dragger = Dragger() for hour, cof in self.coefficient.get_range(start, end): period = tariff.get_period_by_date(hour) if drag_method == 'hour': dp = 'hour' else: dp = period.code d = hour.date() if hour.hour == 0: d -= timedelta(days=1) # To take the first measure if d == start: d += timedelta(days=1) fake_m = Measure(d, period, 0) pos = bisect.bisect_left(measures[period.code], fake_m) consumption = measures[period.code][pos].consumption logger.debug('Hour: {0} Period: {1} Consumption: {2}'.format( hour, period.code, consumption )) cof = cof[tariff.cof] hour_c = ((consumption * cof) / sum_cofs[period.code]) aprox = dragger.drag(hour_c, key=dp) yield ( hour, { 'aprox': aprox, 'drag': dragger[dp], 'consumption': consumption, 'consumption_date': measures[period.code][pos].date, 'sum_cofs': sum_cofs[period.code], 'cof': cof, 'period': period.code } )
def estimate(self, tariff, balance): assert isinstance(tariff, Tariff) logger.debug('Estimating for tariff: {0}'.format( tariff.code )) # Adapt balance for simplified T30A with just one period if isinstance(tariff, T30A_one_period) or isinstance(tariff, T31A_one_period): balance = { "P1": sum([values for values in balance.values()]) } # Get balance for T31ALB if isinstance(tariff, T31A) and tariff.low_voltage_measure: balance = tariff.apply_31A_LB_cof( balance, self.start_date, self.end_date ) measures = [x for x in self.measures if x.valid] start = self.start_date end = self.end_date # - REE cofs get from (year/month) # - Simel cofs get from (year/month/day hour) - can't substract one day if self.first_day_of_month or not issubclass(self.profile_class, REEProfile): cofs = self.profile_class.get_range(start, end) else: cofs = self.profile_class.get_range( start, end - relativedelta(days=1) ) cofs = Coefficients(cofs) cofs_per_period = Counter() for gap in self.gaps: period = tariff.get_period_by_date(gap) gap_cof = cofs.get(gap) cofs_per_period[period.code] += gap_cof.cof[tariff.cof] logger.debug('Coefficients per period calculated: {0}'.format( cofs_per_period )) energy_per_period = self.get_estimable_consumption(tariff, balance) energy_per_period_rem = energy_per_period.copy() dragger = Dragger() # Initialize the Dragger with passed accumulated value if len(self.gaps) > 0: # Drag by_hours if not self.drag_by_periods: init_drag_key = "default" else: init_drag_key = tariff.get_period_by_date(self.gaps[0]).code dragger.drag(self.accumulated, key=init_drag_key) for idx, gap in enumerate(self.gaps): logger.debug('Gap {0}/{1}'.format( idx + 1, len(self.gaps) )) period = tariff.get_period_by_date(gap) drag_key = period.code if not self.drag_by_periods else "default" gap_cof = cofs.get(gap).cof[tariff.cof] energy = energy_per_period[period.code] # If the balance[period] < energy_profile[period] fill with 0 if energy < 0: energy = 0 gap_energy = (energy * gap_cof) / cofs_per_period[period.code] aprox = dragger.drag(gap_energy, key=drag_key) energy_per_period_rem[period.code] -= gap_energy logger.debug( 'Energy for hour {0} is {1}. {2} Energy {3}/{4}'.format( gap, aprox, period.code, energy_per_period_rem[period.code], energy )) pos = bisect.bisect_left(measures, ProfileHour(gap, 0, True, 0.0)) profile_hour = ProfileHour(TIMEZONE.normalize(gap), aprox, True, dragger[drag_key]) measures.insert(pos, profile_hour) profile = Profile(self.start_date, self.end_date, measures) return profile
def estimate(self, tariff, balance): assert isinstance(tariff, Tariff) logger.debug('Estimating for tariff: {0}'.format(tariff.code)) # Adapt balance for simplified T30A with just one period if isinstance(tariff, T30A_one_period) or isinstance( tariff, T31A_one_period): balance = {"P1": sum([values for values in balance.values()])} # Adapt T31A6P adding P4 to P1 if isinstance(tariff, T31A) and balance.get('P4', 0) > 0: balance['P1'] += balance['P4'] balance['P4'] = 0 measures = [x for x in self.measures if x.valid] start = self.start_date end = self.end_date # - REE cofs get from (year/month) # - Simel cofs get from (year/month/day hour) - can't substract one day if self.first_day_of_month or not issubclass(self.profile_class, REEProfile): cofs = self.profile_class.get_range(start, end) else: cofs = self.profile_class.get_range(start, end - relativedelta(days=1)) cofs = Coefficients(cofs) cofs_per_period = Counter() for gap in self.gaps: dt = gap - timedelta(minutes=1) period = tariff.get_period_by_date(dt) gap_cof = cofs.get(dt) cofs_per_period[period.code] += gap_cof.cof[tariff.cof] logger.debug( 'Coefficients per period calculated: {0}'.format(cofs_per_period)) energy_per_period = self.get_estimable_consumption(tariff, balance) energy_per_period_rem = energy_per_period.copy() dragger = Dragger() # Initialize the Dragger with passed accumulated value if len(self.gaps) > 0: # Drag by_hours if not self.drag_by_periods: init_drag_key = "default" else: dt = self.gaps[0] - timedelta(minutes=1) init_drag_key = tariff.get_period_by_date(dt).code dragger.drag(self.accumulated, key=init_drag_key) for idx, gap in enumerate(self.gaps): logger.debug('Gap {0}/{1}'.format(idx + 1, len(self.gaps))) dt = gap - timedelta(minutes=1) period = tariff.get_period_by_date(dt) drag_key = period.code if not self.drag_by_periods else "default" gap_cof = cofs.get(gap).cof[tariff.cof] energy = energy_per_period[period.code] # If the balance[period] < energy_profile[period] fill with 0 if energy < 0: energy = 0 try: gap_energy = (energy * gap_cof) / cofs_per_period[period.code] except ZeroDivisionError as error: gap_energy = 0 logger.debug(error) aprox = dragger.drag(gap_energy, key=drag_key) energy_per_period_rem[period.code] -= gap_energy logger.debug( 'Energy for hour {0} is {1}. {2} Energy {3}/{4}'.format( gap, aprox, period.code, energy_per_period_rem[period.code], energy)) pos = bisect.bisect_left(measures, ProfileHour(gap, 0, True, 0.0)) profile_hour = ProfileHour(TIMEZONE.normalize(gap), aprox, True, dragger[drag_key]) measures.insert(pos, profile_hour) profile = Profile(self.start_date, self.end_date, measures) return profile
def profile(self, tariff, measures, drag_method='hour'): """ :param tariff: :param measures: :param drag_method: 'hour' means drag is passed to the next hour 'period' means drag is passed to the next hour for the same period :return: """ # {'PX': [(date(XXXX-XX-XX), 100), (date(XXXX-XX-XX), 110)]} _measures = list(measures) measures = {} for m in sorted(_measures): measures.setdefault(m.period.code, []) measures[m.period.code].append(m) measures_intervals = EnergyMeasure.intervals(_measures) # Detect single day profiling case if len(measures_intervals) == 1: measures_intervals.append( measures_intervals[-1] ) # duplicate measure date to do not skip loop below logger.debug('Profiling {0} intervals'.format(len(measures_intervals))) for idx, measure_date in enumerate(measures_intervals): if idx + 1 == len(measures_intervals): break start = measure_date if idx > 0: start += timedelta(days=1) end = measures_intervals[idx + 1] logger.debug('Getting coeffs from {0} to {1}'.format(start, end)) sum_cofs = self.coefficient.get_coefs_by_tariff(tariff, start, end) dragger = Dragger() for hour, cof in self.coefficient.get_range(start, end): dt = hour - timedelta(minutes=1) period = tariff.get_period_by_date(dt) if drag_method == 'hour': dp = 'hour' else: dp = period.code d = hour.date() if hour.hour == 0: d -= timedelta(days=1) # To take the first measure if d == start and len( set(measures_intervals )) != 1: # if single day case, do not regress date d += timedelta(days=1) fake_m = Measure(d, period, 0) pos = bisect.bisect_left(measures.get(period.code, []), fake_m) pcode = period.code if pcode not in measures or pos >= len(measures[period.code]): consumption = 0 consumption_date = None else: consumption = measures[period.code][pos].consumption consumption_date = measures[period.code][pos].date logger.debug('Hour: {0} Period: {1} Consumption: {2}'.format( hour, period.code, consumption)) cof = cof[tariff.cof] hour_c = ((consumption * cof) / sum_cofs[period.code]) aprox = dragger.drag(hour_c, key=dp) yield (hour, { 'aprox': aprox, 'drag': dragger[dp], 'consumption': consumption, 'consumption_date': consumption_date, 'sum_cofs': sum_cofs[period.code], 'cof': cof, 'period': period.code })