Repository: C2SM-RCM/emiproc
Pull request #94: Wetcharts
Author: login: lionel42
Created: 2025-05-05T11:06:45Z | Merged: 2025-05-06T11:00:46Z

Adding the wetcharts inventory

<!-- readthedocs-preview emiproc start -->
----
📚 Documentation preview 📚: https://emiproc--94.org.readthedocs.build/en/94/

<!-- readthedocs-preview emiproc end -->


Diff:
diff --git a/docs/source/api/inventories.rst b/docs/source/api/inventories.rst
index e9a727e7..49054611 100644
--- a/docs/source/api/inventories.rst
+++ b/docs/source/api/inventories.rst
@@ -48,3 +48,6 @@ Available Inventories
 
 .. autoclass:: emiproc.inventories.cams_reg_aq.CAMS_REG_AQ
     :special-members: __init__
+
+.. autoclass:: emiproc.inventories.wetcharts.WetCHARTs
+    :special-members: __init__
\ No newline at end of file
diff --git a/docs/source/inventories.rst b/docs/source/inventories.rst
index 52dc6f32..2b275c4f 100644
--- a/docs/source/inventories.rst
+++ b/docs/source/inventories.rst
@@ -64,6 +64,11 @@ CAMS Regional Air Quality
 :py:class:`emiproc.inventories.cams_reg_aq.CAMS_REG_AQ`
 
 
+WetCHARTs
+^^^^^^^^^
+
+:py:class:`emiproc.inventories.wetcharts.WetCHARTs`
+
 
 Grids 
 -----
diff --git a/emiproc/inventories/wetcharts.py b/emiproc/inventories/wetcharts.py
new file mode 100644
index 00000000..47c73ef0
--- /dev/null
+++ b/emiproc/inventories/wetcharts.py
@@ -0,0 +1,106 @@
+from os import PathLike
+
+import geopandas as gpd
+import pandas as pd
+import xarray as xr
+
+from emiproc.grids import RegularGrid
+from emiproc.inventories import Inventory
+from emiproc.profiles.temporal.composite import CompositeTemporalProfiles
+from emiproc.profiles.temporal.profiles import MounthsProfile
+from emiproc.profiles.utils import ratios_dataarray_to_profiles
+from emiproc.utilities import DAY_PER_YR
+
+
+class WetCHARTs(Inventory):
+    """WetCHARTs inventory.
+
+    This class is used to read the WetCHARTs file into an inventory.
+
+    More information about the WetCHARTs is available at:
+    https://daac.ornl.gov/CMS/guides/MonthlyWetland_CH4_WetCHARTsV2.html
+
+
+    :param wetcharts_file: Path to the WetCHARTs file.
+        It can be downloaded from the link above, after creating an account.
+    :param model: Model number to select from the dataset.
+        Few keys are available. To see the available models, please check
+        the data specification in the link above.
+        If None, a mean of all models is used.
+    :param category: Category name to use in the inventory.
+    :param substance: Substance name to use in the inventory.
+
+    """
+
+    def __init__(
+        self,
+        wetcharts_file: PathLike,
+        model: int | None = None,
+        category: str = "wetcharts",
+        substance: str = "CH4",
+    ) -> None:
+        super().__init__()
+
+        with xr.open_dataset(wetcharts_file) as ds:
+            if model is None:
+                ds = ds.mean(dim="model", keep_attrs=True)
+            elif isinstance(model, int):
+                if model not in ds.model.values:
+                    raise ValueError(
+                        f"Model {model} not found in the dataset. Available models: {ds.model.values}"
+                    )
+                ds = ds.sel(model=model)
+            else:
+                raise TypeError(f"Model {model} is not an integer or None.")
+            print(ds)
+            self.grid = RegularGrid.from_centers(
+                x_centers=ds.lon.values,
+                y_centers=ds.lat.values,
+                name="WetCHARTs_grid",
+            )
+
+            assert len(ds.time.values) == 12, "The dataset should have 12 months."
+
+            self.year = pd.to_datetime(ds.time.values[0]).year
+
+            var = "wetland_CH4_emissions"
+            da_stacked = (
+                ds[var]
+                .stack(cell=("lon", "lat"))
+                .drop_vars(["lon", "lat"])
+                .expand_dims(dim=dict(category=[category], substance=[substance]))
+                .fillna(0.0)
+            )
+
+        cell_areas = self.grid.cell_areas
+        cell_areas = xr.DataArray(
+            cell_areas.values, dims=["cell"], coords={"cell": da_stacked.cell.values}
+        )
+
+        # Unit conversion
+        assert ds[var].units == "mg m-2 d-1"
+
+        da_ratios = da_stacked / da_stacked.sum(dim="time")
+
+        da_ratios = da_ratios.rename(time="ratio")
+        ratios, indices = ratios_dataarray_to_profiles(da_ratios)
+
+        # kg / year / cell = mg m-2 / day * m2 /cell  *  kg / mg * days / year
+        da_stacked = da_stacked * cell_areas * 1e-6 * DAY_PER_YR
+
+        # Convert to pandas
+        df = (
+            da_stacked.stack(catsub=("category", "substance"))
+            .mean(dim="time")
+            .drop_vars(["cell"])
+            .to_pandas()
+        )
+        self.gdf = gpd.GeoDataFrame(df, geometry=self.grid.gdf.geometry)
+        self.gdfs = {}
+
+        self.set_profiles(
+            profiles=CompositeTemporalProfiles.from_ratios(
+                ratios=ratios, types=[MounthsProfile]
+            ),
+            indexes=indices,
+        )
diff --git a/examples/inventories/wetcharts.py b/examples/inventories/wetcharts.py
new file mode 100644
index 00000000..3cfe76aa
--- /dev/null
+++ b/examples/inventories/wetcharts.py
@@ -0,0 +1,22 @@
+"""Read WetCHARTs inventory and plot it."""
+
+from emiproc.inventories.wetcharts import WetCHARTs
+from emiproc.plots import plot_inventory
+from emiproc import FILES_DIR
+
+
+# Go to the website and download the file
+# Change the path here to where you downloaded the file
+file_path = FILES_DIR / "wetcharts" / "WetCHARTs_v1_3_3_2021.nc"
+
+inv = WetCHARTs(
+    file_path,
+    # Specify the model number to select from the dataset.
+    # If not specified, a mean of all models is used.
+    # model=2924,
+    # Set the name of the category (default is "wetcharts").
+    category="wetland_emissions",
+)
+
+
+plot_inventory(inv)


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR adds support for a new WetCHARTs inventory, including a module for reading and processing the inventory data and an example script for plotting it.
- Introduces the WetCHARTs class in emiproc/inventories/wetcharts.py to load and process the inventory data.
- Adds an example script in examples/inventories/wetcharts.py to demonstrate downloading, reading, and plotting the inventory.

