コード例 #1
0
ファイル: test_tariff.py プロジェクト: stdk/stoppark_panel
 def test_hourly_with_max_per_day(self):
     tariff_a = Tariff.create(['1', 'Hourly + max_per_day', '1', '1', '1', 'None', '100', 'None'])
     self.assertEqual(tariff_a.calc(datetime(2013, 10, 26, 8, 0, 0), datetime(2013, 10, 28, 16, 20, 0)).state(),
                      (2, 8, 20, 57, timedelta(2, 32400), 57))
     tariff_b = Tariff.create(['1', 'Hourly + max_per_day', '1', '1', '1', 'None', '10', 'None'])
     self.assertEqual(tariff_b.calc(datetime(2013, 10, 26, 8, 0, 0), datetime(2013, 10, 28, 16, 20, 0)).state(),
                      (2, 8, 20, 57, timedelta(2, 32400), 29))
コード例 #2
0
ファイル: test_tariff.py プロジェクト: stdk/stoppark_panel
    def test_dynamic_with_max_per_day(self):
        tariff = Tariff.create(['2', '', '2', '1', ' '.join(str(i) for i in range(1, 25)), 'None', '100', 'None'])
        self.assertEqual(tariff.calc(datetime(2013, 10, 26, 8, 0, 0), datetime(2013, 10, 28, 11, 10, 0)).state(),
                         (2, 3, 10, 51, timedelta(2, 3*3600 + 600), 206))
        self.assertEqual(tariff.calc(datetime(2013, 10, 26, 8, 0, 0), datetime(2013, 10, 28, 16, 20, 0)).state(),
                         (2, 8, 20, 57, timedelta(2, 32400), 245))

        tariff = Tariff.create(['2', '', '2', '1', ' '.join(str(i) for i in range(1, 25)), 'None', '10', 'None'])
        self.assertEqual(tariff.calc(datetime(2013, 10, 26, 8, 0, 0), datetime(2013, 10, 28, 16, 20, 0)).state(),
                         (2, 8, 20, 57, timedelta(2, 32400), 30))
コード例 #3
0
def collect_stats(site,
                  target_set,
                  peaks=True,
                  intervals=False,
                  max_baseline=False):
    stat_set = []
    max_peak = max([max(t['soc']) for t in target_set])
    for target in target_set:
        tariff = Tariff('pge')
        target = tariff.apply_period(load=target)
        stat_set.append(
            get_target_stats(site,
                             target,
                             peaks=peaks,
                             intervals=intervals,
                             max_baseline=max_baseline,
                             max_peak=max_peak))
    return stat_set
コード例 #4
0
def get_demand_reductions(df, targets, config):
    peak_demand_reductions = []
    mid_peak_demand_reductions = []
    non_coincident_demand_reductions = []

    tariff = Tariff(config['site_id'])
    bill_calculator = BillCalculator(config['site_id'])

    for num, month in enumerate(targets):

        baseline_demand = bill_calculator.demand_peaks(month,
                                                       load_column="baseline")
        target_demand = bill_calculator.demand_peaks(month,
                                                     load_column="load_values")

        season = tariff.season(month.timestamp.iloc[0])
        if season == "summer":
            nc_baseline = baseline_demand["summer"]["Non-Coincident"]
            nc_target = target_demand["summer"]["Non-Coincident"]
            mp_baseline = baseline_demand["summer"]["Mid-Peak"]
            mp_target = target_demand["summer"]["Mid-Peak"]
            op_baseline = baseline_demand["summer"]["On-Peak"]
            op_target = target_demand["summer"]["On-Peak"]

            non_coincident_demand_reductions.append(nc_baseline - nc_target)
            mid_peak_demand_reductions.append(mp_baseline - mp_target)
            peak_demand_reductions.append(op_baseline - op_target)

        elif season == "winter":
            nc_baseline = baseline_demand["winter"]["Non-Coincident"]
            nc_target = target_demand["winter"]["Non-Coincident"]
            mp_baseline = baseline_demand["winter"]["Mid-Peak"]
            mp_target = target_demand["winter"]["Mid-Peak"]

            non_coincident_demand_reductions.append(nc_baseline - nc_target)
            mid_peak_demand_reductions.append(mp_baseline - mp_target)
            peak_demand_reductions.append(0)

    df = df.assign(peak_demand_reductions=peak_demand_reductions)
    df = df.assign(midpeak_demand_reductions=mid_peak_demand_reductions)
    df = df.assign(
        non_coincident_demand_reductions=non_coincident_demand_reductions)

    return df
コード例 #5
0
ファイル: test_tariff.py プロジェクト: stdk/stoppark_panel
 def test_dynamic(self):
     tariff = Tariff.create(['2', '', '2', '1', ' '.join(str(i) for i in range(1, 25)), 'None', 'None', 'None'])
     self.assertEqual(tariff.calc(datetime(2013, 10, 28, 11, 0, 0), datetime(2013, 10, 28, 11, 10, 0)).state(),
                      (0, 0, 10, 0, timedelta(0, 600), 0))
     self.assertEqual(tariff.calc(datetime(2013, 10, 28, 11, 0, 0), datetime(2013, 10, 28, 11, 45, 0)).state(),
                      (0, 0, 45, 1, timedelta(0, 3600), 1))
     self.assertEqual(tariff.calc(datetime(2013, 10, 28, 11, 0, 0), datetime(2013, 10, 28, 11, 45, 0)).state(),
                      (0, 0, 45, 1, timedelta(0, 3600), 1))
     self.assertEqual(tariff.calc(datetime(2013, 10, 28, 9, 0, 0), datetime(2013, 10, 28, 14, 45, 0)).state(),
                      (0, 5, 45, 6, timedelta(0, 21600), 21))
コード例 #6
0
ファイル: test_tariff.py プロジェクト: stdk/stoppark_panel
 def test_calc_units_daily(self):
     tariff = Tariff.create(['1', 'Daily tariff', '1', '2', '1', 'None', 'None', 'None'])
     self.assertEqual(tariff.calc_units(datetime(2013, 9, 1, 8, 0, 0), datetime(2013, 9, 30, 9, 30, 0)),
                      (timedelta(29, 5400), 30))
     self.assertEqual(tariff.calc_units(datetime(2013, 9, 30, 8, 0, 0), datetime(2013, 10, 1, 8, 30, 0)),
                      (timedelta(1, 1800), 2))
     self.assertEqual(tariff.calc_units(datetime(2013, 9, 30, 8, 0, 0), datetime(2013, 10, 1, 9, 30, 0)),
                      (timedelta(1, 5400), 2))
     self.assertEqual(tariff.calc_units(datetime(2013, 9, 30, 8, 0, 0),
                                        datetime(2013, 10, 1, 8, tariff.free_time / 60, 0)),
                      (timedelta(1, 900), 1))  # FREE_TIME works here
     self.assertEqual(tariff.calc_units(datetime(2013, 9, 1, 8, 0, 0), datetime(2013, 8, 31, 9, 30, 0)),
                      (timedelta(-1, 5400), 0))
コード例 #7
0
ファイル: test_tariff.py プロジェクト: stdk/stoppark_panel
 def test_calc_units_hourly(self):
     tariff = Tariff.create(['1', 'Hourly tariff', '1', '1', '1', 'None', 'None', 'None'])
     self.assertEqual(tariff.calc_units(datetime(2013, 3, 1, 12, 0, 0), datetime(2013, 3, 1, 14, 0, 0)),
                      (timedelta(0, 7200), 2))
     self.assertEqual(tariff.calc_units(datetime(2010, 3, 1, 12, 0, 0), datetime(2013, 3, 1, 14, 38, 41)),
                      (timedelta(1096, 9521), 26307))
     self.assertEqual(tariff.calc_units(datetime(2013, 3, 1, 12, 0, 0), datetime(2010, 3, 1, 14, 0, 0)),
                      (timedelta(-1096, 7200), 0))
     self.assertEqual(tariff.calc_units(datetime(2013, 3, 1, 12, 0, 0),
                                        datetime(2013, 3, 1, 12, tariff.free_time / 60 - 1, 0)),
                      (timedelta(0, tariff.free_time - 60), 0))
     self.assertEqual(tariff.calc_units(datetime(2013, 3, 1, 12, 0, 0),
                                        datetime(2013, 3, 1, 12, tariff.free_time / 60 - 1, 30)),
                      (timedelta(0, tariff.free_time - 60 + 30), 0))
コード例 #8
0
def test_wrong_record_type():
    tariff = Tariff(
        monthly_fee=900,
        free_minutes=100,
        numbers_free_of_charge={'+420732563345', '+420707325673'},
        fee_for_minute_same_operator=Decimal(0),
        fee_for_minute_different_operator=Decimal(0),
        free_sms_count=0,
        fee_for_sms_same_operator=Decimal(0),
        fee_for_sms_different_operator=Decimal(0),
    )

    billing = Billing(tariff)
    with pytest.raises(WrongRecordType):
        billing.add_records_from_csv_file(
            'test_billing_csvs/test_wrong_record_type.csv')
コード例 #9
0
ファイル: test_tariff.py プロジェクト: stdk/stoppark_panel
    def test_fixed_daily_with_zero_time(self):
        tariff = Tariff.create(['1', 'Special daily tariff', '1', '2', '100', '09:00', 'None', 'None'])

        # Free time in action
        self.assertEqual(tariff.calc(datetime(2013, 10, 1, 12, 0, 0), datetime(2013, 10, 1, 12, 10, 0)).state(),
                         (0, 0, 10, 0, timedelta(0, 600), 0))
        self.assertEqual(tariff.calc(datetime(2013, 10, 1, 8, 50, 0), datetime(2013, 10, 1, 9, 10, 0)).state(),
                         (0, 0, 20, 0, timedelta(0, 1200), 0))
        self.assertEqual(tariff.calc(datetime(2013, 10, 1, 8, 50, 0), datetime(2013, 10, 2, 9, 10, 0)).state(),
                         (1, 0, 20, 1, timedelta(1, 1200), 100))

        self.assertEqual(tariff.calc(datetime(2013, 10, 1, 9, 0, 0), datetime(2013, 10, 2, 9, 0, 0)).state(),
                         (1, 0, 0, 1, timedelta(1, 0), 100))
        self.assertEqual(tariff.calc(datetime(2013, 10, 1, 8, 0, 0), datetime(2013, 10, 2, 9, 0, 0)).state(),
                         (1, 1, 0, 2, timedelta(1, 3600), 200))
        self.assertEqual(tariff.calc(datetime(2013, 10, 1, 8, 0, 0), datetime(2013, 10, 2, 10, 0, 0)).state(),
                         (1, 2, 0, 3, timedelta(2, 3600), 300))
        self.assertEqual(tariff.calc(datetime(2013, 10, 31, 6, 0, 0), datetime(2013, 11, 3, 12, 0, 0)).state(),
                         (3, 6, 0, 5, timedelta(4, 3600 * 3), 500))
        self.assertEqual(tariff.calc(datetime(2013, 10, 26, 8, 0, 0), datetime(2013, 10, 28, 11, 10, 0)).state(),
                         (2, 3, 10, 4, timedelta(3, 3600), 400))
        self.assertEqual(tariff.calc(datetime(2013, 12, 9, 11, 0, 0), datetime(2013, 12, 10, 8, 0, 0)).state(),
                         (0, 21, 0, 1, timedelta(0, 79200), 100))
        self.assertEqual(tariff.calc(datetime(2013, 11, 30, 8, 0, 0), datetime(2013, 12, 1, 9, 0, 0)).state(),
                         (1, 1, 0, 2, timedelta(1, 3600), 200))
        self.assertEqual(tariff.calc(datetime(2008, 2, 28, 16, 0, 0), datetime(2008, 2, 29, 16, 10, 0)).state(),
                         (1, 0, 10, 2, timedelta(1, 61200), 200))
        self.assertEqual(tariff.calc(datetime(2008, 2, 28, 8, 0, 0), datetime(2008, 3, 1, 10, 0, 0)).state(),
                         (2, 2, 0, 4, timedelta(3, 3600), 400))
        self.assertEqual(tariff.calc(datetime(2014, 2, 27, 9, 0, 0), datetime(2014, 3, 1, 10, 10, 0)).state(),
                         (2, 1, 10, 3, timedelta(3), 300))
        self.assertEqual(tariff.calc(datetime(2013, 12, 31, 23, 0, 0), datetime(2014, 1, 1, 9, 5, 0)).state(),
                         (0, 10, 5, 1, timedelta(0, 36300), 100))
        self.assertEqual(tariff.calc(datetime(2013, 12, 9, 8, 0, 0), datetime(2013, 12, 9, 8, 50, 0)).state(),
                         (0, 0, 50, 1, timedelta(0, 3600), 100))
        self.assertEqual(tariff.calc(datetime(2013, 12, 9, 9, 0, 0), datetime(2014, 1, 10, 11, 10, 0)).state(),
                         (32, 2, 10, 33, timedelta(33), 3300))
        self.assertEqual(tariff.calc(datetime(2013, 12, 1, 9, 0, 0), datetime(2014, 11, 1, 10, 10, 0)).state(),
                         (335, 1, 10, 336, timedelta(336), 33600))
        self.assertEqual(tariff.calc(datetime(2013, 12, 3, 9, 0, 0), datetime(2013, 12, 1, 9, 0, 0)).state(),
                         (-2, 0, 0, 0, timedelta(-2), 0))
        self.assertEqual(tariff.calc(datetime(2013, 10, 24, 9, 0, 0), datetime(2013, 10, 24, 8, 50, 0)).state(),
                         (-1, 23, 50, 0, timedelta(-1, 85800), 0))

        self.assertEqual(tariff.calc(datetime(2014, 1, 30, 1, 0, 0), datetime(2014, 1, 30, 5, 0, 0)).state(),
                         (0, 4, 0, 1, timedelta(0, 3600*8), 100))
