Source code for pyH2A.Plugins.Power_Management_Plugin

from pyH2A.Utilities.input_modification import daily_to_yearly_power_quantity
from pyH2A.Utilities.IO import input_resolver_function, output_inserter_function
from pyH2A.Utilities.Unit_Handler.quantity import Quantity
import numpy as np

input_dict = {
    "Power Generation": {
        "Available energy (daily)": {
            "Value": {
                "type": {dict,},
                "bounds": (0, None),
            },
            "Unit": {
                "dimension": "energy",
            },
            "optional": True,
            "description": "Available energy on a daily basis, as a dictionary of years. "
                            "If not provided, it is assumed that no energy is available."
        },
        "Stored energy (daily)": {
            "Value": {
                "type": {dict,},
                "bounds": (0, None),
            },
            "Unit": {
                "dimension": "energy",
            },
            "optional": True,
            "description": "Stored energy on a daily basis as a dictionary of years. "
                            "If not provided, it is assumed that no stored energy is generated."
        },
    },
    "Power Consumption": {
        "<...>": {
            "Value": {
                "type": {np.ndarray,float,int,},
                "bounds": (0, None),
            },
            "Type": {
                "type": {str,},
                "options": {'flexible', 'on_demand'},
            },
            "Unit": {
                "dimension": "energy",
            },
            "optional": True,
            "description": "Power consumption values for each year. Can be provided for multiple consumers, "
                            "in which case they should be provided as separate entries under Power Consumption. "
                            "The type of consumer should be specified as either 'flexible' for consumers that "
                            "can consume both available and stored power, or 'on_demand' for consumers "
                            "that can only consume stored power."
        },
    },
    "Grid Electricity": {
        "Cost": {
            "Value": {
                "type": {float, np.ndarray, int},
                "bounds": (0, None),
            },
            "Unit": {
                "dimension": "currency / energy",
            },
            "optional": True,
            "description": "Cost of grid electricity. Can be provided as a single value "
                            "or as an array with values for each year. If not provided, "
                            "it is assumed that grid electricity is not used."
        },
    },
}

output_dict = {
    "Power Generation": {
        "Available energy (yearly)": {
            "Value": {
                "inserted_value": "remaining_flexible",
                "type": {np.ndarray,}, 
                "dimension": "energy",
            },
            "optional": True,
            "description": "Remaining available energy, yearly basis.",
        },
        "Stored energy (yearly)": {
            "Value": {
                "inserted_value": "remaining_stored",
                "type": {np.ndarray,},
                "dimension": "energy",
            },
            "optional": True,
            "description": "Remaining stored energy, yearly basis.",
        },
        "Available energy (daily)": {
            "Value": {
                "inserted_value": Quantity(0, 'J'),
                "type": {float,},
                "dimension": "energy",
            },
            "description": "Remaining available energy, daily basis set to 0.",
        },
        "Stored energy (daily)": {
            "Value": {
                "inserted_value": Quantity(0, 'J'),
                "type": {float,},
                "dimension": "energy",
            },
            "description": "Remaining stored energy, daily basis set to 0.",
        },
    },
    "Grid Electricity": {
        "Used grid electricity (yearly)": {
            "Value": {
                "inserted_value": "total_unfulfilled",
                "type": {np.ndarray,},
                "dimension": "energy",
            },
            "optional": True,
            "description": "Used grid electricity, yearly basis.",
        },
    },
    "Other Variable Operating Cost - Grid Electricity": {
        "Cost of grid electricity (yearly)": {
            "Value": {
                "inserted_value": "electricity_cost",
                "type": {np.ndarray,},
                "dimension": "currency",
            },
            "optional": True,
            "description": "Cost of grid electricity, yearly basis.",
        },
    },
}

