|
| 1 | +""" |
| 2 | +This module gives us the possibility to enforce a wind to solar capacity ratio. |
| 3 | +
|
| 4 | +It takes in wind_to_solar_ratio.csv that has the following format |
| 5 | +
|
| 6 | +PERIOD,wind_to_solar_ratio,wind_to_solar_ratio_const_gt |
| 7 | +2020,.,. |
| 8 | +2030,0.5,0 |
| 9 | +2040,1,0 |
| 10 | +2050,1.5,1 |
| 11 | +
|
| 12 | +Here when wind_to_solar_ratio is specified (i.e. not '.') a constraint is activated that enforces that |
| 13 | +
|
| 14 | +Online wind capacity >=/<= Online solar capacity * wind_to_solar_ratio |
| 15 | +
|
| 16 | +for the entire period. |
| 17 | +
|
| 18 | +When wind_to_solar_ratio_const_gt is true (1) the constraint is a >= constraint. |
| 19 | +When wind_to_solar_ratio_const_gt is False (0) the constraint is a <= constraint. |
| 20 | +""" |
| 21 | +import os |
| 22 | + |
| 23 | +import pandas as pd |
| 24 | +from pyomo.environ import * |
| 25 | + |
| 26 | +from switch_model.reporting import write_table |
| 27 | + |
| 28 | +_WIND_ENERGY_TYPE = "Wind" |
| 29 | +_SOLAR_ENERGY_TYPE = "Solar" |
| 30 | + |
| 31 | + |
| 32 | +def define_components(mod): |
| 33 | + mod.WindCapacity = Expression( |
| 34 | + mod.PERIODS, |
| 35 | + rule=lambda m, p: sum( |
| 36 | + m.GenCapacity[g, p] |
| 37 | + for g in m.VARIABLE_GENS |
| 38 | + if m.gen_energy_source[g] == _WIND_ENERGY_TYPE |
| 39 | + ), |
| 40 | + ) |
| 41 | + |
| 42 | + mod.SolarCapacity = Expression( |
| 43 | + mod.PERIODS, |
| 44 | + rule=lambda m, p: sum( |
| 45 | + m.GenCapacity[g, p] |
| 46 | + for g in m.VARIABLE_GENS |
| 47 | + if m.gen_energy_source[g] == _SOLAR_ENERGY_TYPE |
| 48 | + ), |
| 49 | + ) |
| 50 | + |
| 51 | + mod.wind_to_solar_ratio = Param( |
| 52 | + mod.PERIODS, |
| 53 | + default=0, # 0 means the constraint is inactive |
| 54 | + within=NonNegativeReals, |
| 55 | + ) |
| 56 | + |
| 57 | + mod.wind_to_solar_ratio_const_gt = Param(mod.PERIODS, default=True, within=Boolean) |
| 58 | + |
| 59 | + # We use a scaling factor to improve the numerical properties |
| 60 | + # of the model. |
| 61 | + # Learn more by reading the documentation on Numerical Issues. |
| 62 | + # 1e-3 was picked since this value is normally on the order of GW instead of MW |
| 63 | + scaling_factor = 1e-3 |
| 64 | + |
| 65 | + def wind_to_solar_ratio_const_rule(m, p): |
| 66 | + if m.wind_to_solar_ratio[p] == 0: # 0 means Constraint is inactive |
| 67 | + return Constraint.Skip |
| 68 | + |
| 69 | + lhs = m.WindCapacity[p] * scaling_factor |
| 70 | + rhs = m.SolarCapacity[p] * m.wind_to_solar_ratio[p] * scaling_factor |
| 71 | + if m.wind_to_solar_ratio_const_gt[p]: |
| 72 | + return lhs >= rhs |
| 73 | + else: |
| 74 | + return lhs <= rhs |
| 75 | + |
| 76 | + mod.wind_to_solar_ratio_const = Constraint( |
| 77 | + mod.PERIODS, rule=wind_to_solar_ratio_const_rule |
| 78 | + ) |
| 79 | + |
| 80 | + |
| 81 | +def load_inputs(mod, switch_data, inputs_dir): |
| 82 | + switch_data.load_aug( |
| 83 | + filename=os.path.join(inputs_dir, "wind_to_solar_ratio.csv"), |
| 84 | + auto_select=True, |
| 85 | + param=(mod.wind_to_solar_ratio, mod.wind_to_solar_ratio_const_gt), |
| 86 | + optional=True, # We want to allow including this module even if the file isn't there |
| 87 | + ) |
| 88 | + |
| 89 | + |
| 90 | +def post_solve(m, outdir): |
| 91 | + df = pd.DataFrame( |
| 92 | + { |
| 93 | + "WindCapacity (GW)": value(m.WindCapacity[p]) / 1000, |
| 94 | + "SolarCapacity (GW)": value(m.SolarCapacity[p]) / 1000, |
| 95 | + "ComputedRatio": value(m.WindCapacity[p] / m.SolarCapacity[p]) |
| 96 | + if value(m.SolarCapacity[p]) != 0 |
| 97 | + else ".", |
| 98 | + "ExpectedRatio": value(m.wind_to_solar_ratio[p]) |
| 99 | + if m.wind_to_solar_ratio[p] != 0 |
| 100 | + else ".", |
| 101 | + } |
| 102 | + for p in m.PERIODS |
| 103 | + ) |
| 104 | + write_table( |
| 105 | + m, |
| 106 | + output_file=os.path.join(outdir, "wind_to_solar_ratio.csv"), |
| 107 | + df=df, |
| 108 | + index=False, |
| 109 | + ) |
0 commit comments