Skip to content

Commit ab5208f

Browse files
committed
Merge remote-tracking branch 'rael/wecc' into min_per_type
2 parents ef20a81 + bbae587 commit ab5208f

36 files changed

+2229
-1308
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,6 @@ SWITCH. This will build HTML documentation files from python doc strings which
1818
will include descriptions of each module, their intentions, model
1919
components they define, and what input files they expect.
2020

21-
To learn about **numerical solvers** read [`docs/Numerical Solvers.md`](/docs/Numerical%20Solvers.md)
21+
To learn about **numerical solvers** read [`docs/Numerical Solvers.md`](/docs/Numerical%20Solvers.md)
22+
23+
To learn about **numerical issues** read [`docs/Numerical Issues.md`](/docs/Numerical%20Issues.md)

docs/Database.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ their strong points. DBVisualizer can also create a graph of all the relationshi
2121
tables.
2222

2323
Further, it is often useful to read the comments on tables (PGAdmin: right-click table -> Properties)
24-
as they sometimes give details on the tables role. Finally, if the table is used in [`get_inputs.py`](/switch_model/wecc/get_inputs.py)
24+
as they sometimes give details on the tables role. Finally, if the table is used in [`get_inputs.py`](/switch_model/wecc/get_inputs/get_inputs.py)
2525
one can discover what it does by looking at how get_inputs.py uses the table to generate the SWITCH inputs.
2626

2727
## Connecting to the database

docs/Graphs.md

Lines changed: 132 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -18,92 +18,128 @@ and `ca_policies` have already been solved).
1818

1919
## Adding new graphs
2020

21-
Graphs can be defined in any module by adding the following function to the file.
22-
21+
New graphs can be added with the `@graph(...)` annotation.
2322
```python
24-
def graph(tools):
23+
from switch_model.tools.graph import graph
24+
25+
@graph(
26+
name="my_custom_graph",
27+
title="An example plot",
28+
note="Some optional note to add below the graph",
29+
# Other options are possible see code documentation
30+
)
31+
def my_graphing_function(tools):
2532
# Your graphing code
2633
...
2734
```
2835

29-
In `graph()` you can use the `tools` object to create graphs. Here are some important methods.
30-
31-
- `tools.get_dataframe(csv=filename)` will return a pandas dataframe for the file called `filename`. You can also
32-
specify `folder=tools.folders.INPUTS` to load a csv from the inputs directory.
33-
34-
- `tools.get_new_axes(out, title, note)` will return a matplotlib axes. This should be the axes used while
35-
graphing. `out` is the name of the `.png` file that will be created with this graph. `title` and `note` are optional
36-
and will be the title and footnote for the graph.
37-
38-
- `tools.pd`, `tools.sns`, `tools.np`, `tools.mplt` are references to the pandas, seaborn, numpy and matplotlib
39-
libraries. This is useful if your graphing code needs to access these libraries since it doesn't require adding an
36+
In `my_graphing_function()` you can use the `tools` object to create graphs. Here are some important methods.
37+
38+
- `tools.get_dataframe(filename)` will return a pandas dataframe for the file called `filename`. You can also
39+
specify `from_inputs=True` to load a csv from the inputs directory.
40+
41+
- `tools.get_axes()` or `tools.get_figure()` will return a matplotlib axes or figure
42+
that should be used while graphing. When possible, always use `get_axes` instead of `get_figure` since
43+
this allows plots from different scenarios to share the same figure.
44+
45+
- `tools.save_figure(fig)`. Some libraries (e.g. plotnine)
46+
always generate their own figures. In this case we can add the figure
47+
to our outputs with this function. When possible, use `tools.get_axes()` instead.
48+
49+
- `tools.pd`, `tools.sns`, `tools.np`, `tools.mplt`, `tools.pn` are references to the pandas, seaborn, numpy, matplotlib
50+
and plotnine graphing libraries. This is useful if your graphing code needs to access these libraries since it doesn't require adding an
4051
import to your file.
4152

42-
- `tools.add_gen_type_column(df)` adds a column called `gen_type` to a dataframe with columns
43-
`gen_tech` and `gen_energy_source`. `gen_type` is a user-friendly name for the technology (e.g. Nuclear instead of
44-
Uranium). The mapping of `gen_energy_source` and `gen_tech` to `gen_type` is defined in
45-
a `inputs/graph_tech_types.csv`. If this file isn't present, a default mapping will be used. You can also use other
46-
mappings found in `graph_tech_types.csv` by specifying `map_name=` when calling `add_gen_type_column()`.
47-
53+
- `tools.transform` is a reference to a `TransformTools` object that provides
54+
useful helper methods for modyfing a dataframe for graphing. Full documentation
55+
can be found in the `TransformTools` class but some examples include.
56+
57+
- `tools.transform.build_year(df)` which will convert build years that aren't
58+
a period to the string `Pre-existing`.
59+
60+
- `tools.transform.gen_type(df)` which adds a column called `gen_type` to the dataframe.
61+
`gen_type` is a user-friendly name for the technology (e.g. Nuclear instead of
62+
Uranium) and is determined using the mappings in `inputs/graph_tech_types.csv`.
63+
64+
- `tools.transform.timestamp(df)`: which adds columns such as the hour, the timestamp in datetime format
65+
in the correct timezone, etc.
66+
67+
- `tools.transform.load_zone(df)`: Adds a column called 'region' to the dataframe which
68+
normally corresponds to the load zone state.
69+
4870
- `tools.get_colors()` returns a mapping of `gen_type` to its color. This is useful for graphing and can normally be
49-
passed straight to `color=` in standard plotting libraries. You can also specify a different color mapping using a
50-
similar process to above (`map_name=`)
71+
passed straight to `color=` in standard plotting libraries. The color mapping is based on `inputs/graph_tech_colors.csv`.
5172