コード例 #10
0
ファイル: test_tariff.py プロジェクト: stdk/stoppark_panel
 def test_fixed_hourly(self):
     tariff = Tariff.create(['1', 'Hourly tariff', '1', '1', '1', 'None', 'None', 'None'])
     self.assertEqual(tariff.calc(datetime(2013, 10, 28, 11, 0, 0), datetime(2013, 10, 28, 11, 10, 0)).state(),
                      (0, 0, 10, 0, timedelta(0, 600), 0))
     self.assertEqual(tariff.calc(datetime(2013, 10, 28, 9, 0, 0), datetime(2013, 10, 28, 14, 45, 0)).state(),
                      (0, 5, 45, 6, timedelta(0, 21600), 6))
     self.assertEqual(tariff.calc(datetime(2013, 10, 28, 11, 0, 0), datetime(2013, 10, 28, 11, 10, 0)).state(),
                      (0, 0, 10, 0, timedelta(0, 600), 0))
     self.assertEqual(tariff.calc(datetime(2013, 12, 1, 9, 0, 0), datetime(2013, 12, 2, 10, 0, 0)).state(),
                      (1, 1, 0, 25, timedelta(1, 3600), 25))
     self.assertEqual(tariff.calc(datetime(2013, 12, 1, 23, 55, 0), datetime(2013, 12, 2, 3, 25, 0)).state(),
                      (0, 3, 30, 4, timedelta(0, 14400), 4))
     self.assertEqual(tariff.calc(datetime(2014, 2, 28, 23, 30, 0), datetime(2014, 3, 1, 4, 0, 0)).state(),
                      (0, 4, 30, 5, timedelta(0, 18000), 5))
     self.assertEqual(tariff.calc(datetime(2008, 2, 28, 23, 30, 0), datetime(2008, 2, 29, 2, 0, 0)).state(),
                      (0, 2, 30, 3, timedelta(0, 10800), 3))
     self.assertEqual(tariff.calc(datetime(2008, 2, 28, 23, 30, 0), datetime(2008, 3, 1, 2, 0, 0)).state(),
                      (1, 2, 30, 27, timedelta(1, 10800), 27))
     self.assertEqual(tariff.calc(datetime(2013, 11, 30, 23, 55, 0), datetime(2013, 12, 1, 0, 5, 0)).state(),
                      (0, 0, 10, 0, timedelta(0, 600), 0))
     self.assertEqual(tariff.calc(datetime(2013, 10, 30, 23, 50, 0), datetime(2013, 10, 31, 1, 0, 0)).state(),
                      (0, 1, 10, 1, timedelta(0, 4200), 1))
     self.assertEqual(tariff.calc(datetime(2013, 9, 30, 22, 0, 0), datetime(2013, 10, 1, 9, 0, 0)).state(),
                      (0, 11, 0, 11, timedelta(0, 39600), 11))
     self.assertEqual(tariff.calc(datetime(2013, 10, 31, 22, 0, 0), datetime(2013, 11, 1, 9, 0, 0)).state(),
                      (0, 11, 0, 11, timedelta(0, 39600), 11))
     self.assertEqual(tariff.calc(datetime(2013, 11, 30, 23, 0, 0), datetime(2013, 12, 1, 8, 0, 0)).state(),
                      (0, 9, 0, 9, timedelta(0, 32400), 9))
     self.assertEqual(tariff.calc(datetime(2013, 12, 9, 9, 0, 0), datetime(2014, 12, 10, 9, 0, 0)).state(),
                      (366, 0, 0, 8784, timedelta(366), 8784))
     self.assertEqual(tariff.calc(datetime(2013, 12, 9, 23, 0, 0), datetime(2014, 1, 10, 23, 0, 0)).state(),
                      (32, 0, 0, 768, timedelta(32), 768))
     self.assertEqual(tariff.calc(datetime(2013, 12, 31, 23, 59, 59), datetime(2014, 1, 1, 1, 0, 0)).state(),
                      (0, 1, 0, 1, timedelta(0, 3601), 1))
     self.assertEqual(tariff.calc(datetime(2013, 12, 9, 10, 0, 0), datetime(2013, 12, 9, 10, 15, 1)).state(),
                      (0, 0, 15, 1, timedelta(0, 3600), 1))
     self.assertEqual(tariff.calc(datetime(2013, 10, 26, 23, 0, 0), datetime(2013, 10, 27, 1, 0, 0)).state(),
                      (0, 2, 0, 2, timedelta(0, 7200), 2))
     self.assertEqual(tariff.calc(datetime(2013, 10, 24, 9, 0, 0), datetime(2013, 10, 21, 10, 0, 0)).state(),
                      (-3, 1, 0, 0, timedelta(-3, 3600), 0))
     self.assertEqual(tariff.calc(datetime(2013, 10, 24, 9, 0, 0), datetime(2013, 10, 24, 8, 50, 0)).state(),
                      (-1, 23, 50, 0, timedelta(-1, 85800), 0))