### Reviewed Changes

Copilot reviewed 2 out of 4 changed files in this pull request and generated no comments.

| File                                     | Description                                                           |
| ---------------------------------------- | --------------------------------------------------------------------- |
| examples/inventories/wetcharts.py        | New script to load the WetCHARTs inventory and generate a plot.       |
| emiproc/inventories/wetcharts.py         | New module that implements the WetCHARTs class for inventory processing. |

<details>
<summary>Files not reviewed (2)</summary>

* **docs/source/api/inventories.rst**: Language not supported
* **docs/source/inventories.rst**: Language not supported
</details>

<details>
<summary>Comments suppressed due to low confidence (1)</summary>

**emiproc/inventories/wetcharts.py:10**
* The identifier 'MounthsProfile' appears to be a typo. Consider renaming it to 'MonthsProfile' for clarity.
```
from emiproc.profiles.temporal.profiles import MounthsProfile
```
</details>


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T11:06:56Z
user: login: lbernet
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T13:32:18Z
user: login: lbernet
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T13:37:24Z
user: login: lbernet
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T13:41:33Z
user: login: lbernet
body: great, thanks!
I am just confused concerning 2 points: 
- the downloaded wetcharts files contain monthly emissions, is that considered?
- the files contain 18 different model ensembles, I think it would be great to be able to take a mean of them instead of picking a random ensemble
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T13:43:17Z
user: login: lionel42
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T13:54:39Z
user: login: lionel42
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T13:55:14Z
user: login: lionel42
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T13:55:52Z
user: login: lbernet
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T14:18:42Z
user: login: lbernet
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T14:26:53Z
user: login: lbernet
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T14:27:55Z
user: login: lionel42
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T14:30:02Z
user: login: lionel42
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T14:37:48Z
user: login: lbernet
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-06T09:23:34Z