[docs] class Power_Management_Plugin: '''Management of electricity production and consumption. Parameters ---------- Power Generation > Available energy (daily) > Value : dict, optional Available energy, daily basis, dictionary of years Power Generation > Stored energy (daily) > Value : dict, optional Stored energy, daily basis, dictionary of years Power Consumption > [...] > Value : nd.array or float, optional Array of yearly power consumption values Power Consumption > [...] > Type : str, optional Type of power consumer, either 'flexible' for power consumer that can consume both available power (not stored) or stored power, or 'on_demand' for power consumer that can only consume stored power. Grid Electricity > Cost > Value : float or nd.array, optional Cost of grid electricity, can be float or nd.array with same shape as Technical Operating Parameters and Specifications> Output per Year > Value Returns ------- Power Generation > Available energy (yearly) > Value : nd.array Reamining available energy, yearly basis. Power Generation > Stored energy (yearly) > Value : nd.array Reamining stored energy, yearly basis. Power Generation > Available energy (daily) > Value : float Available energy (daily) is set to zero, since available energy is now only in yearly format. Power Generation > Stored energy (daily) > Value : float Stored energy (daily) is set to zero, since stored energy is now only in yearly format. Grid Electricity > Used grid electricity (yearly) > Value : nd.array Used grid electricity, yearly basis. Other Variable Operating Cost - Grid Electricity > Cost of grid electricity (yearly) > Value : nd.array Cost of grid electricity, yearly basis. ''' def __init__(self, dcf, print_info): self.input_dict_resolved = input_resolver_function(input_dict, dcf, 'Power_Management_Plugin') if 'Power Consumption' in self.input_dict_resolved: self.calculate_consumers(dcf) self.calculate_electricity_cost(dcf) output_inserter_function(output_dict, self, dcf, 'Power_Management_Plugin')
[docs] def calculate_consumers(self, dcf): '''Negoitate available and stored power with power consumers. Including fall back options if power generation (either available power or stored power is not available). In those cases they are set to zero. ''' try: flexible_available_energy_yearly = daily_to_yearly_power_quantity( self.input_dict_resolved['Power Generation']['Available energy (daily)']['Value']) except KeyError: flexible_available_energy_yearly = np.zeros(len(dcf.operation_years)) try: stored_available_energy_yearly = daily_to_yearly_power_quantity( self.input_dict_resolved['Power Generation']['Stored energy (daily)']['Value']) except KeyError: stored_available_energy_yearly = np.zeros(len(dcf.operation_years)) (self.total_unfulfilled, self.remaining_flexible, self.remaining_stored) = allocate_power(self.input_dict_resolved['Power Consumption'], flexible_available_energy_yearly, stored_available_energy_yearly)
def calculate_electricity_cost(self, dcf): electricity_cost = Quantity(self.total_unfulfilled.unit['J'] * self.input_dict_resolved['Grid Electricity']['Cost']['Value'].unit['USD/J'], 'USD') self.electricity_cost = np.concatenate([np.zeros(dcf.inp['Financial Input Values']['Construction time']['Value']), electricity_cost.unit['USD']]) self.electricity_cost = Quantity(self.electricity_cost, 'USD')
[docs] def allocate_power(consumption, flexible_power, stored_power): """Allocate available power to consumers based on their type.""" # Initialize remaining power remaining_flexible = flexible_power.unit['J'].copy() remaining_stored = stored_power.unit['J'].copy() # Initialize total unfufilled demand total_unfulfilled = np.zeros_like(flexible_power.unit['J']) # Process on_demand consumers first (stored power only) for key, consumer in consumption.items(): if consumer['Type'] == 'on_demand': demand = consumer['Value'].unit['J'] remaining_stored, unfulfilled = calculate_fulfillment(demand, remaining_stored) total_unfulfilled += unfulfilled # Process flexible consumers (both power sources) for key, consumer in consumption.items(): if consumer['Type'] == 'flexible': demand = consumer['Value'].unit['J'] # Try flexible power first remaining_flexible, remaining_demand = calculate_fulfillment(demand, remaining_flexible) # Use stored power for remaining demand remaining_stored, unfulfilled = calculate_fulfillment(remaining_demand, remaining_stored) total_unfulfilled += unfulfilled elif consumer['Type'] == 'on_demand': pass else: print('Warning: Unknown power consumer type:', consumer['Type'], f', in Power Consumption > {key} > Type') return Quantity(total_unfulfilled, 'J'), Quantity(remaining_flexible, 'J'), Quantity(remaining_stored, 'J')
[docs] def calculate_fulfillment(demand, remaining): """Calculate fulfillment of demand using stored power.""" fulfilled = np.minimum(demand, remaining) remaining -= fulfilled unfulfilled = demand - fulfilled return remaining, unfulfilled