コード例 #11
0
def test_free_tariff():
    tariff = Tariff(
        monthly_fee=900,
        free_minutes=100,
        numbers_free_of_charge={'+420732563345', '+420707325673'},
        fee_for_minute_same_operator=Decimal(0),
        fee_for_minute_different_operator=Decimal(0),
        free_sms_count=0,
        fee_for_sms_same_operator=Decimal(0),
        fee_for_sms_different_operator=Decimal(0),
    )

    billing = Billing(tariff)
    billing.add_records_from_csv_file('test_billing_csvs/test_free_tariff.csv')
    print(billing)
    assert billing.charged_total == 0
    assert pytest.approx(billing.paid_minutes_same_operator, 39.32)
    assert billing.charged_for_minutes_same_operator == 0
    assert pytest.approx(billing.paid_minutes_different_operator, 79.78)
    assert billing.charged_for_minutes_different_operator == 0
    assert billing.paid_sms_count_same_operator == 13
    assert billing.paid_sms_count_different_operator == 15
コード例 #12
0
class Optimizer:
    """Create & solve linear optimization to genereate target building load

    The Optimizer class constructs a linear optimization with multiple input
    datapoints and configurations. It has methods to create & solve an LP
    optimization & extract decision variables to ultimately return a target
    building load.

    Attributes:
        start (datetime): first timestamp of the optimization window
        end (datetime): last timestamp of the optimization window
        mcl (int): Max charge limit (fixed value)
        mdl (int): Max discharge limit (fixed value)
        rb_capacity (int): RB tank capacity in KWH_thermal
        soc_init (int): Initial state of charge of the RB tank
        big_m (int): Big M value for slack variables
        time_labels (list): time labels [T01, T02..]
        timestamp_labels (dict): timestamps associated with each time label
        soc (dict): Decision variable - state of charge of the RB, float
        dof (dict): Decision variable - discharge offsets, float
        cof (dict): Decision variable - charge offsets, float
        y (dict): Decision variable - selector variable, binary
        max_peaks (dict): dummy variables, peak demand for all periods
        frame (obj): PuLP LP optimization object
    """

    def __init__(self, config, optimize_energy=True):
        """
        Initialize the Optimizer object.

        :param dict config: dictionary with input configurations
        """
        self.tariff_id = config.get('tariff_id') or config['site_id']
        self.tariff = Tariff(self.tariff_id)

        self.start = config['start']
        self.end = config['end']

        self.optimize_energy = optimize_energy

        # Load performance parameters from configuration
        self.mcl = config['MCL']
        self.mdl = config['MDL']
        self.mrc = config['MRC']
        self.rb_capacity = config['RB_capacity']
        self.soc_init = config['SOC_initial']
        self.soc_final = config.get('SOC_final', self.soc_init)
        self.rb_min_soc = config.get('RB_min_soc', 0)
        self.min_charge_offset = config['min_charge_offset']
        self.min_discharge_offset = config['min_discharge_offset']
        self.rte_setpoint = config.get('RTE_setpoint')
        self.cop_dchg_coefficients = config['cop_dchg_coefficients']
        self.cop_chg_coefficients = config['cop_chg_coefficients']
        self.heat_leak_coefficients = config.get('heat_leak_coefficients')
        self.chg_limit_curve = config.get('chg_limit_curve')
        self.dchg_limit_curve = config.get('dchg_limit_curve')
        self.demand_peaks = config.get('peaks')

        # Create peak structure with zero values of all peaks if peaks are
        # not included
        if not self.demand_peaks:
            self.demand_peaks = copy.deepcopy(self.tariff.demand_rates())
            for season in self.demand_peaks:
                for peak in self.demand_peaks[season]:
                    self.demand_peaks[season][peak] = 0

        # Load selectable constraints and output
        self.constraints = config.get('constraints')
        self.outputs = config.get('outputs')

        # Load big M variable value
        self.big_m = config['M']

        # Fetch dataframe wih 15 min interval timestamps and time labels
        self.timestamp_df = time_ops.get_timestamps(self.start, self.end)

        # Create list of time labels
        self.time_labels = list(self.timestamp_df.time_label)

        # Create mapping of time labels to timestamps
        self.timestamp_labels = dict(
            zip(self.timestamp_df.time_label, self.timestamp_df.timestamp))

        # Define a frame & set it up as an LP Minimization problem
        self.frame = pulp.LpProblem(
            "Simple schedule optimization",
            pulp.LpMinimize)

        # Decision variables
        self.soc = None
        self.dof = None
        self.cof = None
        self.y = None
        self.max_peaks = None

        # Define a dictionary to hold the final values for the decision
        # variables once the optimizer has solved the problem
        self.decision_variables = {}

        # Constraints
        self.chg_limits = None
        self.dchg_limits = None
        self.cop_dchg = None
        self.cop_chg = None

    def _define_decision_variables(self):
        """
        Define attributes related to optimization framework.

        This function utilizes the attributes set during initialization to set
        more attributes, namely the decision variables & other slack or dummy
        variables. The prime deicison variables include offset variables, SOC
        and a selector variable. The dummy variables include variables for each
        of the demand charges. Finally this function also sets up the frame for
        the optimization problem and defines it specifically as a minimization
        LP problem.
        """
        # Define SOC decision variables (continuous)
        self.soc = pulp.LpVariable.dicts(
            "SOC",
            self.time_labels,
            lowBound=self.rb_min_soc,
            upBound=self.rb_capacity)

        # Define discharge offset decision variables (continuous)
        self.dof = pulp.LpVariable.dicts(
            "Discharge_Offset",
            self.time_labels,
            lowBound=0,
            upBound=self.mdl)

        # Define charge offset decision variables (continuous)
        self.cof = pulp.LpVariable.dicts(
            "Charge_Offset",
            self.time_labels,
            lowBound=0,
            upBound=None)

        # Define selector decision variables (binary)
        self.y = pulp.LpVariable.dicts(
            "Selector",
            self.time_labels,
            lowBound=None,
            upBound=None,
            cat='Binary')

        # Define variables max peak, part-peak & non-coincidental demand Summer
        self.max_peaks = {season: {peak: pulp.LpVariable(
            "Maximum {} {}".format(peak, season), lowBound=0, upBound=None)
            for peak in self.demand_peaks[season]}
            for season in self.demand_peaks}

    def _define_objective(self, df, building_power_column='building_baseline'):
        """
        Define the objective function of the LP Minimization

        This function takes in building power as a dataframe, loads the energy
        tariff and demand charges from the tariffs module and proceeds to setup
        the objective function in the frame attribute of the optimizer object.
        The objective function here is simply the total bill including the
        energy and demand charges as a function of the building power, offset
        values and the peak demands.

        :param DataFrame df: Building power time series inputs
        :param building_power_column: Name of building baseline column
        """
        # Fetch energy tariff's for the given time frame
        energy_tariff_df = self.tariff.apply_energy_rates(df)

        # Fetch demand charges
        demand_charges = self.tariff.demand_rates()

        # Create mapping of energy tariffs and time labels for the given frame
        energy_tariff = dict(
            zip(self.time_labels, energy_tariff_df.energy_tariff))

        # Create mapping of building power and time labels for the given frame
        building_power = dict(
            zip(self.time_labels, df[building_power_column]))

        # ===== Set objective function =====
        # Demand portion
        season_df = self.tariff.apply_season(df)
        demand = []
        for season in self.max_peaks:
            # Determine the fraction of rows in the current season
            n = len(season_df[season_df['season'] == season])
            fraction = n / len(season_df)

            # Weight each peak by the demand charge in that period as well
            # as the fraction of days in the current season
            for period, rate in demand_charges[season].items():
                peak = self.max_peaks[season][period]
                demand.append(peak * rate * fraction)

        # Energy portion
        energy = []
        for t in self.time_labels:
            offset = self.dof[t] - self.cof[t]
            power = building_power[t] - offset
            energy.append(energy_tariff[t] * power / 4)

        # Add objective to frame
        if self.optimize_energy:
            self.frame += pulp.lpSum(demand + energy)
        else:
            self.frame += pulp.lpSum(demand)

    def _add_constraints(self, df, building_power_column='building_baseline',
                         crs_power_column='crs_baseline',
                         temperature_column='temperature',
                         discharge_limit_column='discharge_limits',
                         charge_limit_column='charge_limits',
                         cop_charge_column='cop_charge',
                         cop_discharge_column='cop_discharge'):
        """
        Define constraints for the LP Minimization

        This function takes in building power as a DataFrame and adds energy
        balance constraints to the optimization frame, as well as limits on
        different parameters including both offsets to limit their value as
        well as force only one parameter to be active in each 15 min interval.

        :param DataFrame df: Building power, CRS power, and OAT time series
        :param str building_power_column: Name of building baseline column
        :param str crs_power_column: Name of CRS baseline column
        :param str temperature_column: Name of air temperature column
        :param str discharge_limit_column: Name of discharge limit column
        :param str charge_limit_column: Name of charge limit column
        """
        # If the CRS power is given, use it to derive CHG/DCHG limits,
        # otherwise the charge and discharge limits must be given
        if crs_power_column in df.columns:
            # CRS power time series inputs
            crs_power_df = df[crs_power_column]

            # Derive discharge limits from CRS power
            self.dchg_limits = get_discharge_limits(crs_power_df)

            # Derive charge limits from CRS power
            self.chg_limits = get_charge_limits(crs_power_df, self.mrc)
        else:
            # Discharge limit time series inputs
            self.dchg_limits = df[discharge_limit_column]

            # Charge limit power time series inputs
            self.chg_limits = df[charge_limit_column]

        # Fetch max compressor capacity for each interval based on temperature
        # mcc_df = cop.generate_mcc(temperature_df)

        # Create mappings of building power and time labels
        building_power = dict(zip(self.time_labels, df[building_power_column]))

        # Create mappings of time series discharge limits and time labels
        dchg_limits = dict(zip(self.time_labels, self.dchg_limits))

        # Create mappings of time series charge limits and time labels
        chg_limits = dict(zip(self.time_labels, self.chg_limits))

        # Create COP time series
        if (cop_charge_column in df.columns and
                cop_discharge_column in df.columns):
            self.cop_chg = df[cop_charge_column]
            self.cop_dchg = df[cop_discharge_column]
        elif cop_charge_column in df.columns:
            self.cop_chg = df[cop_charge_column]

            # Generate CHG COP for each interval based on temperature
            cop_df = cop.generate_discharge_cop(
                df, self.cop_dchg_coefficients,
                temperature_column=temperature_column)
            self.cop_dchg = cop_df['cop_dchg']
        elif cop_discharge_column in df.columns:
            self.cop_dchg = df[cop_discharge_column]

            # Generate CHG COP for each interval based on temperature
            cop_df = cop.generate_charge_cop(
                df, self.cop_chg_coefficients,
                temperature_column=temperature_column)
            self.cop_chg = cop_df['cop_chg']
        else:
            # Generate CHG & DCHG COP for each interval based on temperature
            cop_df = cop.generate_cop(
                df, self.cop_dchg_coefficients, self.cop_chg_coefficients,
                temperature_column=temperature_column)
            self.cop_dchg = cop_df['cop_dchg']
            self.cop_chg = cop_df['cop_chg']

        # Create mapping of discharge COP's and time labels
        cop_dchg = dict(zip(self.time_labels, self.cop_dchg))

        # Create mapping of charge COP's and time labels
        cop_chg = dict(zip(self.time_labels, self.cop_chg))

        if self.heat_leak_coefficients:
            heat_leak_df = cop.generate_heat_leak(
                df, self.heat_leak_coefficients,
                temperature_column=temperature_column)
        else:
            heat_leak_df = df.copy()
            # heat_leak_df['heat_leak'] = 0

        self.heat_leak = heat_leak_df['heat_leak']
        # Create mapping of charge COP's and time labels
        heat_leak = dict(zip(self.time_labels, self.heat_leak))

        # Run loop to add constraints to the frame
        common.timer('constraints_loop')
        for t in range(len(self.time_labels)):
            if t < len(self.time_labels) - 1:
                # Difference in subsequent SOC states in kWh
                soc_difference = (self.soc[self.time_labels[t]] -
                                  self.soc[self.time_labels[t + 1]])

                # Energy fed into or used up from the tank
                offset_energy = 0.25 * (cop_dchg[self.time_labels[t]] *
                                        self.dof[self.time_labels[t]] -
                                        cop_chg[self.time_labels[t]] *
                                        self.cof[self.time_labels[t]])

                energy_heat_leak = 0.25 * (self.soc[self.time_labels[t]] *
                                           heat_leak[self.time_labels[t]])

                # Energy balance equation for the RB
                effective_energy = offset_energy + energy_heat_leak
                self.frame += soc_difference == effective_energy

                # ===== OPTIONAL CONSTRAINT =====
                if self.constraints['time_transition']:
                    # Helper variable
                    beta = (self.y[self.time_labels[t]] -
                            self.y[self.time_labels[t + 1]])

                    # Equations to support time transitions
                    self.frame += (self.dof[self.time_labels[t + 1]] <=
                                   self.big_m * (beta + 1))

                    self.frame += (self.cof[self.time_labels[t + 1]] <=
                                   self.big_m * (1 - beta))

                # Helper variable
                alpha = (self.y[self.time_labels[t]] +
                         self.y[self.time_labels[t + 1]])

                # ===== OPTIONAL CONSTRAINT =====
                if self.constraints['minimum_discharge_offset']:
                    # Equation to support minimum discharge offset
                    self.frame += (self.dof[self.time_labels[t + 1]] >=
                                   self.min_discharge_offset * (alpha - 1))

                # ===== OPTIONAL CONSTRAINT =====
                if self.constraints['minimum_charge_offset']:
                    # Equation to support minimum charge offset
                    self.frame += (self.cof[self.time_labels[t + 1]] >=
                                   self.min_charge_offset * (1 - alpha))

            # Constraint discharge offset less than time series dchg limits
            self.frame += (self.dof[self.time_labels[t]] <=
                           dchg_limits[self.time_labels[t]])

            # Constrain charge offset less than time series CRS charge limits
            self.frame += (self.cof[self.time_labels[t]] <=
                           chg_limits[self.time_labels[t]])

            # Constraints that allow either chg or dchg offset in an interval
            self.frame += (self.dof[self.time_labels[t]] <=
                           self.big_m * self.y[self.time_labels[t]])

            self.frame += (self.cof[self.time_labels[t]] <=
                           self.big_m * (1 - self.y[self.time_labels[t]]))

            # Once the RB has chosen to charge or discharge, effective
            # demand represents the adjustment to the building baseline by
            # either a charge or discharge offset to create a new target for
            #  the given 15-minute interval
            effective_demand = (building_power[self.time_labels[t]] -
                                self.dof[self.time_labels[t]] +
                                self.cof[self.time_labels[t]])

            # Since the operation of finding a maximum is not linear,
            # constraints must be added to select the maximum effective demand
            # amongst all 15-minute intervals
            time = self.timestamp_labels[self.time_labels[t]]
            season = self.tariff.season(time)
            period = self.tariff.period(time)

            if NON_COINCIDENT in self.max_peaks[season]:
                peak = self.max_peaks[season][NON_COINCIDENT]
                self.frame += peak >= effective_demand

            if period in self.max_peaks[season]:
                peak = self.max_peaks[season][period]
                self.frame += peak >= effective_demand

            # ===== OPTIONAL CONSTRAINT =====
            if self.constraints['dchg_limit_curve'] and self.dchg_limit_curve:
                if not self.dchg_limit_curve:
                    raise TypeError('Value for "dchg_limit_curve" unset')
                # Additional bounding constraint on discharge offset as f(SOC)
                for line in self.dchg_limit_curve:
                    m, b = line
                    self.frame += self.dof[self.time_labels[t]] <= ((
                                                                            m *
                                                                            self.soc[
                                                                                self.time_labels[
                                                                                    t]] + b) /
                                                                    cop_dchg[
                                                                        self.time_labels[
                                                                            t]])
            else:
                if self.dchg_limit_curve:
                    common.warning('Value provided for "dchg_limit_curve" but '
                                   '"dchg_limit_curve" constraint is not '
                                   'active')

            # ===== OPTIONAL CONSTRAINT =====
            if self.constraints['chg_limit_curve'] and self.chg_limit_curve:
                if not self.chg_limit_curve:
                    raise TypeError('Value for "chg_limit_curve" unset')
                # Additional bounding constraint on discharge offset as f(SOC)
                for line in self.chg_limit_curve:
                    m, b = line
                    self.frame += self.cof[self.time_labels[t]] <= ((
                                                                            m *
                                                                            self.soc[
                                                                                self.time_labels[
                                                                                    t]] + b) /
                                                                    cop_chg[
                                                                        self.time_labels[
                                                                            t]])
            else:
                if self.chg_limit_curve:
                    common.warning('Value provided for "chg_limit_curve" but '
                                   '"chg_limit_curve" constraint is not '
                                   'active')

        common.timer('constraints_loop', "Ran constraints loop")

        # Constraint to set initial SOC in the frame
        self.frame += self.soc["T01"] == self.soc_init

        # Constraint to set final SOC in the frame (same as initial)
        self.frame += self.soc[self.time_labels[-1]] == self.soc_final

        # ===== OPTIONAL CONSTRAINT =====
        if self.constraints['fixed_rte']:
            if not self.rte_setpoint:
                raise TypeError('Value for "rte_setpoint" unset')
            # Constraint to force RTE to a specific value
            total_dof = sum([self.dof[t] for t in self.time_labels])
            total_cof = sum([self.cof[t] for t in self.time_labels])
            self.frame += total_dof >= self.rte_setpoint * total_cof
        else:
            if self.rte_setpoint:
                common.warning('Value provided for "rte_setpoint" but '
                               '"fixed_rte" constraint is not active')

        # Constraint to set target peaks based on historical peaks in the
        # billing period
        for season in self.max_peaks:
            for period in self.max_peaks[season]:
                peak = self.max_peaks[season][period]
                self.frame += peak >= self.demand_peaks[season][period]

                # Ensure NON_COINCIDENT peak is tied to all other peaks
                if period == NON_COINCIDENT:
                    for other_period in self.max_peaks[season]:
                        if other_period != NON_COINCIDENT:
                            other_peak = self.max_peaks[season][other_period]
                            self.frame += peak >= other_peak

    def write_frame(self, file_name):
        """
        Write linear programming framework to a file

        This function writes the LP framework developed by form_optimization
        to the file path specified in the argument.

        :param str file_name:: file path for storing lp framework
        """
        # Write frame LP problem to file
        self.frame.writeLP(file_name)

    def _solve_frame(self):
        """
        Solve optimization frame and time it

        This function solves the frame setup by the form_optimization method &
        prints out the status of the solution (solved, undefined, infeasible).
        It also prints out the time taken for convergence.
        """
        pulp.LpSolverDefault.msg = 1

        # Solve & time frame
        common.timer('solve_frame')

        self.frame.solve(pulp.PULP_CBC_CMD(maxSeconds=120))
        common.timer('solve_frame', "Convergence time")

        # Post solution status to logger
        status = pulp.LpStatus[self.frame.status]
        common.debug("Solution status: {}".format(status))

        # Extract decision variable solutions from solved frame
        self.decision_variables = {v.name: v.varValue
                                   for v in self.frame.variables()}

    def _get_target(self, df, building_power_column='building_baseline',
                    temperature_column='temperature',
                    time_column='timestamp'):
        """
        Construct the target building load from the offsets & baseline

        This function takes in building power and generates the target building
        load by adding the offset values to the building baseline power.

        :param DataFrame df: Building power, CRS power, and OAT time series
        :param str building_power_column: Name of building baseline column
        :param str temperature_column: Name of air temperature column
        :param time_column: Name of time column
        :return DataFrame: Target building time series output with offsets
        """
        # Calculate effective offsets (Discharge - Charge)
        offset_values = []
        soc_values = []
        for label in self.time_labels:
            dof = self.decision_variables["Discharge_Offset_" + label]
            cof = self.decision_variables["Charge_Offset_" + label]
            soc = self.decision_variables["SOC_" + label]

            offset_values.append(dof - cof)
            soc_values.append(soc)

        # Calculate building target value based on baseline and offset
        building_target = [
            a - b for a, b in zip(df[building_power_column], offset_values)]

        output_map = OrderedDict([
            ('timestamp', df[time_column]),
            ('baseline', df[building_power_column]),
            ('offsets', offset_values),
            ('load_values', building_target),
            ('soc', soc_values),
            ('charge_limits', self.chg_limits),
            ('discharge_limits', self.dchg_limits),
            ('cop_dchg', self.cop_dchg),
            ('cop_chg', self.cop_chg),
            ('heat_leak', self.heat_leak),
            ('temperature', df[temperature_column]),
        ])

        return pandas.DataFrame(OrderedDict(
            [(output, output_map[output]) for output in output_map
             if self.outputs.get(output)]
        ))

    def solve(self, df, building_power_column='building_baseline',
              crs_power_column='crs_baseline',
              temperature_column='temperature',
              discharge_limit_column='discharge_limits',
              charge_limit_column='charge_limits',
              cop_charge_column='cop_charge',
              cop_discharge_column='cop_discharge'):
        """
        Constructs the LP Minimization framework by creating an LPP minimizer
        frame, adding an objective function, and applying constraints to it.
        The solver then produces an optimal target schedule for the RB.

        :param DataFrame df: Building power, CRS power, and OAT time series
        :param str building_power_column: Name of building baseline column
        :param str crs_power_column: Name of CRS baseline column
        :param str temperature_column: Name of air temperature column
        :param str discharge_limit_column: Name of discharge limit column
        :param str charge_limit_column: Name of charge limit column
        :return DataFrame: Target building time series output with offsets
        """
        # Call function to define decision variables
        self._define_decision_variables()

        # Call function to define objective function
        self._define_objective(df, building_power_column=building_power_column)

        # Call function to add constraints to frame
        self._add_constraints(df,
                              building_power_column=building_power_column,
                              crs_power_column=crs_power_column,
                              temperature_column=temperature_column,
                              discharge_limit_column=discharge_limit_column,
                              charge_limit_column=charge_limit_column,
                              cop_charge_column=cop_charge_column,
                              cop_discharge_column=cop_discharge_column)

        # Run the solver
        self._solve_frame()

        return self._get_target(df)
