How to Calculate Costs

The EEC package supports computing the electricity bill of a consumer with the following data:

  • Consumption data: NumPy array, CVXPY variable, or Pyomo parameter/variable

  • Tariff sheet: Pandas dataframe that can be loaded from our CSV format

This how-to guide assumes that you have already loaded the tariff sheet into a Pandas.DataFrame called tariff_df. Further guidance on how to load the data can be found at data-format-tariff.

Import Statements

To make this how-to guide clear, below are the import statements used throughout:

import datetime
import cvxpy as cp
import numpy as np
import pandas as pd
import pyomo.environ as pyo
from electric_emission_cost.units as u
from electric_emission_cost import costs

Create Charge Dictionary

The first step to computing the cost of electricity is converting the tariff DataFrame to dict. We will use the built-in function get_charge_dict:

start_date, end_date = datetime.datetime(2025, 5, 1), datetime.datetime(2025, 6, 1)
charge_dict = costs.get_charge_dict(start_date, end_date, tariff_df, resolution="15m")

In this case, start_date and end_date must be of a datetime type (i.e., datetime.datetime, numpy.datetime64, or pandas.Timestamp). Note that end_date is exclusive, so in the example above the charge_dict will be 1-month long.

The optional argument resolution should be used to specify the temporal resolution of the consumption data as a string in the from <binsize><unit>, where units are either ‘m’ for minutes, ‘h’ for hours, or ‘d’ / ‘D’ for days. The default is “15m”, so the timeseries will be on 15-minute intervals if not otherwise specified.

Calculate Electricity Bill

Next, we will calculate the cost for the given period (from start_date to end_date, exclusively). We show an example in NumPy, CVXPY, and Pyomo since the EEC package supports all three libraries.

The below examples exclude some more advanced features available via optional arguments and flags, which are particularly useful for moving horizon optimization. How to Use Advanced Features offers a more complete overview of those advanced features.

Optimize Electricity Costs offers a more complete look at how to use this functionality in an optimization problem.

Basic Usage

NumPy

# one month of 15-min intervals
num_timesteps = 24 * 4 * 31
# this is synthetic consumption data, but a user could provide real historical meter data
consumption_data_dict = {"electric": np.ones(num_timesteps) * 100, "gas": np.ones(num_timesteps))}
total_monthly_bill, _ = costs.calculate_cost(charge_dict, consumption_data_dict)

Note that we ignore the second value of the tuple returned by calculate_cost. This entry in the tuple is reserved for the Pyomo model object.

CVXPY

consumption_data_dict = {"electric": cp.Variable(num_timesteps), "gas": cp.Variable(num_timesteps)}
total_monthly_bill, _ = costs.calculate_cost(
    charge_dict, consumption_data_dict, consumption_estimate=sum(np.ones(num_timesteps) * 100)
)

Tip

You must use the consumption_estimate argument when using an optimization variable for consumption in order to determine the appropriate charge tier of the customer. For NumPy, the charge tiers can be calculated directly from the data so the consumption_estimate is ignored.

Note that we ignore the second value of the tuple returned by calculate_cost. This entry in the tuple is reserved for the Pyomo model object.

This cost would be the objective function of the optimization problem, but the user will still have to provide constraints to bound the cost minimization. See the CVXPY tutorial about how to Optimize Electricity Costs for more information!

Pyomo

consumption_data_dict = {
    "electric": pyo.Var(range(num_timesteps), initialize=np.zeros(num_timesteps), bounds=(0, None))
    "gas": pyo.Var(range(num_timesteps), initialize=np.zeros(num_timesteps), bounds=(0, None))
}
total_monthly_bill, model = costs.calculate_cost(
    charge_dict, consumption_data_dict, consumption_estimate=sum(np.ones(num_timesteps) * 100), model=model
)

Tip

You must use the consumption_estimate argument when using an optimization variable for consumption in order to determine the appropriate charge tier of the customer. For NumPy, the charge tiers can be calculated directly from the data so the consumption_estimate is ignored.

We must pass in and retrieve the Pyomo model object for the eletricity bill to be calculated correctly. The tutorial on Pyomo cost optimization has more examples of how to use the model object with the functions

Warning

For the Pyomo code to work properly, we require the model object has an attribute t that is the range of the time period.

We usually set model.t = range(model.T) where model.T = len(consumption_data_dict[“electric”]).

Specify Resolution

The temporal resolution of the consumption data should be provided as a string. The default is 15-minute intervals, so resolution=”15m”.

charge_dict = costs.get_charge_dict(start_date, end_date, tariff_df, resolution="1h")
num_timesteps = 24 * 31
consumption_data_dict = {"electric": cp.Variable(num_timesteps), "gas": cp.Variable(num_timesteps)}
total_monthly_bill, _ = costs.calculate_cost(
    charge_dict,
    consumption_data_dict,
    consumption_estimate=sum(np.ones(num_timesteps) * 100),
    resolution="1h",
)

Specify Utility

Users can select between electric and natural gas utilties by using the desired_utility optional argument. The accepted arguments are “electric”, “gas”, or None. By default, the combined costs across both utilities is calculated (i.e., desired_utility=None).

consumption_data_dict = {"electric": np.ones(num_timesteps) * 100, "gas": np.ones(num_timesteps))}
monthly_elec_bill, _ = costs.calculate_cost(charge_dict, consumption_data_dict, desired_utility="electric")

Specify Charge Type

Users can select between customer, energy, and demand charges by using the desired_charge_type optional argument. The accepted arguments are “customer”, “energy”, “demand”, or None. By default, the combined costs across both utilities is calculated (i.e., desired_utility=desired_charge_type).

consumption_data_dict = {"electric": np.ones(num_timesteps) * 100, "gas": np.ones(num_timesteps))}
monthly_elec_bill, _ = costs.calculate_cost(charge_dict, consumption_data_dict, desired_charge_type="demand")

Units

The EEC package uses Pint to handle nit conversions automaitcally. The logic depends on the proper electric_consumption_units and gas_consumption_units arguments being provided. Based on the most common data sources we have used, the electric consumption units are in kW and gas consumption units in cubic meters per day, so electric_consumption_units=u.kW and gas_consumption_units=u.m ** 3 / u.day.

For example:

# TODO: INSERT CODE SNIPPET WITH VARIOUS UNITS
# THESE OPTIONAL ARGUMENTS STILL HAVE TO BE IMPLEMENTED
# https://github.com/we3lab/electric-emission-cost/issues/17

Itemized Charges

The function calculate_itemized_cost will give you a breakdown of electricity, demand, and customer charges to analyze the customer’s electricity bill in more detail.

consumption_data_dict = {"electric": np.ones(num_timesteps) * 100, "gas": np.ones(num_timesteps))}
monthly_elec_bill, _ = costs.calculate_cost(charge_dict, consumption_data_dict, desired_charge_type="demand")
itemized_cost_dict = costs.calculate_itemized_cost(charge_dict, consumption_data_dict)

The above example is quite simple, but you can use the same optional arguments that we demonstrated above, such as resolution, desired_utility, and consumption_estimate. (desired_charge_type is not an option since the dictionary uses charge_type as a key.)

Advanced Features

See How to Use Advanced Features for an explanation of more advanced features, especially for performing moving horizon optimization.

If you have not done it already, we recommend the walkthrough to practice using this functions: Optimize Electricity Costs.