Skip to content

Commit

Permalink
Merge pull request #47 from openscm/45-docs
Browse files Browse the repository at this point in the history
Move docs
  • Loading branch information
znicholls committed Dec 6, 2023
2 parents 9f5e278 + 0adac5f commit 5b2835b
Show file tree
Hide file tree
Showing 11 changed files with 229 additions and 199 deletions.
1 change: 1 addition & 0 deletions changelog/47.docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Move docs on design decisions out of `src/openscm_units/_unit_registry.py` into a dedicated notebook
3 changes: 3 additions & 0 deletions changelog/47.trivial.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Move `unit_registry` from `src/openscm_units/_unit_registry.py` into `openscm_units/__init__.py`.
This seems to be necessary to make the docs appear correctly without hacks (see [this issue](https://github.com/sphinx-doc/sphinx/issues/8547)).
This does not affect users of the public API hence is a trivial change.
20 changes: 0 additions & 20 deletions docs/source/api/openscm_units._unit_registry.rst

This file was deleted.

13 changes: 12 additions & 1 deletion docs/source/api/openscm_units.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,16 @@ API Reference
.. autosummary::
:toctree: ./

openscm_units._unit_registry
openscm_units.data

unit\_registry
==============

.. autodata:: unit_registry


ScmUnitRegistry
===============

.. autoclass:: ScmUnitRegistry
:members:
7 changes: 4 additions & 3 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@

project = "OpenSCM-Units"
# put the authors in their own variable, so they can be reused later
authors = ", ".join(["Zeb Nicholls", "Sven Willner", "Jared Lewis", "Robert Gieseke"])
authors = ", ".join(
["Zeb Nicholls", "Jared Lewis", "Mika Pflueger", "Robert Gieseke", "Sven Willner"]
)
# add a copyright year variable, we can extend this over time in future as
# needed
copyright_year = "2020 - 2023"
Expand Down Expand Up @@ -72,6 +74,7 @@
autodoc_default_options = {
# Show the inheritance of classes
"show-inheritance": True,
"imported-members": True,
}

# autosummary with autodocgen
Expand All @@ -87,8 +90,6 @@
"module_title_decider": lambda modulename: "API Reference"
if modulename == "openscm_units"
else modulename,
# Include private docs too
"skip_module_regex": "",
}
]

Expand Down
12 changes: 10 additions & 2 deletions docs/source/notebooks.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
(notebooks-reference)=
# Notebooks