コード例 #13
0
ファイル: test_tariff.py プロジェクト: stdk/stoppark_panel
 def test_fixed_hourly_extra(self):
     tariff = Tariff.create(['1', 'Hourly tariff', '1', '1', '11', 'None', 'None', 'None'])
     print tariff.calc(datetime(2014, 1, 17, 16, 24, 25), datetime(2014, 01, 17, 16, 32, 23))
コード例 #14
0
    def __init__(self, config, optimize_energy=True):
        """
        Initialize the Optimizer object.

        :param dict config: dictionary with input configurations
        """
        self.tariff_id = config.get('tariff_id') or config['site_id']
        self.tariff = Tariff(self.tariff_id)

        self.start = config['start']
        self.end = config['end']

        self.optimize_energy = optimize_energy

        # Load performance parameters from configuration
        self.mcl = config['MCL']
        self.mdl = config['MDL']
        self.mrc = config['MRC']
        self.rb_capacity = config['RB_capacity']
        self.soc_init = config['SOC_initial']
        self.soc_final = config.get('SOC_final', self.soc_init)
        self.rb_min_soc = config.get('RB_min_soc', 0)
        self.min_charge_offset = config['min_charge_offset']
        self.min_discharge_offset = config['min_discharge_offset']
        self.rte_setpoint = config.get('RTE_setpoint')
        self.cop_dchg_coefficients = config['cop_dchg_coefficients']
        self.cop_chg_coefficients = config['cop_chg_coefficients']
        self.heat_leak_coefficients = config.get('heat_leak_coefficients')
        self.chg_limit_curve = config.get('chg_limit_curve')
        self.dchg_limit_curve = config.get('dchg_limit_curve')
        self.demand_peaks = config.get('peaks')

        # Create peak structure with zero values of all peaks if peaks are
        # not included
        if not self.demand_peaks:
            self.demand_peaks = copy.deepcopy(self.tariff.demand_rates())
            for season in self.demand_peaks:
                for peak in self.demand_peaks[season]:
                    self.demand_peaks[season][peak] = 0

        # Load selectable constraints and output
        self.constraints = config.get('constraints')
        self.outputs = config.get('outputs')

        # Load big M variable value
        self.big_m = config['M']

        # Fetch dataframe wih 15 min interval timestamps and time labels
        self.timestamp_df = time_ops.get_timestamps(self.start, self.end)

        # Create list of time labels
        self.time_labels = list(self.timestamp_df.time_label)

        # Create mapping of time labels to timestamps
        self.timestamp_labels = dict(
            zip(self.timestamp_df.time_label, self.timestamp_df.timestamp))

        # Define a frame & set it up as an LP Minimization problem
        self.frame = pulp.LpProblem(
            "Simple schedule optimization",
            pulp.LpMinimize)

        # Decision variables
        self.soc = None
        self.dof = None
        self.cof = None
        self.y = None
        self.max_peaks = None

        # Define a dictionary to hold the final values for the decision
        # variables once the optimizer has solved the problem
        self.decision_variables = {}

        # Constraints
        self.chg_limits = None
        self.dchg_limits = None
        self.cop_dchg = None
        self.cop_chg = None
コード例 #15
0
ファイル: test_tariff.py プロジェクト: stdk/stoppark_panel
 def test_dynamic_zero_time_24(self):
     tariff = Tariff.create(['2', '', '2', '1', ' '.join(str(i) for i in range(1, 25)), '24:00', '100', 'None'])
     self.assertEqual(tariff.calc(datetime(2014, 2, 1, 8, 0, 0), datetime(2014, 2, 1, 10, 0, 0)).state(),
                      (0, 2, 0, 2, timedelta(0, 2*3600), 1+2))
コード例 #16
0
ファイル: test_tariff.py プロジェクト: stdk/stoppark_panel
 def test_dynamic_with_zero_time_and_max_per_day(self):
     tariff = Tariff.create(['2', '', '2', '1', ' '.join(str(i) for i in range(1, 25)), '00:00', '100', 'None'])
     self.assertEqual(tariff.calc(datetime(2014, 01, 29, 8, 21, 0), datetime(2014, 01, 29, 10, 0, 0)).state(),
                      (0, 1, 39, 2, timedelta(0, 2*3600), 9 + 10))
     self.assertEqual(tariff.calc(datetime(2014, 01, 29, 8, 21, 0), datetime(2014, 01, 30, 10, 0, 0)).state(),
                      (1, 1, 39, 26, timedelta(1, 2*3600), 100 + 55))
