Breakthrough-Energy / SwitchWrapper

Wrapper for Switch
MIT License
1 stars 2 forks source link

feat: add storage expansion results to Grid #104

Closed danielolsen closed 3 years ago

danielolsen commented 3 years ago

Pull Request doc

Purpose

What the code is doing

Testing

Tested manually on results files generated both with and without storage candidates.

Code used to prepare inputs with storage candidates:

>>> import os
>>> import pandas as pd
>>> from powersimdata import Scenario
>>> from switchwrapper.prepare import prepare_inputs
>>> scenario = Scenario(599)
>>> grid = scenario.get_grid()
>>> profiles = {
...     "demand": scenario.get_demand(),
...     "hydro": scenario.get_hydro(),
...     "solar": scenario.get_solar(),
...     "wind": scenario.get_wind(),
... }
>>> timepoints = pd.read_csv("timepoints_input_v2.csv", index_col=0)
>>> timestamps_to_timepoints = pd.read_csv("slicing_recovery.csv", index_col=0).squeeze()
>>> prepare_inputs(grid, profiles, timepoints, timestamps_to_timepoints, "prepared", storage_candidate_buses={2010001, 2010002})
Please enter base study year (normally PowerSimData scenario year): 2030
Please enter the number of investment stages: 1
Single stage expansion identified.
Please enter investment period year, separate by space: 2030
Please enter start year for each period, separate by space: 2030
Please enter end year for each period, separate by space: 2030
>>>

To analyze the results:

>>> import pickle
>>> from powersimdata import Scenario
>>> from switchwrapper.switch_to_grid import construct_grids_from_switch_results
>>> filename = "path_to_results_file\results.pickle"
>>> with open(filename, "rb") as f:
...     results = pickle.load(f)
...
>>> grid = Scenario(599).get_grid() # the grid used as input to this Switch run
>>> all_grids = construct_grids_from_switch_results(grid, results)
>>>

For this example, there are storage results, as verified via:

>>> import re
>>> sorted({re.search(r"(.*)\[", v).group(1) for v in results.solution._list[0]["Variable"]})
['BuildGen', 'BuildLocalTD', 'BuildStorageEnergy', 'BuildTx', 'ChargeStorage', 'DispatchBaseloadByPeriod', 'DispatchGen', 'DispatchTx', 'GenFuelUseRate', 'StateOfCharge', 'WithdrawFromCentralGrid']

But no storage capacity is build, so there is no storage added to the resulting Grid:

>>> all_grids[2030].storage
{'gen': Empty DataFrame
Columns: [bus_id, Pg, Qg, Qmax, Qmin, Vg, mBase, status, Pmax, Pmin, Pc1, Pc2, Qc1min, Qc1max, Qc2min, Qc2max, ramp_agc, ramp_10, ramp_30, ramp_q, apf, mu_Pmax, mu_Pmin, mu_Qmax, mu_Qmin]
Index: [], 'gencost': Empty DataFrame
Columns: [type, startup, shutdown, n, c2, c1, c0]
Index: [], 'StorageData': Empty DataFrame
Columns: [UnitIdx, InitialStorage, InitialStorageLowerBound, InitialStorageUpperBound, InitialStorageCost, TerminalStoragePrice, MinStorageLevel, MaxStorageLevel, OutEff, InEff, LossFactor, rho, ExpectedTerminalStorageMax, ExpectedTerminalStorageMin]
Index: [], 'genfuel': [], 'duration': None, 'min_stor': None, 'max_stor': None, 'InEff': None, 'OutEff': None, 'LossFactor': None, 'energy_price': None, 'terminal_min': None, 'terminal_max': None}

Time estimate

30 minutes.

BainanXia commented 3 years ago

Correct me if I'm wrong: there are two parameters that we need to extract for each storage expansion candidate: 1) capacity, which is specified within the group of generators, i.e. BuildGen, and it gives the maximum input/output power of a storage device 2) energy, which is specified within the particular group "BuildStorageEnergy", and it gives the maximum energy that a specific storage device could store. What is the relationship of the same entry between different investment years? Are they cumulative values that we could take directly or deltas that we need to do a cumulative sum?

danielolsen commented 3 years ago

Correct me if I'm wrong: there are two parameters that we need to extract for each storage expansion candidate: 1) capacity, which is specified within the group of generators, i.e. BuildGen, and it gives the maximum input/output power of a storage device 2) energy, which is specified within the particular group "BuildStorageEnergy", and it gives the maximum energy that a specific storage device could store.

This is correct.

What is the relationship of the same entry between different investment years? Are they cumulative values that we could take directly or deltas that we need to do a cumulative sum?

This is a very good question! I don't know, but we have the same concerns for generation and transmission. Currently, we are processing the Switch results as if they are cumulative, but that could be wrong. It will need further investigation.

danielolsen commented 3 years ago

I added a couple more commits so that we can successfully call get_pg even when there are some storage candidates in the inputs, and therefore storage dispatch in the results:

BainanXia commented 3 years ago

Looks good to me. We can merge this once @YifanLi86 is happy.