Skip to content

eprofile

E-PROFILE (European ALC network) Reader

EPROFILEReader

Bases: GriddedReader

Reader for E-PROFILE (European ALC network) NetCDF data.

Source code in monetio/readers/eprofile.py
12
13
14
15
16
17
18
19
20
21
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
@register_reader("eprofile")
class EPROFILEReader(GriddedReader):
    """
    Reader for E-PROFILE (European ALC network) NetCDF data.
    """

    def open_dataset(
        self,
        files: str | list[str],
        dates: pd.DatetimeIndex | list | pd.Timestamp | str | None = None,
        **kwargs,
    ) -> xr.Dataset:
        """
        Retrieve and load E-PROFILE data.

        Parameters
        ----------
        files : Union[str, List[str]]
            File path, list of paths, or glob pattern.
        dates : Optional[Union[pd.DatetimeIndex, List, pd.Timestamp, str]], optional
            Dates to retrieve if files are not provided (not yet implemented for E-PROFILE), by default None.
        **kwargs : dict
            Additional arguments passed to xarray.open_mfdataset.

        Returns
        -------
        xr.Dataset
            The loaded E-PROFILE data.
        """
        if files is None and dates is not None:
            # Placeholder for build_urls if E-PROFILE hub provides a predictable URL pattern
            # For now, we expect files to be provided.
            raise NotImplementedError("Retrieval from dates is not yet implemented for E-PROFILE.")

        # Default to combined dimensions if not specified
        kwargs.setdefault("combine", "by_coords")

        # Use XarrayDriver (via GriddedReader) to open
        ds = super().open_dataset(files, preprocess=eprofile_preprocess, **kwargs)

        return ds

open_dataset(files, dates=None, **kwargs)

Retrieve and load E-PROFILE data.

Parameters:

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

File path, list of paths, or glob pattern.

required
dates Optional[Union[DatetimeIndex, List, Timestamp, str]]

Dates to retrieve if files are not provided (not yet implemented for E-PROFILE), by default None.

None
**kwargs dict

Additional arguments passed to xarray.open_mfdataset.

{}

Returns:

Type Description
Dataset

The loaded E-PROFILE data.

Source code in monetio/readers/eprofile.py
18
19
20
21
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
def open_dataset(
    self,
    files: str | list[str],
    dates: pd.DatetimeIndex | list | pd.Timestamp | str | None = None,
    **kwargs,
) -> xr.Dataset:
    """
    Retrieve and load E-PROFILE data.

    Parameters
    ----------
    files : Union[str, List[str]]
        File path, list of paths, or glob pattern.
    dates : Optional[Union[pd.DatetimeIndex, List, pd.Timestamp, str]], optional
        Dates to retrieve if files are not provided (not yet implemented for E-PROFILE), by default None.
    **kwargs : dict
        Additional arguments passed to xarray.open_mfdataset.

    Returns
    -------
    xr.Dataset
        The loaded E-PROFILE data.
    """
    if files is None and dates is not None:
        # Placeholder for build_urls if E-PROFILE hub provides a predictable URL pattern
        # For now, we expect files to be provided.
        raise NotImplementedError("Retrieval from dates is not yet implemented for E-PROFILE.")

    # Default to combined dimensions if not specified
    kwargs.setdefault("combine", "by_coords")

    # Use XarrayDriver (via GriddedReader) to open
    ds = super().open_dataset(files, preprocess=eprofile_preprocess, **kwargs)

    return ds

eprofile_preprocess(ds)

Preprocess E-PROFILE dataset: standardize coordinates and dimensions.

Parameters:

Name Type Description Default
ds Dataset

Input E-PROFILE dataset.

required

Returns:

Type Description
Dataset

Preprocessed dataset.

Source code in monetio/readers/eprofile.py
 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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
def eprofile_preprocess(ds: xr.Dataset) -> xr.Dataset:
    """
    Preprocess E-PROFILE dataset: standardize coordinates and dimensions.

    Parameters
    ----------
    ds : xr.Dataset
        Input E-PROFILE dataset.

    Returns
    -------
    xr.Dataset
        Preprocessed dataset.
    """
    # 1. Standardize Dimensions and Coordinates
    # Common ALC NetCDF dimensions: 'time', 'range', 'level', 'layer', 'timeDim'
    rename_dims = {}
    if "timeDim" in ds.dims:
        rename_dims["timeDim"] = "time"
    if "range" in ds.dims:
        # Keep range as dim, but we might want a 1D coord for it if it's not there
        pass

    if rename_dims:
        ds = ds.rename(rename_dims)

    # 2. Standardize Variable Names
    rename_vars = {}
    # Attenuated backscatter
    for v in ["beta", "backscatter", "attenuated_backscatter_coefficient"]:
        if v in ds.variables and "attenuated_backscatter" not in ds.variables:
            rename_vars[v] = "attenuated_backscatter"

    # Station Metadata
    if "station_altitude" in ds.variables and "elevation" not in ds.variables:
        rename_vars["station_altitude"] = "elevation"
    if "station_latitude" in ds.variables and "latitude" not in ds.variables:
        rename_vars["station_latitude"] = "latitude"
    if "station_longitude" in ds.variables and "longitude" not in ds.variables:
        rename_vars["station_longitude"] = "longitude"

    if rename_vars:
        ds = ds.rename_vars(rename_vars)

    # 3. Coordinate Handling
    # Ensure latitude and longitude are coordinates
    coord_vars = ["latitude", "longitude", "elevation", "time", "range"]
    actual_coords = [v for v in coord_vars if v in ds.variables]
    if actual_coords:
        ds = ds.set_coords(actual_coords)

    # 4. Standard Units and Attributes
    if "latitude" in ds.coords:
        ds["latitude"].attrs.update({"units": "degrees_north", "standard_name": "latitude"})
    if "longitude" in ds.coords:
        ds["longitude"].attrs.update({"units": "degrees_east", "standard_name": "longitude"})
    if "elevation" in ds.coords:
        ds["elevation"].attrs.update({"units": "m", "standard_name": "height_above_mean_sea_level"})

    # 5. Vertical Coordinate Calculation
    # If we have elevation (station height) and range, calculate altitude (absolute height)
    if "range" in ds.variables and "elevation" in ds.coords:
        if "altitude" not in ds.variables:
            # We assume range is in meters. If it has units km, we should convert.
            range_da = ds["range"]
            if range_da.attrs.get("units") == "km":
                range_da = range_da * 1000.0

            ds["altitude"] = ds["elevation"] + range_da
            ds["altitude"].attrs.update(
                {"units": "m", "standard_name": "altitude", "positive": "up"}
            )
            ds = ds.set_coords("altitude")

    # Update history
    ds = update_history(ds, "Preprocessed E-PROFILE data.")

    return ds