コード例 #17
0
ファイル: db.py プロジェクト: stdk/stoppark_panel
 def get_tariffs(self):
     free_time = self.get_free_time()
     ret = self.query('select * from Tariff')
     if ret:
         self.local.update_tariffs(ret)
     return filter(lambda x: x is not None, [Tariff.create(t, free_time) for t in self.local.get_tariffs()])
コード例 #18
0
from tariff import Tariff
from user import User
from comparator import Comparator

# Object For Electric Companies ( Name ,
Elect_Com = []
Elect_Com.append(
    Tariff("ENTEGA", [
        'Freiburg', 'Stuttgart', 'Karlsruhe', 'Frankfurt', 'Berlin', 'Munich'
    ], "GrunerTraum", 7.12, 9.12, "Green"))
Elect_Com.append(
    Tariff("Eon", [
        'Freiburg', 'Stuttgart', 'Karlsruhe', 'Frankfurt', 'Berlin', 'Munich'
    ], "Grun_Energien", 7.45, 9.45, "Green"))
Elect_Com.append(
    Tariff("Badenova", ['Freiburg', 'Stuttgart', 'Karlsruhe', 'Frankfurt'],
           "Express_Package", 6.25, 8.25, "Mixed"))
Elect_Com.append(
    Tariff("EnBW", ['Freiburg', 'Stuttgart', 'Karlsruhe', 'Frankfurt'],
           "EnBW_Package", 6.95, 8.95, "Mixed"))

