Skip to content

wrfchem

WRF-Chem Reader

WRFChemReader

Bases: GriddedReader

Reader for WRF-Chem and RAP-Chem model output files.

Source code in monetio/readers/wrfchem.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
@register_reader("wrfchem")
class WRFChemReader(GriddedReader):
    """
    Reader for WRF-Chem and RAP-Chem model output files.
    """

    def open_dataset(
        self,
        files: str | list[str],
        convert_to_ppb: bool = True,
        mech: str = "racm_esrl_vcp",
        var_list: list[str] | None = None,
        surf_only: bool = False,
        surf_only_nc: bool = False,
        **kwargs: Any,
    ) -> xr.Dataset:
        """
        Reads WRF-Chem netCDF files.

        Parameters
        ----------
        files : Union[str, List[str]]
            File path, list of paths, or glob pattern.
        convert_to_ppb : bool, optional
            Convert gas species from ppmV to ppbV, by default True.
        mech : str, optional
            Mechanism for calculating sums, by default "racm_esrl_vcp".
        var_list : list of str, optional
            List of variables to include, by default None.
        surf_only : bool, optional
            Whether to only keep surface data, by default False.
        surf_only_nc : bool, optional
            Whether input data already contains only surface data, by default False.
        **kwargs : Any
            Additional arguments passed to the driver.

        Returns
        -------
        xarray.Dataset
            The processed WRF-Chem dataset.
        """
        if "preprocess" not in kwargs:
            kwargs["preprocess"] = partial(
                wrfchem_preprocess,
                convert_to_ppb=convert_to_ppb,
                mech=mech,
                var_list=var_list,
                surf_only=surf_only,
                surf_only_nc=surf_only_nc,
            )

        if "combine" not in kwargs:
            kwargs["combine"] = "nested"
        if "concat_dim" not in kwargs:
            kwargs["concat_dim"] = "time"

        ds = self.driver.open(files, **kwargs)

        ds = self.harmonize(ds)

        # Update history
        ds = update_history(ds, "Read WRF-Chem data.")

        return ds

open_dataset(files, convert_to_ppb=True, mech='racm_esrl_vcp', var_list=None, surf_only=False, surf_only_nc=False, **kwargs)

Reads WRF-Chem netCDF files.

Parameters:

Name Type Description Default
files Union[str, List[str]]

File path, list of paths, or glob pattern.

required
convert_to_ppb bool

Convert gas species from ppmV to ppbV, by default True.

True
mech str

Mechanism for calculating sums, by default "racm_esrl_vcp".

'racm_esrl_vcp'
var_list list of str

List of variables to include, by default None.

None
surf_only bool

Whether to only keep surface data, by default False.

False
surf_only_nc bool

Whether input data already contains only surface data, by default False.

False
**kwargs Any

Additional arguments passed to the driver.

{}

Returns:

Type Description
Dataset

The processed WRF-Chem dataset.

Source code in monetio/readers/wrfchem.py
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
def open_dataset(
    self,
    files: str | list[str],
    convert_to_ppb: bool = True,
    mech: str = "racm_esrl_vcp",
    var_list: list[str] | None = None,
    surf_only: bool = False,
    surf_only_nc: bool = False,
    **kwargs: Any,
) -> xr.Dataset:
    """
    Reads WRF-Chem netCDF files.

    Parameters
    ----------
    files : Union[str, List[str]]
        File path, list of paths, or glob pattern.
    convert_to_ppb : bool, optional
        Convert gas species from ppmV to ppbV, by default True.
    mech : str, optional
        Mechanism for calculating sums, by default "racm_esrl_vcp".
    var_list : list of str, optional
        List of variables to include, by default None.
    surf_only : bool, optional
        Whether to only keep surface data, by default False.
    surf_only_nc : bool, optional
        Whether input data already contains only surface data, by default False.
    **kwargs : Any
        Additional arguments passed to the driver.

    Returns
    -------
    xarray.Dataset
        The processed WRF-Chem dataset.
    """
    if "preprocess" not in kwargs:
        kwargs["preprocess"] = partial(
            wrfchem_preprocess,
            convert_to_ppb=convert_to_ppb,
            mech=mech,
            var_list=var_list,
            surf_only=surf_only,
            surf_only_nc=surf_only_nc,
        )

    if "combine" not in kwargs:
        kwargs["combine"] = "nested"
    if "concat_dim" not in kwargs:
        kwargs["concat_dim"] = "time"

    ds = self.driver.open(files, **kwargs)

    ds = self.harmonize(ds)

    # Update history
    ds = update_history(ds, "Read WRF-Chem data.")

    return ds

