Skip to content

Commit

Permalink
Merge pull request #468 from Breakthrough-Energy/develop
Browse files Browse the repository at this point in the history
chore: merge develop into master for v0.4.1 release
  • Loading branch information
danielolsen authored Apr 28, 2021
2 parents 59f1038 + 6e05438 commit d707839
Show file tree
Hide file tree
Showing 57 changed files with 2,681 additions and 1,426 deletions.
47 changes: 47 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
name: Bug report
about: Create a report to help us improve
title: Bug report
labels: bug
assignees: ahurli, BainanXia, danielolsen, jon-hagg, rouille

---

# :beetle:

- [ ] I have checked that this issue has not already been reported.


### Bug summary
A short 1-2 sentences that succinctly describes the bug.

### Code for reproduction
A minimum code snippet required to reproduce the bug. Please make sure to minimize the
number of dependencies required.
```python
# Paste your code here
#
#
```

### Actual outcome
The output produced by the above code, which may be a screenshot, console output, etc.
```shell
# If applicable, paste the console output here
#
#
```

### Expected outcome
A description of the expected outcome from the code snippet.

### Environment
Please specify your platform and versions of the relevant libraries you are using:
* Operating system:
* PowerSimData revision (run `git rev-parse origin/HEAD`):
* Python version:
* Jupyter version (if applicable):
* Other libraries:

### Additional context
Add any other context about the problem here.
26 changes: 26 additions & 0 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Publish docker image

on:
workflow_dispatch:
push:
branches:
- 'develop'

jobs:
push_to_registry:
name: Push Docker image to GitHub Packages
runs-on: ubuntu-latest
steps:
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.CR_PAT }}

- name: Build and push
uses: docker/build-push-action@v2
with:
push: true
tags: |
ghcr.io/breakthrough-energy/powersimdata:latest
133 changes: 61 additions & 72 deletions Pipfile.lock

Large diffs are not rendered by default.

488 changes: 46 additions & 442 deletions README.md

Large diffs are not rendered by default.

123 changes: 123 additions & 0 deletions docs/capacity_planning.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
Capacity Planning Framework
---------------------------
The capacity planning framework is intended to estimate the amount of new capacity that
will be required to meet future clean energy goals.


Required Inputs
+++++++++++++++
At minimum, this framework requires a *reference* ``Scenario`` object--used to specify
the current capacities and capacity factors of resources which *count* towards
state-level clean energy goals (this ``Scenario`` object must be in **analyze**
state)--and a list of target areas (comprised of one or more zones) and their target
clean energy penetrations. A strategy must also be specified, either ``independent``
(each area meets it own goal) or ``collaborative`` (all areas with non-zero goals work
together to meet a shared goal, resembling REC trading).

The list of targets may be specified in either a CSV file or a data frame, as long as
the required columns are present: ``region_name`` and ``ce_target_fraction``. Optional
columns are: ``allowed_resources`` (defaulting to solar & wind),
``external_ce_addl_historical_amount`` (clean energy not modeled in our grid, defaulting
to 0), and ``solar_percentage`` (how much of the new capacity will be solar, defaulting
to the current solar:wind ratio. This input only applies to the *independent* strategy,
a shared-goal new solar fraction for *collaborative* planning is specified in the
function call to ``calculate_clean_capacity_scaling``.


Optional Inputs
+++++++++++++++
Since increasing penetration of renewable capacity is often associated with increased
curtailment, an expectation of this new curtailment can be passed as the
``addl_curtailment`` parameter. For the *collaborative* method, this must be passed as a
dictionary of ``{resource_name: value}`` pairs, for the *independent* method this must
be passed as a data frame or as a two-layer nested dictionary which can be interpreted
as a data frame. For either method, additional curtailment must be a value between 0 and
1, representing a percentage, not percentage points. For example, if the previous
capacity factor was 30%, and additional curtailment of 10% is specified, the expected
new capacity factor will be 27%, not 20%.

Another ``Scenario`` object can be passed as ``next_scenario`` to specify the magnitude
of future demand (relevant for energy goals which are expressed as a fraction of total
consumption); this `Scenario` object may be any state, as long as
``Scenario.get_demand()`` can be called successfully, i.e., if the ``Scenario`` object
is in **create** state, an interconnection must be defined. This allows calculation of
new capacity for a scenario which is being designed, using the demand scaling present in
the change table.

Finally, for the *collaborative* method, a ``solar_fraction`` may be defined, which
determines scenario-wide how much of the new capacity should be solar (the remainder
will be wind).


Example Capacity Planning Function Calls
++++++++++++++++++++++++++++++++++++++++
Basic independent call, using the demand from the reference scenario to approximate the
future demand:

.. code-block:: python
from powersimdata.design.generation.clean_capacity_scaling import calculate_clean_capacity_scaling
from powersimdata.scenario.scenario import Scenario
ref_scenario = Scenario(403)
targets_and_new_capacities_df = calculate_clean_capacity_scaling(
ref_scenario,
method="independent",
targets_filename="eastern_2030_clean_energy_targets.csv"
)
Complex collaborative call, using all optional parameters:

.. code-block:: python
from powersimdata.design.generation.clean_capacity_scaling import calculate_clean_capacity_scaling
from powersimdata.scenario.scenario import Scenario
ref_scenario = Scenario(403)
# Start building a new scenario, to plan capacity for greater demand
new_scenario = Scenario()
new_scenario.set_grid("Eastern")
zone_demand_scaling = {"Massachusetts": 1.1, "New York City": 1.2}
new_scenario.change_table.scale_demand(zone_name=zone_demand_scaling)
# Define additional expected curtailment
addl_curtailment = {"solar": 0.1, "wind": 0.15}
targets_and_new_capacities_df = calculate_clean_capacity_scaling(
ref_scenario,
method="collaborative",
targets_filename="eastern_2030_clean_energy_targets.csv",
addl_curtailment=addl_curtailment,
next_scenario=new_scenario,
solar_fraction=0.55
)
Creating a Change Table from Capacity Planning Results
++++++++++++++++++++++++++++++++++++++++++++++++++++++
The capacity planning framework returns a data frame of capacities by resource type and
target area, but the scenario creation process ultimately requires scaling factors by
resource type and zone or plant id. A function ``create_change_table`` exists to perform
this conversion process. Using a reference scenario, a set of scaling factors by
resource type, zone, and plant id is calculated. When applied to a base ``Grid`` object,
these scaling factors will result in capacities that are nearly identical to the
reference scenario on a per-plant basis (subject to rounding), with the exception of
solar and wind generators, which will be scaled up to meet clean energy goals.

.. code-block:: python
from powersimdata.design.generation.clean_capacity_scaling import create_change_table
change_table = create_change_table(targets_and_new_capacities_df, ref_scenario)
# The change table method only accepts zone names, not zone IDs, so we have to translate
id2zone = new_scenario.state.get_grid().id2zone
# Plants can only be scaled one resource at a time, so we need to loop through
for resource in change_table:
new_scenario.change_table.scale_plant_capacity(
resource=resource,
zone_name={
id2zone[id]: value
for id, value in change_table[resource]["zone_name"].items()
},
plant_id=change_table[resource]["zone_name"]
)
51 changes: 51 additions & 0 deletions docs/grid.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@

Grid Object
-----------
A ``Grid`` object contains data representing an electric power system. An object has various attributes that are listed below:

- ``data_loc`` (``str``) gives the path to the data used to create a ``Grid`` object
- ``model_immutables`` (``object``) contains static data specific to the power system
- ``zone2id`` and ``id2zone`` (``dict``) map load zone name (id) to load zone id
(name)
- ``interconnect`` (``str``) indicates the geographical region covered
- ``bus`` (``pandas.DataFrame``) encloses the characteristics of the buses
- ``sub`` (``pandas.DataFrame``) encloses the characteristics of the substations
- ``bus2sub`` (``pandas.DataFrame``) maps buses to substations
- ``plant`` (``pandas.DataFrame``) encloses the characteristics of the plants
- ``branch`` (``pandas.DataFrame``) encloses the characteristics of the AC lines,
transformers and transformer windings
- ``gencost`` (``dict``) encloses the generation cost curves
- ``dcline`` (``pandas.DataFrame``) encloses the characteristics of the HVDC lines
- ``storage`` (``dict``) encloses information related to storage units

Only the U.S. Test system presented `here <https://arxiv.org/pdf/2002.06155.pdf>`_ is
available at this time. Thus, a ``Grid`` object can represent in addition to the full
continental U.S., one of the three interconnections -- Eastern, Western or Texas-- or
a combination of two interconnections.

A ``Grid`` object can be created as follows:

- U.S. grid

.. code-block:: python
from powersimdata.input.grid import Grid
usa = Grid("USA")
- Western interconnection

.. code-block:: python
from powersimdata.input.grid import Grid
western = Grid("Western")
- combination of two interconnections

.. code-block:: python
from powersimdata.input.grid import Grid
eastern_western = Grid(["Eastern", "Western"])
texas_western = Grid(["Texas", "Western"])
A ``Grid`` object can be transformed, i.e., generators/lines can be scaled or added.
This is achieved in the scenario framework.
17 changes: 17 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
PowerSimData
============
This tutorial is designed to help users to use our software to carry power flow study in
the U.S. electrical grid. PowerSimData is an open source package written in Python that
is available on `GitHub <https://github.com/Breakthrough-Energy/PowerSimData>`_.

.. include::
grid.rst

.. include::
scenario.rst

.. include::
capacity_planning.rst

.. include::
scenario_design.rst
Loading

0 comments on commit d707839

Please sign in to comment.