Here we provide various examples of how to use OpenSCM-Units.
They are derived from
Here we provide further documentation related to OpenSCM-Units.
The docs are derived from
[jupyter notebooks](https://docs.jupyter.org/en/latest/start/index.html),
but are saved using [jupytext](https://jupytext.readthedocs.io/en/latest/)
to keep our repository slim and make it easier to track changes.
Expand All @@ -16,6 +16,14 @@ notebooks/basic-demo.py
notebooks/custom-conversions.py
```

## For developers

```{toctree}
:caption: Contents
:maxdepth: 1
notebooks/design-principles.py
```

## Notebook execution info

```{nb-exec-table}
Expand Down
10 changes: 6 additions & 4 deletions docs/source/notebooks/custom-conversions.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,12 @@
unit_registry.add_standards()

# start with e.g. N2O
nitrous_oxide = unit_registry("N2O")
print(f"N2O: {nitrous_oxide}")
nitrous_oxide = unit_registry("tN2O / yr")

# our unit registry allows us to make conversions using the
# conversion factors we previously defined
with unit_registry.context("Custom1"):
print(f"N2O in CO2-equivalent: {nitrous_oxide.to('CO2')}")
for context in ["Custom1", "Custom2"]:
with unit_registry.context(context):
print(
f"{nitrous_oxide} in CO2-equivalent in context {context} is {nitrous_oxide.to('tCO2 / yr')}"
)
178 changes: 178 additions & 0 deletions docs/source/notebooks/design-principles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
# ---
# jupyter:
# jupytext:
# text_representation:
# extension: .py
# format_name: percent
# format_version: '1.3'
# jupytext_version: 1.15.2
# kernelspec:
# display_name: Python 3 (ipykernel)
# language: python
# name: python3
# ---

# %% [markdown]
# # Design principles
#
# Here we provide an overview of the key design principles and choices in OpenSCM-Units.

# %% [markdown]
# Unit handling makes use of the [Pint](https://github.com/hgrecco/pint) library. This
# allows us to easily define units as well as contexts. Contexts allow us to perform
# conversions which would not normally be allowed e.g. in the 'AR4GWP100'
# context we can convert from CO2 to CH4 using the AR4GWP100 equivalence metric.
#
# An illustration of how the ``unit_registry`` can be used is shown below

# %%
import traceback

from pint.errors import DimensionalityError

from openscm_units import unit_registry

# %%
unit_registry("CO2")

# %%
emissions_aus = 0.34 * unit_registry("Gt C / yr")
emissions_aus

# %%
emissions_aus.to("Mt CO2 / yr")

# %%
with unit_registry.context("AR4GWP100"):
print((100 * unit_registry("Mt CH4 / yr")).to("Mt CO2 / yr"))

# %% [markdown]
# ## More details on emissions unit
#
# Emissions are a flux composed of three parts: mass, the species being emitted and the
# time period e.g. "t CO2 / yr". As mass and time are part of SI units, all we need to
# define in OpenSCM-Units are emissions units i.e. the stuff. Here we include as many of the canonical
# emissions units, and their conversions, as possible.
#
# For emissions units, there are a few cases to be considered:
#
# - fairly obvious ones e.g. carbon dioxide emissions can be provided in 'C' or 'CO2' and
# converting between the two is possible
# - less obvious ones e.g. NOx emissions can be provided in 'N' or 'NOx', we provide
# conversions between these two which can be enabled if needed (see below).
# - case-sensitivity. In order to provide a simplified interface, using all uppercase
# versions of any unit is also valid e.g. `unit_registry("HFC4310mee")` is the same as
# `unit_registry("HFC4310MEE")`
# - hyphens and underscores in units. In order to be Pint compatible and to simplify
# things, we strip all hyphens and underscores from units.
#
# As a convenience, we allow users to combine the mass and the type of emissions to make a
# 'joint unit' e.g. "tCO2". It should be recognised that this joint unit is a derived
# unit and not a base unit.
#
# By defining these three separate components, it is much easier to track what conversions
# are valid and which are not. For example, as the emissions units are all defined as
# emissions units, and not as atomic masses, we are able to prevent invalid conversions.
# If emissions units were simply atomic masses, it would be possible to convert between
# e.g. C and N2O which would be a problem. Conventions such as allowing carbon dioxide
# emissions to be reported in C or CO2, despite the fact that they are fundamentally
# different chemical species, is a convention which is particular to emissions (as far as
# we can tell).
#
# Pint's contexts are particularly useful for emissions as they facilitate
# metric conversions. With a context, a conversion which wouldn't normally be allowed
# (e.g. tCO2 --> tN2O) is allowed and will use whatever metric conversion is appropriate
# for that context (e.g. AR4GWP100).

# %% [markdown]
# ## Namespace collisions
#
# Finally, we discuss namespace collisions.

# %% [markdown]
# ### CH$_4$
#
# Methane emissions are defined as 'CH4'. In order to prevent inadvertent conversions of
# 'CH4' to e.g. 'CO2' via 'C', the conversion 'CH4' <--> 'C' is by default forbidden.
# However, it can be performed within the context 'CH4_conversions' as shown below:

# %%
try:
unit_registry("CH4").to("C")
except DimensionalityError:
traceback.print_exc(limit=0, chain=False)

# %% [markdown]
# With a context, the conversion becomes legal again

# %%
with unit_registry.context("CH4_conversions"):
print(unit_registry("CH4").to("C"))

# %% [markdown]
# As an unavoidable side effect, this also becomes possible

# %%
with unit_registry.context("CH4_conversions"):
print(unit_registry("CH4").to("CO2"))

# %% [markdown]
# ### N$_2$O
#
# Nitrous oxide emissions are typically reported with units of 'N2O'. However,
# they are also reported with units of 'N2ON' (a short-hand which indicates that
# only the mass of the nitrogen is being counted). Reporting nitrous oxide
# emissions with units of simply 'N' is ambiguous (do you mean the mass of
# nitrogen, so 1 N = 28 / 44 N2O or just the mass of a single N atom, so
# 1 N = 14 / 44 N2O). By default, converting 'N2O' <--> 'N' is forbidden to
# prevent this ambiguity. However, the conversion can be performed within the
# context 'N2O_conversions', in which case it is assumed that 'N' just means a
# single N atom i.e. 1 N = 14 / 44 N2O, as shown below:

# %%
try:
unit_registry("N2O").to("N")
except DimensionalityError:
traceback.print_exc(limit=0, chain=False)

# %% [markdown]
# With a context, the conversion becomes legal again:

# %%
with unit_registry.context("N2O_conversions"):
print(unit_registry("N2O").to("N"))

# %% [markdown]
# ### NO$_x$

# %% [markdown]
# Like for methane, NOx emissions also suffer from a namespace collision. In order to
# prevent inadvertent conversions from 'NOx' to e.g. 'N2O', the conversion 'NOx' <-->
# 'N' is by default forbidden. It can be performed within the 'NOx_conversions' context:

# %%
try:
unit_registry("NOx").to("N")
except DimensionalityError:
traceback.print_exc(limit=0, chain=False)

# %%
with unit_registry.context("NOx_conversions"):
print(unit_registry("NOx").to("N"))

# %% [markdown]
# ### NH$_3$
#
# In order to prevent inadvertent conversions from 'NH3' to 'CO2', the conversion
# 'NH3' <--> 'N' is by default forbidden. It can be performed within the 'NH3_conversions'
# context analogous to the 'NOx_conversions' context:

# %%
try:
unit_registry("NH3").to("N")
except DimensionalityError:
traceback.print_exc(limit=0, chain=False)

# %%
with unit_registry.context("NH3_conversions"):
unit_registry("NH3").to("N")
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ description = "Handling of units related to simple climate modelling."
authors = [
"Zebedee Nicholls <zebedee.nicholls@climate-energy-college.org>",
"Jared Lewis <jared.lewis@climate-resource.com>",
"Mika Pflueger <mika.pflueger@climate-resource.com>",
"Robert Gieseke <rob.g@web.de>",
"Sven Willner <sven.willer@gmail.com>",
]
Expand Down
14 changes: 13 additions & 1 deletion src/openscm_units/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,23 @@
"""
import importlib.metadata

from ._unit_registry import ScmUnitRegistry, unit_registry
from ._unit_registry import ScmUnitRegistry

__version__ = importlib.metadata.version("openscm_units")

__all__ = [
"ScmUnitRegistry",
"unit_registry",
]


unit_registry = ScmUnitRegistry()
"""
Standard unit registry
The unit registry contains all of the recognised units. Be careful, if you
edit this registry in one place then it will also be edited in any other
places that use :mod:`openscm_units`. If you want multiple, separate registries,
create multiple instances of :class:`ScmUnitRegistry`.
"""
unit_registry.add_standards()
Loading

0 comments on commit 5b2835b

Please sign in to comment.