wrfchem_preprocess(ds, *, convert_to_ppb=True, mech='racm_esrl_vcp', var_list=None, surf_only=False, surf_only_nc=False)

Preprocess function for a single WRF-Chem file.

Parameters:

Name Type Description Default
ds Dataset

Input WRF-Chem dataset.

required
convert_to_ppb bool

Whether to convert gas species to ppbV, by default True.

True
mech str

Mechanism for diagnostics, by default "racm_esrl_vcp".

'racm_esrl_vcp'
var_list list of str

List of variables to keep, by default None.

None
surf_only bool

Keep only surface layer, by default False.

False
surf_only_nc bool

Whether input is already surface-only, by default False.

False

Returns:

Type Description
Dataset

The preprocessed dataset.

Source code in monetio/readers/wrfchem.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
def wrfchem_preprocess(
    ds: xr.Dataset,
    *,
    convert_to_ppb: bool = True,
    mech: str = "racm_esrl_vcp",
    var_list: list[str] | None = None,
    surf_only: bool = False,
    surf_only_nc: bool = False,
) -> xr.Dataset:
    """
    Preprocess function for a single WRF-Chem file.

    Parameters
    ----------
    ds : xarray.Dataset
        Input WRF-Chem dataset.
    convert_to_ppb : bool, optional
        Whether to convert gas species to ppbV, by default True.
    mech : str, optional
        Mechanism for diagnostics, by default "racm_esrl_vcp".
    var_list : list of str, optional
        List of variables to keep, by default None.
    surf_only : bool, optional
        Keep only surface layer, by default False.
    surf_only_nc : bool, optional
        Whether input is already surface-only, by default False.

    Returns
    -------
    xarray.Dataset
        The preprocessed dataset.
    """
    # 1. Coordinate and Dimension Renaming
    rename_dict = {
        "Time": "time",
        "south_north": "y",
        "west_east": "x",
        "XLONG": "longitude",
        "XLAT": "latitude",
        "bottom_top": "z",
        "bottom_top_stag": "z_stag",
        "soil_layers_stag": "z_soil",
    }
    # Only rename what exists
    actual_rename = {}
    for k, v in rename_dict.items():
        if k in ds.variables or k in ds.dims:
            if v in ds.dims and k != v:
                # Target exists, maybe it's already renamed or there's a conflict
                continue
            actual_rename[k] = v

    if actual_rename:
        ds = ds.rename(actual_rename)

    # 2. Lazy Time Parsing
    if "Times" in ds.variables:
        ds = _parse_wrf_times(ds)

    # 3. Unit Conversions (Lazy) - Move before diagnostics to ensure synced units
    if convert_to_ppb:
        ds = _convert_to_ppb(ds)

    # convert "ug/kg-dryair -> ug/m3" if density can be calculated
    ds = _convert_ugkg_to_ugm3(ds)

    # 4. Add lazy diagnostic variables
    for name, spec in DIAGNOSTICS.items():
        ds = add_lazy_diagnostic(ds, name, spec)

    # 5. Subset variables if requested
    if var_list is not None:
        # We must keep coordinates and some essentials
        essentials = ["latitude", "longitude", "time", "z", "z_stag", "z_soil"]
        to_keep = set(var_list) | set(essentials)
        # Add those that were added as diagnostics
        to_keep |= {name for name in DIAGNOSTICS if name in ds.variables}
        available = [v for v in ds.variables if v in to_keep]
        ds = ds[available]

    # 6. Handle Surface Only
    if surf_only and not surf_only_nc and "z" in ds.dims:
        ds = ds.isel(z=[0])

    # 7. Scientific Hygiene
    ds = _scientific_hygiene(ds)

    # Update history
    ds = update_history(ds, "Preprocessed WRF-Chem data.")

    return ds