Skip to content

[Feature] Storage Components Need Separate Cashflows for Charge/Discharge/SOC #15

@dylanjm

Description

@dylanjm

Motivation

Currently, storage components only support a single cash flow that is applied to the SOC (State of Charge) variable. This creates problems when trying to model more realistic economic behavior of storage systems where:

  1. There might be different costs for charging vs discharging operations
  2. There could be a holding cost based on SOC (capacity payments)
  3. Degradation costs might vary based on operation type

Current Behavior

  • Storage components only allow associating cashflows with the SOC variable
  • These cashflows are processed differently from non-storage components in the objective function
  • To work around this limitation, users need to remove cashflows from storage entirely

Desired Behavior

Storage components should support three distinct types of cashflows:

  • SOC Cashflows: Applied to energy stored (capacity payments, opportunity costs)
  • Charge Cashflows: Applied to charging operations (electricity purchase costs, wear-and-tear)
  • Discharge Cashflows: Applied to discharging operations (electricity sales revenue)

Design

  1. Extend the Storage class to accept three separate cashflow lists:
class Storage(Component):
    def __init__(
        self,
        # ... existing parameters ...
        cashflows=None,  # Legacy parameter (applied to SOC)
        soc_cashflows=None,  # Applied to SOC
        charge_cashflows=None,  # Applied to charging operations
        discharge_cashflows=None,  # Applied to discharging operations
        # ... other parameters ...
    ):
        # Initialize cashflow lists
        self.soc_cashflows = soc_cashflows or []
        self.charge_cashflows = charge_cashflows or []
        self.discharge_cashflows = discharge_cashflows or []
        
        # For backward compatibility
        if cashflows:
            self.soc_cashflows.extend(cashflows)
  1. Update the objective function to consider all three cashflow types:
def objective_rule(m: pyo.ConcreteModel) -> pyo.Expression:
    total = 0
    # ... existing code for non-storage components ...
    
    # Handle storage components
    for comp in m.system.components:
        if comp.name in m.STORAGE:
            # SOC cashflows
            for cf in comp.soc_cashflows:
                for t in m.T:
                    price = cf.price_profile[t] if t < len(cf.price_profile) else cf.alpha
                    total += cf.sign * price * ((m.SOC[comp.name, t] / cf.dprime) ** cf.scalex)
            
            # Charge cashflows
            for cf in comp.charge_cashflows:
                for t in m.T:
                    price = cf.price_profile[t] if t < len(cf.price_profile) else cf.alpha
                    total += cf.sign * price * ((m.charge[comp.name, t] / cf.dprime) ** cf.scalex)
            
            # Discharge cashflows
            for cf in comp.discharge_cashflows:
                for t in m.T:
                    price = cf.price_profile[t] if t < len(cf.price_profile) else cf.alpha
                    total += cf.sign * price * ((m.discharge[comp.name, t] / cf.dprime) ** cf.scalex)

Impact

Example Usage

battery = Storage(
    name="battery",
    resource=electricity,
    max_capacity=50,
    rte=0.9,
    max_charge_rate=0.3,
    max_discharge_rate=0.3,
    initial_stored=0.2,
    soc_cashflows=[Cost(name="battery_capacity_cost", alpha=0.5)],  # $/MWh-stored
    charge_cashflows=[Cost(name="battery_charging_cost", alpha=45)],  # $/MWh-charged
    discharge_cashflows=[Revenue(name="battery_discharge_rev", alpha=50)],  # $/MWh-discharged
)
  1. More realistic modeling of storage economics
  2. Better representation of market participation for batteries
  3. Ability to model arbitrage and ancillary services more accurately
  4. More intuitive model specification for users

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions