Skip to content

camx

CAMx Reader

CAMxReader

Bases: GriddedReader

Reader for CAMx model output files.

Source code in monetio/readers/camx.py
 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
 86
 87
 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
@register_reader("camx")
class CAMxReader(GriddedReader):
    """
    Reader for CAMx model output files.
    """

    def open_dataset(
        self,
        files: str | list[str],
        earth_radius: float = 6370000,
        convert_to_ppb: bool = True,
        drop_duplicates: bool = False,
        **kwargs: Any,
    ) -> xr.Dataset:
        """
        Reads CAMx netCDF files.

        Parameters
        ----------
        files : Union[str, List[str]]
            File path, list of paths, or glob pattern.
        earth_radius : float, optional
            Earth radius in meters, by default 6370000.
        convert_to_ppb : bool, optional
            Convert gas species from ppmV to ppbV, by default True.
        drop_duplicates : bool, optional
            Drop duplicate time steps within each file, by default False.
        **kwargs : Any
            Additional arguments passed to the driver.

        Returns
        -------
        xarray.Dataset
            The processed CAMx dataset.
        """
        # Set default backend kwargs for CAMx if not present
        if "engine" not in kwargs:
            kwargs["engine"] = "pseudonetcdf"
            if "backend_kwargs" not in kwargs:
                kwargs["backend_kwargs"] = {"format": "uamiv"}

        # 1. Setup preprocessing
        if "preprocess" not in kwargs:
            kwargs["preprocess"] = partial(
                camx_preprocess,
                earth_radius=earth_radius,
                convert_to_ppb=convert_to_ppb,
            )

        # 2. Open the dataset using standard xarray (via XarrayDriver)
        if "combine" not in kwargs:
            kwargs["combine"] = "nested"
        if "concat_dim" not in kwargs:
            kwargs["concat_dim"] = "time"

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

        # 3. Finalize
        if drop_duplicates:
            ds = ds.drop_duplicates("time")
            ds = update_history(ds, "Dropped duplicate time steps.")

        ds = self.harmonize(ds)

        # Update history
        ds = update_history(ds, "Read CAMx data.")

        return ds

    def harmonize(self, ds: xr.Dataset) -> xr.Dataset:
        """
        Standardize variable names and metadata.

        Parameters
        ----------
        ds : xr.Dataset
            CAMx dataset.

        Returns
        -------
        xr.Dataset
            Harmonized dataset.
        """
        # 1. Standardize variable names and drop redundant ones
        ds = _harmonize_ioapi_vars(ds)

        # 2. Clean up attributes
        ds = _scientific_hygiene(ds)

        # Update history
        ds = update_history(ds, "Harmonized CAMx dataset.")

        return ds

harmonize(ds)

Standardize variable names and metadata.

Parameters:

Name Type Description Default
ds Dataset

CAMx dataset.

required

Returns:

Type Description
Dataset

Harmonized dataset.

Source code in monetio/readers/camx.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def harmonize(self, ds: xr.Dataset) -> xr.Dataset:
    """
    Standardize variable names and metadata.

    Parameters
    ----------
    ds : xr.Dataset
        CAMx dataset.

    Returns
    -------
    xr.Dataset
        Harmonized dataset.
    """
    # 1. Standardize variable names and drop redundant ones
    ds = _harmonize_ioapi_vars(ds)

    # 2. Clean up attributes
    ds = _scientific_hygiene(ds)

    # Update history
    ds = update_history(ds, "Harmonized CAMx dataset.")

    return ds

open_dataset(files, earth_radius=6370000, convert_to_ppb=True, drop_duplicates=False, **kwargs)

Reads CAMx netCDF files.

Parameters:

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

File path, list of paths, or glob pattern.

required
earth_radius float

Earth radius in meters, by default 6370000.

6370000
convert_to_ppb bool

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

True
drop_duplicates bool

Drop duplicate time steps within each file, by default False.

False
**kwargs Any

Additional arguments passed to the driver.

{}

Returns:

Type Description
Dataset

The processed CAMx dataset.

Source code in monetio/readers/camx.py
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
86
87
88
89
90
91
92
def open_dataset(
    self,
    files: str | list[str],
    earth_radius: float = 6370000,
    convert_to_ppb: bool = True,
    drop_duplicates: bool = False,
    **kwargs: Any,
) -> xr.Dataset:
    """
    Reads CAMx netCDF files.

    Parameters
    ----------
    files : Union[str, List[str]]
        File path, list of paths, or glob pattern.
    earth_radius : float, optional
        Earth radius in meters, by default 6370000.
    convert_to_ppb : bool, optional
        Convert gas species from ppmV to ppbV, by default True.
    drop_duplicates : bool, optional
        Drop duplicate time steps within each file, by default False.
    **kwargs : Any
        Additional arguments passed to the driver.

    Returns
    -------
    xarray.Dataset
        The processed CAMx dataset.
    """
    # Set default backend kwargs for CAMx if not present
    if "engine" not in kwargs:
        kwargs["engine"] = "pseudonetcdf"
        if "backend_kwargs" not in kwargs:
            kwargs["backend_kwargs"] = {"format": "uamiv"}

    # 1. Setup preprocessing
    if "preprocess" not in kwargs:
        kwargs["preprocess"] = partial(
            camx_preprocess,
            earth_radius=earth_radius,
            convert_to_ppb=convert_to_ppb,
        )

    # 2. Open the dataset using standard xarray (via XarrayDriver)
    if "combine" not in kwargs:
        kwargs["combine"] = "nested"
    if "concat_dim" not in kwargs:
        kwargs["concat_dim"] = "time"

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

    # 3. Finalize
    if drop_duplicates:
        ds = ds.drop_duplicates("time")
        ds = update_history(ds, "Dropped duplicate time steps.")

    ds = self.harmonize(ds)

    # Update history
    ds = update_history(ds, "Read CAMx data.")

    return ds

camx_preprocess(ds, *, earth_radius=6370000, convert_to_ppb=True)

Preprocess function for a single CAMx file.

Parameters:

Name Type Description Default
ds Dataset

Input CAMx dataset.

required
earth_radius float

Earth radius in meters, by default 6370000.

6370000
convert_to_ppb bool

Convert gas species to ppbV, by default True.

True

Returns:

Type Description
Dataset

Processed dataset.

Examples:

>>> ds = camx_preprocess(ds, convert_to_ppb=True)
Source code in monetio/readers/camx.py
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
179
180
181
182
183
184
185
186
187
def camx_preprocess(
    ds: xr.Dataset,
    *,
    earth_radius: float = 6370000,
    convert_to_ppb: bool = True,
) -> xr.Dataset:
    """
    Preprocess function for a single CAMx file.

    Parameters
    ----------
    ds : xarray.Dataset
        Input CAMx dataset.
    earth_radius : float, optional
        Earth radius in meters, by default 6370000.
    convert_to_ppb : bool, optional
        Convert gas species to ppbV, by default True.

    Returns
    -------
    xarray.Dataset
        Processed dataset.

    Examples
    --------
    >>> ds = camx_preprocess(ds, convert_to_ppb=True)
    """
    # 1. Add lazy diagnostic variables
    for name, spec in DIAGNOSTICS.items():
        ds = add_lazy_diagnostic(ds, name, spec)

    # 2. Grid and Coordinates
    grid = grid_from_dataset(ds, earth_radius=earth_radius)
    if grid:
        ds = ds.assign_attrs({"proj4_srs": grid})
        ds = _add_ioapi_latlon(ds, grid)

        # Also assign proj4_srs to all data variables for compatibility
        for var in ds.data_vars:
            ds[var].attrs["proj4_srs"] = grid

    # 3. Time
    if "TFLAG" in ds.variables:
        from .base import _get_ioapi_times

        ds = _get_ioapi_times(ds)

    # 4. Units and Formatting
    if convert_to_ppb:
        ds = _convert_to_ppb(ds)
    ds = _format_units(ds)

    # 5. Rename dimensions
    ds = _harmonize_ioapi_dims(ds)

    # 6. Predefined mapping tables (backward compatibility)
    ds = _predefined_mapping_tables(ds)

    # 7. Harmonize variables (lowercase and drop redundant)
    ds = _harmonize_ioapi_vars(ds)

    # 8. Scientific Hygiene
    ds = _scientific_hygiene(ds)

    # Update history
    ds = update_history(ds, "Preprocessed CAMx data.")

    return ds