## Object For Consumers ( USERId, Name , Occupants , Type of Consumer , Location , Generation Type Needed , Monthly Consumption , Month )
User_Profile = []
User_Profile.append(User("123", "Jack", 3, "Pvt", "Freiburg", "Green", 300, 1))
User_Profile.append(
    User("125", "Jone", 100, "Com", "Freiburg", "Mixed", 1500, 1))
User_Profile.append(User("127", "Tom", 5, "Pvt", "Stuttgart", "Mixed", 450, 1))
User_Profile.append(User("129", "Julia", 1, "Pvt", "Karlsruhe", "ANY", 200, 1))

# Calculate Prices for Single Consumer based on its choice
コード例 #19
0
ファイル: test_tariff.py プロジェクト: stdk/stoppark_panel
 def test_tariff_create(self):
     a = Tariff.create(['1', 'Hourly tariff', '1', '1', '1', 'None', 'None', 'None'])
     self.assertEquals((a.type, a.interval), (Tariff.FIXED, Tariff.HOURLY))
コード例 #20
0
ファイル: db.py プロジェクト: stdkFeanor/stoppark_panel
 def get_tariffs(self):
     ret = self.query('select * from tariff')
     if ret:
         self.local.update_tariffs(ret)
     return [Tariff.create(t) for t in self.local.get_tariffs()]
コード例 #21
0
            paid_minutes_same_operator=self.paid_minutes_same_operator,
            charged_for_seconds_same_operator=self.charged_for_minutes_same_operator,

            paid_sms_count_same_operator=self.paid_sms_count_same_operator,
            charged_for_sms_same_operator=self.charged_for_sms_same_operator,

            paid_minutes_different_operator=self.paid_minutes_different_operator,
            charged_for_seconds_different_operator=self.charged_for_minutes_different_operator,

            paid_sms_count_different_operator=self.paid_sms_count_different_operator,
            charged_for_sms_different_operator=self.charged_for_sms_different_operator,
        )


if __name__ == '__main__':
    tariff = Tariff(
        monthly_fee=900,
        free_minutes=100,
        numbers_free_of_charge={'+420732563345', '+420707325673'},
        fee_for_minute_same_operator=Decimal(1.50),
        fee_for_minute_different_operator=Decimal(3.50),
        free_sms_count=10,
        fee_for_sms_same_operator=Decimal(1.0),
        fee_for_sms_different_operator=Decimal(2.0),
    )

    billing = Billing(tariff)
    billing.add_records_from_csv_file('data.csv')
    print(billing)