Review comments:
diff_hunk: @@ -0,0 +1,100 @@
+from os import PathLike
+
+import geopandas as gpd
+import pandas as pd
+import xarray as xr
+
+from emiproc.grids import RegularGrid
+from emiproc.inventories import Inventory
+from emiproc.profiles.temporal.composite import CompositeTemporalProfiles
+from emiproc.profiles.temporal.profiles import MounthsProfile
+from emiproc.profiles.utils import ratios_dataarray_to_profiles
+from emiproc.utilities import DAY_PER_YR
+
+
+class WetCHARTs(Inventory):
+    """WetCHARTs inventory.
+
+    This class is used to read the WetCHARTs file into an inventory.
+
+    More information about the WetCHARTs is available at:
+    https://daac.ornl.gov/CMS/guides/MonthlyWetland_CH4_WetCHARTsV2.html
+
+
+    :param wetcharts_file: Path to the WetCHARTs file.
+        It can be downloaded from the link above, after creating an account.
+    :param model: Model number to select from the dataset.
+        Few keys are available. To see the available models, please check
+        the data specification in the link above.
+    :param category: Category name to use in the inventory.
+    :param substance: Substance name to use in the inventory.
+
+    """
+
+    def __init__(
+        self,
+        wetcharts_file: PathLike,
+        model: int = 2924,
path: emiproc/inventories/wetcharts.py
user: login: lbernet
body: would it be possible to use the mean of all models instead of selecting a single model? 
created_at: 2025-05-05T13:32:17Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,21 @@
+"""Read WetCHARTs inventory and plot it."""
+
+from emiproc.inventories.wetcharts import WetCHARTs
+from emiproc.plots import plot_inventory
+from emiproc import FILES_DIR
+import xarray as xr
+
+
+# Download the file and change the path here or anywhere you want.
+file_path = FILES_DIR / "wetcharts" / "WetCHARTs_v1_3_3_2021.nc"
path: examples/inventories/wetcharts.py
user: login: lbernet
body: do I understand correctly that I would need to download the correct file manually? Wouldn't it make sense to include a download function?
created_at: 2025-05-05T13:37:24Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,21 @@
+"""Read WetCHARTs inventory and plot it."""
+
+from emiproc.inventories.wetcharts import WetCHARTs
+from emiproc.plots import plot_inventory
+from emiproc import FILES_DIR
+import xarray as xr
+
+
+# Download the file and change the path here or anywhere you want.
+file_path = FILES_DIR / "wetcharts" / "WetCHARTs_v1_3_3_2021.nc"
+
+inv = WetCHARTs(
path: examples/inventories/wetcharts.py
user: login: lbernet
body: one thing is not clear to me: the wetchart data includes monthly emissions. However, it looks like it is an annual value when it is read in as in inventory. Is/can that be considered?
created_at: 2025-05-05T13:41:33Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,21 @@
+"""Read WetCHARTs inventory and plot it."""
+
+from emiproc.inventories.wetcharts import WetCHARTs
+from emiproc.plots import plot_inventory
+from emiproc import FILES_DIR
+import xarray as xr
+
+
+# Download the file and change the path here or anywhere you want.
+file_path = FILES_DIR / "wetcharts" / "WetCHARTs_v1_3_3_2021.nc"
+
+inv = WetCHARTs(
path: examples/inventories/wetcharts.py
user: login: lionel42
body: Yes this is considered, a total value is calculated, but then a mounthly profile is created which says how the emissions are distributed over the months for each cell.
created_at: 2025-05-05T13:54:39Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,21 @@
+"""Read WetCHARTs inventory and plot it."""
+
+from emiproc.inventories.wetcharts import WetCHARTs
+from emiproc.plots import plot_inventory
+from emiproc import FILES_DIR
+import xarray as xr
+
+
+# Download the file and change the path here or anywhere you want.
+file_path = FILES_DIR / "wetcharts" / "WetCHARTs_v1_3_3_2021.nc"
path: examples/inventories/wetcharts.py
user: login: lionel42
body: Yes, the problem is you need to log in to get access to the file, so I don't know how we could automate the download.
created_at: 2025-05-05T13:55:14Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,100 @@
+from os import PathLike
+
+import geopandas as gpd
+import pandas as pd
+import xarray as xr
+
+from emiproc.grids import RegularGrid
+from emiproc.inventories import Inventory
+from emiproc.profiles.temporal.composite import CompositeTemporalProfiles
+from emiproc.profiles.temporal.profiles import MounthsProfile
+from emiproc.profiles.utils import ratios_dataarray_to_profiles
+from emiproc.utilities import DAY_PER_YR
+
+
+class WetCHARTs(Inventory):
+    """WetCHARTs inventory.
+
+    This class is used to read the WetCHARTs file into an inventory.
+
+    More information about the WetCHARTs is available at:
+    https://daac.ornl.gov/CMS/guides/MonthlyWetland_CH4_WetCHARTsV2.html
+
+
+    :param wetcharts_file: Path to the WetCHARTs file.
+        It can be downloaded from the link above, after creating an account.
+    :param model: Model number to select from the dataset.
+        Few keys are available. To see the available models, please check
+        the data specification in the link above.
+    :param category: Category name to use in the inventory.
+    :param substance: Substance name to use in the inventory.
+
+    """
+
+    def __init__(
+        self,
+        wetcharts_file: PathLike,
+        model: int = 2924,
path: emiproc/inventories/wetcharts.py
user: login: lionel42
body: as I wrote below:

Not sure how meaningful this is. as I understood, this number depends on various factors and not sure if taking an average is really the way to go.

Should we ask stephan about this ?
created_at: 2025-05-05T13:55:52Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,100 @@
+from os import PathLike
+
+import geopandas as gpd
+import pandas as pd
+import xarray as xr
+
+from emiproc.grids import RegularGrid
+from emiproc.inventories import Inventory
+from emiproc.profiles.temporal.composite import CompositeTemporalProfiles
+from emiproc.profiles.temporal.profiles import MounthsProfile
+from emiproc.profiles.utils import ratios_dataarray_to_profiles
+from emiproc.utilities import DAY_PER_YR
+
+
+class WetCHARTs(Inventory):
+    """WetCHARTs inventory.
+
+    This class is used to read the WetCHARTs file into an inventory.
+
+    More information about the WetCHARTs is available at:
+    https://daac.ornl.gov/CMS/guides/MonthlyWetland_CH4_WetCHARTsV2.html
+
+
+    :param wetcharts_file: Path to the WetCHARTs file.
+        It can be downloaded from the link above, after creating an account.
+    :param model: Model number to select from the dataset.
+        Few keys are available. To see the available models, please check
+        the data specification in the link above.
+    :param category: Category name to use in the inventory.
+    :param substance: Substance name to use in the inventory.
+
+    """
+
+    def __init__(
+        self,
+        wetcharts_file: PathLike,
+        model: int = 2924,
path: emiproc/inventories/wetcharts.py
user: login: lbernet
body: I already asked Stephan, he said we would need to check in the documentation. 
I just saw that they mention there that the mean of the model ensembles was used for another model (GEOSChem), so I thought it would be best to also use the mean. 
In [this ](https://agupubs.onlinelibrary.wiley.com/doi/10.1029/2021AV000408) paper they distinguish high-performance and low-performance models for wetchart, but it is not clear which ones are the best, so I thought taking the mean is maybe a good compromise. 
Let me know if you have a better idea! :)
created_at: 2025-05-05T14:18:42Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,21 @@
+"""Read WetCHARTs inventory and plot it."""
+
+from emiproc.inventories.wetcharts import WetCHARTs
+from emiproc.plots import plot_inventory
+from emiproc import FILES_DIR
+import xarray as xr
+
+
+# Download the file and change the path here or anywhere you want.
+file_path = FILES_DIR / "wetcharts" / "WetCHARTs_v1_3_3_2021.nc"
path: examples/inventories/wetcharts.py
user: login: lbernet
body: ok, then its fine I guess. Maybe make clear that it has to be downloaded manually, since for EDGAR it is easily downloaded within emiproc. 
created_at: 2025-05-05T14:26:53Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,21 @@
+"""Read WetCHARTs inventory and plot it."""
+
+from emiproc.inventories.wetcharts import WetCHARTs
+from emiproc.plots import plot_inventory
+from emiproc import FILES_DIR
+import xarray as xr
+
+
+# Download the file and change the path here or anywhere you want.
+file_path = FILES_DIR / "wetcharts" / "WetCHARTs_v1_3_3_2021.nc"
+
+inv = WetCHARTs(
path: examples/inventories/wetcharts.py
user: login: lbernet
body: oh that sounds good! I still need to figure out how to access the temporal profiles then ;)
created_at: 2025-05-05T14:27:55Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,100 @@
+from os import PathLike
+
+import geopandas as gpd
+import pandas as pd
+import xarray as xr
+
+from emiproc.grids import RegularGrid
+from emiproc.inventories import Inventory
+from emiproc.profiles.temporal.composite import CompositeTemporalProfiles
+from emiproc.profiles.temporal.profiles import MounthsProfile
+from emiproc.profiles.utils import ratios_dataarray_to_profiles
+from emiproc.utilities import DAY_PER_YR
+
+
+class WetCHARTs(Inventory):
+    """WetCHARTs inventory.
+
+    This class is used to read the WetCHARTs file into an inventory.
+
+    More information about the WetCHARTs is available at:
+    https://daac.ornl.gov/CMS/guides/MonthlyWetland_CH4_WetCHARTsV2.html
+
+
+    :param wetcharts_file: Path to the WetCHARTs file.
+        It can be downloaded from the link above, after creating an account.
+    :param model: Model number to select from the dataset.
+        Few keys are available. To see the available models, please check
+        the data specification in the link above.
+    :param category: Category name to use in the inventory.
+    :param substance: Substance name to use in the inventory.
+
+    """
+
+    def __init__(
+        self,
+        wetcharts_file: PathLike,
+        model: int = 2924,
path: emiproc/inventories/wetcharts.py
user: login: lionel42
body: Okay good, I implemented that we can use the mean if no model is specified
created_at: 2025-05-05T14:30:02Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,21 @@
+"""Read WetCHARTs inventory and plot it."""
+
+from emiproc.inventories.wetcharts import WetCHARTs
+from emiproc.plots import plot_inventory
+from emiproc import FILES_DIR
+import xarray as xr
+
+
+# Download the file and change the path here or anywhere you want.
+file_path = FILES_DIR / "wetcharts" / "WetCHARTs_v1_3_3_2021.nc"
+
+inv = WetCHARTs(
path: examples/inventories/wetcharts.py
user: login: lionel42
body: Maybe we can discuss what output format you need exactly.

for the moment you can use the following function in python to have emissions over time:

```
from emiproc.exports.utils import get_temporally_scaled_array

da = get_temporally_scaled_array(inv, time_range=inv.year, sum_over_cells=False)

da.sel(category='wetland_emissions', substance='CH4').sum(dim='cell').plot()

```
created_at: 2025-05-05T14:37:48Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,21 @@
+"""Read WetCHARTs inventory and plot it."""
+
+from emiproc.inventories.wetcharts import WetCHARTs
+from emiproc.plots import plot_inventory
+from emiproc import FILES_DIR
+import xarray as xr
+
+
+# Download the file and change the path here or anywhere you want.
+file_path = FILES_DIR / "wetcharts" / "WetCHARTs_v1_3_3_2021.nc"
+
+inv = WetCHARTs(
path: examples/inventories/wetcharts.py
user: login: lbernet
body: I would need monhtly maps, including lon-and lat coordiantes. 


created_at: 2025-05-06T09:23:34Z
author_association: COLLABORATOR

PR comments:
user: login: lionel42
created_at: 2025-05-05T11:09:49Z
body: @lbernet  please try this and let me know, the doc for how to use is in the docstring
author_association: COLLABORATOR
user: login: lionel42
created_at: 2025-05-05T13:51:20Z
body: 
> 
>     * the downloaded wetcharts files contain monthly emissions, is that considered?
> 

Yes, there is a mounthly profiles which is created and defines how the emissions are distributed over the year.

The total emissions is the sum over the time


author_association: COLLABORATOR
user: login: lionel42
created_at: 2025-05-05T13:53:19Z
body: 
> 
>     * the files contain 18 different model ensembles, I think it would be great to be able to take a mean of them instead of picking a random ensemble


Not sure how meaningful this is. as I understood, this number depends on various factors and not sure if taking an average is really the way to go.

Should we ask stephan about this  ?

author_association: COLLABORATOR