diff --git a/osier/models/capacity_expansion.py b/osier/models/capacity_expansion.py index 68f4aba..a672018 100644 --- a/osier/models/capacity_expansion.py +++ b/osier/models/capacity_expansion.py @@ -13,10 +13,10 @@ LARGE_NUMBER = 1e20 -dispatch_models = {'lp': DispatchModel, - 'hierarchical': LogicDispatchModel, - 'logical': LogicDispatchModel, - 'rule_based': LogicDispatchModel} +dispatch_models = { + 'optimal':DispatchModel, + 'logical': LogicDispatchModel + } class CapacityExpansion(ElementwiseProblem): @@ -77,6 +77,11 @@ class CapacityExpansion(ElementwiseProblem): Indicates which solver to use. May require separate installation. Accepts: ['cplex', 'cbc', 'glpk']. Other solvers will be added in the future. + model_engine : str + Determines which dispatch algorithm to use. + Accepts: ['optimal', 'logical'] where 'optimal' will use a linear + program and 'logical' will use a myopic rule-based approach. + Default is 'optimal'. Notes ----- @@ -103,7 +108,7 @@ def __init__(self, allow_blackout=False, verbosity=50, solver='cbc', - model_engine='lp', + model_engine='optimal', **kwargs): self.technology_list = deepcopy(technology_list) self.demand = demand diff --git a/osier/models/logic_dispatch.py b/osier/models/logic_dispatch.py index 4d961c6..b555707 100644 --- a/osier/models/logic_dispatch.py +++ b/osier/models/logic_dispatch.py @@ -114,7 +114,6 @@ def _calculate_objective(self): def solve(self): """ This function executes the model solve with a rule-based approach. - Net demand is copied, then the technology histories are reset. """ self.covered_demand = self.net_demand.copy() self._reset_all() diff --git a/osier/tech_library.py b/osier/tech_library.py index 2f0ccb2..d35689d 100644 --- a/osier/tech_library.py +++ b/osier/tech_library.py @@ -3,7 +3,7 @@ import pandas as pd from osier.technology import * from osier.technology import _dim_opts -from unyt import GW, MW, kW, hour, day, year, kg +from unyt import GW, MW, kW, hour, day, year, kg, MWh import unyt as u to_MDOLLARS = 1e-6 @@ -96,7 +96,7 @@ fuel_cost=0*to_MDOLLARS, storage_duration=4, efficiency=0.85, - initial_storage=0.0*MW*hr, + initial_storage=0.0*MWh, capacity_credit=0.5, lifecycle_co2_rate=3.3e-5*co2_eq_units, land_intensity=0.0, diff --git a/osier/technology.py b/osier/technology.py index 91de10d..ebd0289 100644 --- a/osier/technology.py +++ b/osier/technology.py @@ -329,38 +329,30 @@ def __ge__(self, tech) -> bool: """Tests greater or equal to.""" if (self.variable_cost == tech.variable_cost): return self.efficiency >= tech.efficiency - elif self.variable_cost >= tech.variable_cost: - return True else: - return False + return self.variable_cost >= tech.variable_cost def __le__(self, tech) -> bool: - """Tests greater or equal to.""" + """Tests less or equal to.""" if (self.variable_cost == tech.variable_cost): return self.efficiency <= tech.efficiency - elif self.variable_cost <= tech.variable_cost: - return True else: - return False + return self.variable_cost <= tech.variable_cost def __lt__(self, tech) -> bool: - """Tests greater or equal to.""" + """Tests less than.""" if (self.variable_cost == tech.variable_cost): return self.efficiency < tech.efficiency - elif self.variable_cost < tech.variable_cost: - return True else: - return False + return self.variable_cost < tech.variable_cost def __gt__(self, tech) -> bool: - """Tests greater or equal to.""" + """Tests greater than.""" if (self.variable_cost == tech.variable_cost): return self.efficiency > tech.efficiency - elif self.variable_cost > tech.variable_cost: - return True else: - return False + return self.variable_cost > tech.variable_cost @property def unit_power(self): @@ -641,6 +633,11 @@ def power_output(self, demand : :class:`unyt.unyt_quantity` The demand at a particular timestep. Must be a :class:`unyt.unyt_quantity` to avoid ambiguity. + + Returns + ------- + power_level : :class:`unyt.unyt_quantity` + The current power level of the technology. """ assert isinstance(demand, unyt_quantity) self.power_level = max(0 * demand.units, min(demand, self.capacity)) @@ -721,6 +718,11 @@ def max_power(self, time_delta: unyt_quantity = 1 * hr): ---------- time_delta : :class:`unyt.unyt_quantity` The difference between two timesteps. Default is one hour. + + Returns + ------- + max_power : :class:`unyt.unyt_quantity` + The maximum achievable power level. """ output = self.power_level + self.ramp_up * time_delta @@ -735,6 +737,11 @@ def min_power(self, time_delta: unyt_quantity = 1 * hr): ---------- time_delta : :class:`unyt.unyt_quantity` The difference between two timesteps. Default is one hour. + + Returns + ------- + max_power : :class:`unyt.unyt_quantity` + The maximum achievable power level. """ output = self.power_level - self.ramp_down * time_delta @@ -756,6 +763,11 @@ def power_output(self, to avoid ambiguity. time_delta : :class:`unyt.unyt_quantity` The difference between two timesteps. Default is one hour. + + Returns + ------- + power_level : :class:`unyt.unyt_quantity` + The current power level of the technology. """ assert isinstance(demand, unyt_quantity) @@ -875,7 +887,21 @@ def reset_history(self): self.charge_history = [] def discharge(self, demand: unyt_quantity, time_delta=1 * hr): + """ + Discharges the battery if there is a surplus of energy. + + Parameters + ---------- + demand : :class:`unyt.unyt_quantity` + Amount of surplus. + time_delta : :class:`unyt.unyt_quantity` + The real time passed between modeled timesteps. + Returns + ------- + power_level : :class:`unyt.unyt_quantity` + The current power level of the technology. + """ # check that the battery has power to discharge fully. power_out = max(0 * demand.units, min(demand, self.capacity)) @@ -891,7 +917,21 @@ def discharge(self, demand: unyt_quantity, time_delta=1 * hr): return self.power_level.to(demand.units) def charge(self, surplus, time_delta=1 * hr): + """ + Charges the battery if there is a surplus of energy. + + Parameters + ---------- + surplus : :class:`unyt.unyt_quantity` + Amount of surplus. + time_delta : :class:`unyt.unyt_quantity` + The real time passed between modeled timesteps. + Returns + ------- + power_level : :class:`unyt.unyt_quantity` + The current power level of the technology. + """ # check that the battery has enough power to consume surplus. power_in = min(np.abs(min(0 * surplus.units, surplus)), self.capacity) @@ -908,6 +948,14 @@ def charge(self, surplus, time_delta=1 * hr): return self.power_level.to(surplus.units) def power_output(self, v, time_delta=1 * hr): + """ + Calculates the power output given a demand value. + + Returns + ------- + output : :class:`unyt.unyt_quantity` + The current power level of the technology. + """ if v >= 0: output = self.discharge(demand=v, time_delta=time_delta) else: