Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,19 +90,21 @@

# The theme to use for HTML and HTML Help pages.
master_doc = "index"
html_theme = "pydata_sphinx_theme"
html_logo = "_static/Small_PNG-RMI_logo_PrimaryUse.PNG"
html_theme = "furo"
# html_logo = "_static/Small_PNG-RMI_logo_PrimaryUse.PNG"
html_icon = "_static/favicon-16x16.png"

# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
html_theme_options = {
"navigation_with_keys": True,
"logo": {
"image_light": "Small_PNG-RMI_logo_PrimaryUse.PNG",
"image_dark": "Small_PNG-RMI_logo_PrimaryUse_White_Horizontal.PNG",
},
# "logo": {
# "image_light": "Small_PNG-RMI_logo_PrimaryUse.PNG",
# "image_dark": "Small_PNG-RMI_logo_PrimaryUse_White_Horizontal.PNG",
# },
"light_logo": "Small_PNG-RMI_logo_PrimaryUse.PNG",
"dark_logo": "Small_PNG-RMI_logo_PrimaryUse_White_Horizontal.PNG",
}

# Add any paths that contain custom static files (such as style sheets) here,
Expand Down
21 changes: 19 additions & 2 deletions docs/release_notes.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
=======================================================================================
Dispatch Release Notes
Release Notes
=======================================================================================

.. _release-v0-3-0:
Expand All @@ -21,6 +21,23 @@ What's New?
:attr:`.DispatchModel.dispatchable_plant_specs` to
:attr:`.DispatchModel.dispatchable_profiles` using
:func:`.apply_op_ret_date`.
* Added validation and processing for :attr:`.DispatchModel.re_plant_specs` and
:attr:`.DispatchModel.re_profiles`, as well as :meth:`.DispatchModel.re_summary`
to, when the data is provided create a summary of renewable operations analogous
to :meth:`.DispatchModel.operations_summary`.
* Added :meth:`.DispatchModel.storage_summary` to create a summary of storage
operations analogous to :meth:`.DispatchModel.operations_summary`.
* Added :meth:`.DispatchModel.full_output` to create the kind of outputs needed by
Optimus and other post-dispatch analysis tools built on the three

Known Issues
^^^^^^^^^^^^
* :meth:`.DispatchModel.re_summary` and :meth:`.DispatchModel.storage_summary` have
operations cost data.
* :meth:`.DispatchModel.re_summary` and :meth:`.DispatchModel.storage_summary` have
no tests.
* There is still no nice way to include nuclear and hydro resources.


.. _release-v0-2-0:

Expand Down Expand Up @@ -81,7 +98,7 @@ Bug Fixes

Known Issues
^^^^^^^^^^^^
* :py:class:`.DispatchModel` only set up to work properly with
* :class:`.DispatchModel` only set up to work properly with
`patio-model <https://github.com/rmi-electricity/patio-model>`_.
* Test thoroughness is lacking.
* No substantive readme or documentation.
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ doc =
sphinx-autoapi>=1.8,<2.1
sphinx-autodoc-typehints
sphinxcontrib-mermaid
pydata-sphinx-theme>=0.10
furo
sphinx-issues>=1.2,<3.1
tests =
; Checks code for security issues
Expand Down
95 changes: 65 additions & 30 deletions src/dispatch/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,39 +28,57 @@
},
coerce=True,
)
DISPATCHABLE_COST_SCHEMA = pa.DataFrameSchema(
index=pa.MultiIndex(
[PID_SCHEMA, GID_SCHEMA, DT_SCHEMA],
unique=["plant_id_eia", "generator_id", "datetime"],
strict=True,
coerce=True,
),
columns={
"vom_per_mwh": pa.Column(float),
"fuel_per_mwh": pa.Column(float),
"total_var_mwh": pa.Column(float, required=False),
"fom_per_kw": pa.Column(float),
"start_per_kw": pa.Column(float),
},
coerce=True,
)

NET_LOAD_SCHEMA = pa.SeriesSchema(pa.Float, index=DT_SCHEMA, coerce=True)
STORAGE_SPECS_SCHEMA = pa.DataFrameSchema(
columns={
"capacity_mw": pa.Column(float),
"duration_hrs": pa.Column(int),
"roundtrip_eff": pa.Column(float),
"operating_date": pa.Column(pa.Timestamp),
},
index=pa.Index(int, unique=True),
strict=True,
coerce=True,
)


class Validator:
"""Validator for :class:`.DispatchModel` inputs."""

dispatchable_cost_schema = pa.DataFrameSchema(
index=pa.MultiIndex(
[PID_SCHEMA, GID_SCHEMA, DT_SCHEMA],
unique=["plant_id_eia", "generator_id", "datetime"],
strict=True,
coerce=True,
),
columns={
"vom_per_mwh": pa.Column(float),
"fuel_per_mwh": pa.Column(float),
"total_var_mwh": pa.Column(float, required=False),
"fom_per_kw": pa.Column(float),
"start_per_kw": pa.Column(float),
},
coerce=True,
)
storage_specs_schema = pa.DataFrameSchema(
columns={
"plant_id_eia": pa.Column(int, required=False),
"generator_id": pa.Column(str, required=False),
"capacity_mw": pa.Column(float),
"duration_hrs": pa.Column(int),
"roundtrip_eff": pa.Column(float),
"operating_date": pa.Column(pa.Timestamp),
},
index=pa.Index(int, unique=True),
# strict=True,
coerce=True,
)
renewable_specs_schema = pa.DataFrameSchema(
index=pa.MultiIndex(
[PID_SCHEMA, GID_SCHEMA],
unique=["plant_id_eia", "generator_id"],
strict=True,
coerce=True,
),
columns={
"capacity_mw": pa.Column(float),
"operating_date": pa.Column(pa.Timestamp),
"retirement_date": pa.Column(pa.Timestamp, nullable=True),
},
coerce=True,
)

def __init__(self, obj: Any):
"""Init Validator."""
self.obj = obj
Expand All @@ -74,6 +92,7 @@ def dispatchable_profiles(
dispatchable_profiles = pa.DataFrameSchema(
index=DT_SCHEMA,
columns={x: pa.Column(float) for x in self.gen_set},
ordered=True,
coerce=True,
strict=True,
).validate(dispatchable_profiles)
Expand All @@ -86,7 +105,7 @@ def dispatchable_profiles(

def dispatchable_cost(self, dispatchable_cost: pd.DataFrame) -> pd.DataFrame:
"""Validate dispatchable_cost."""
dispatchable_cost = DISPATCHABLE_COST_SCHEMA.validate(dispatchable_cost)
dispatchable_cost = self.dispatchable_cost_schema.validate(dispatchable_cost)
# make sure al
if not np.all(
dispatchable_cost.reset_index(
Expand All @@ -98,7 +117,7 @@ def dispatchable_cost(self, dispatchable_cost: pd.DataFrame) -> pd.DataFrame:
"generators in `dispatchable_cost` do not match generators in `dispatchable_specs`"
)
marg_freq = pd.infer_freq(dispatchable_cost.index.get_level_values(2).unique())
self.obj.__meta__["marginal_cost_freq"] = marg_freq
self.obj._metadata["marginal_cost_freq"] = marg_freq
if "YS" not in marg_freq and "AS" not in marg_freq:
raise AssertionError("Cost data must be `YS`")
marg_dts = dispatchable_cost.index.get_level_values("datetime")
Expand Down Expand Up @@ -128,4 +147,20 @@ def storage_specs(self, storage_specs: pd.DataFrame) -> pd.DataFrame:
"operating_date",
],
)
return STORAGE_SPECS_SCHEMA.validate(storage_specs)
return self.storage_specs_schema.validate(storage_specs)

def renewables(
self, re_plant_specs: pd.DataFrame | None, re_profiles: pd.DataFrame | None
) -> tuple[pd.DataFrame | None, pd.DataFrame | None]:
"""Validate renewable specs and profiles."""
if re_plant_specs is None or re_profiles is None:
return re_plant_specs, re_profiles
re_plant_specs = self.renewable_specs_schema.validate(re_plant_specs)
re_profiles = pa.DataFrameSchema(
index=DT_SCHEMA,
columns={x: pa.Column(float) for x in re_plant_specs.index},
ordered=True,
coerce=True,
strict=True,
).validate(re_profiles)
return re_plant_specs, re_profiles
Loading