5273
## Adding a comparison graph
5374

54-
By default, `tools.get_dataframe` will return the data for only one scenario (the one you are graphing).
55-
56-
Sometimes, you may wish to create a graph that compares multiple scenarios. To do this create a function
57-
called `compare`.
75+
Sometimes you may want to create graphs that compare data from multiple scenarios.
76+
To do this, add `supports_multi_scenario=True` inside the `@graph()` decorator.
5877

5978
```python
60-
def compare(tools):
61-
# Your graphing code
62-
...
79+
from switch_model.tools.graph import graph
80+
81+
@graph(
82+
name="my_custom_comparison_graph",
83+
title="My Comparison plot",
84+
supports_multi_scenario=True,
85+
# Instead of supports_multi_scenario, you can use
86+
# requires_multi_scenario if you want the graphing function
87+
# to *only* be run when we have multiple scenarios.
88+
# requires_multi_scenario=True,
89+
)
90+
def my_graphing_comparison_function(tools):
91+
# Read data from all the scenarios
92+
df = tools.get_dataframe("some_file.csv")
93+
# Plot data
94+
...
6395
```
6496

65-
If you call `tools.get_dataframe(...)` from within `compare`, then
66-
`tools.get_dataframe` will return a dataframe containing the data from *all*
67-
the scenarios. The dataframe will contain a column called `scenario` to indicate which rows correspond to which
68-
scenarios. You can then use this column to create a graph comparing the different scenarios (still
69-
using `tools.get_new_axes`).
97+
Now everytime you call `tools.get_dataframe(filename)`, data for *all* the scenarios
98+
gets returned. The way this works is that the
99+
returned dataframe will contain a column called `scenario_name`
100+
to indicate which rows correspond to which scenarios.
101+
You can then use this column to create a graph comparing the different scenarios (still
102+
using `tools.get_axes`).
70103

71-
At this point, when you run `switch compare`, your `compare(tools)` function will be called and your comparison graph
104+
At this point, when you run `switch compare`, your `my_graphing_comparison_function` function will be called and your comparison graph
72105
will be generated.
73106

74107
## Example
75108

76109
In this example we create a graph that shows the power capacity during each period broken down by technology.
77110

78111
```python
112+
from switch_model.tools.graph import graph
113+
114+
@graph(
115+
"capacity_per_period",
116+
title="Capacity per period"
117+
)
79118
def graph(tools):
80-
# Get a dataframe of gen_cap.csv
81-
gen_cap = tools.get_dataframe(csv="gen_cap")
82-
83-
# Add a 'gen_type' column to your dataframe
84-
gen_cap = tools.add_gen_type_column(gen_cap)
85-
86-
# Aggregate the generation capacity by gen_type and PERIOD
87-
capacity_df = gen_cap.pivot_table(
88-
index='PERIOD',
89-
columns='gen_type',
90-
values='GenCapacity',
91-
aggfunc=tools.np.sum,
92-
fill_value=0 # Missing values become 0
93-
)
94-
95-
# Get a new pair of axis to plot onto
96-
ax = tools.get_new_axes(out="capacity_per_period")
97-
98-
# Plot
99-
capacity_df.plot(
100-
kind='bar',
101-
ax=ax, # Notice we pass in the axis
102-
stacked=True,
103-
ylabel="Capacity Online (MW)",
104-
xlabel="Period",
105-
color=tools.get_colors(len(capacity_df.index))
106-
)
119+
# Get a dataframe of gen_cap.csv
120+
df = tools.get_dataframe("gen_cap.csv")
121+
122+
# Add a 'gen_type' column to your dataframe
123+
df = tools.transform.gen_type(df)
124+
125+
# Aggregate the generation capacity by gen_type and PERIOD
126+
df = df.pivot_table(
127+
index='PERIOD',
128+
columns='gen_type',
129+
values='GenCapacity',
130+
aggfunc=tools.np.sum,
131+
fill_value=0 # Missing values become 0
132+
)
133+
134+
# Plot
135+
df.plot(
136+
kind='bar',
137+
ax=tools.get_axes(),
138+
stacked=True,
139+
ylabel="Capacity Online (MW)",
140+
xlabel="Period",
141+
color=tools.get_colors(len(df.index))
142+
)
107143
```
108144

109145
Running `switch graph` would run the `graph()` function above and create
@@ -112,3 +148,37 @@ Running `switch graph` would run the `graph()` function above and create
112148
Running `switch compare` would create `capacity_per_period.png` containing
113149
your plot side-by-side with the same plot but for the scenario you're comparing to.
114150

151+
### Testing your graphs
152+
153+
To test your graphs, you can run `switch graph` or `switch compare`. However,
154+
this takes quite some time. If you want to test just one graphing function
155+
you can run `switch graph/compare -f FIGURE`. This will run only the graphing function
156+
you've defined. Here `FIGURE` should be the name of the graph (the first
157+
argument in `@graph()`, so `capacity_per_period` in the example above).
158+
159+
### Creating graphs outside of SWITCH
160+
161+
Sometimes you may want to create graphs but don't want to permently add
162+
them to the switch code. To do this create the following Python file anywhere
163+
on your computer.
164+
165+
```python
166+
from switch_model.tools.graph import graph
167+
from switch_model.tools.graph.cli_graph import main as graph
168+
169+
@graph(
170+
...
171+
)
172+
def my_first_graph(tools):
173+
...
174+
175+
@graph(
176+
...
177+
)
178+
def my_second_graph(tools):
179+
...
180+
181+
if __name__=="__main__":
182+
graph(["--ignore-modules-txt"])
183+
```
184+

0 commit comments

Comments
 (0)