|
4 | 4 |
|
5 | 5 | import xarray as xr |
6 | 6 |
|
| 7 | +import ilamb3 |
7 | 8 | import ilamb3.dataset as dset |
8 | 9 | from ilamb3.transform.base import ILAMBTransform |
9 | 10 |
|
@@ -35,34 +36,43 @@ def __call__(self, ds: xr.Dataset) -> xr.Dataset: |
35 | 36 | def _to_vol_fraction(ds: xr.Dataset, varname: str) -> xr.DataArray: |
36 | 37 | """ |
37 | 38 | Convert the array from a mass area density to a volume fraction. |
| 39 | +
|
| 40 | + Note |
| 41 | + ---- |
| 42 | + As of xarray 2025.10.1, pint 0.25, pint-xarray 0.6.0, quantifying the |
| 43 | + dataarray here changes the depth dimension data yielding differences in the |
| 44 | + dimension on the order (1e-8). Here we avoid using quantify to circumvent |
| 45 | + this issue. |
38 | 46 | """ |
39 | 47 | da = ds[varname] |
40 | | - da = da.pint.quantify() |
41 | | - ureg = da.pint.registry |
42 | | - quantity = 1.0 * da.pint.units |
| 48 | + ureg = ilamb3.units |
| 49 | + quantity = ureg(da.attrs["units"]) |
43 | 50 | if quantity.check(""): |
44 | | - return da.pint.dequantify() |
| 51 | + # units are already compatible with volume fraction |
| 52 | + return da |
45 | 53 | if not quantity.check("[mass] / [length]**2"): |
46 | | - raise ValueError("Cannot convert array of this type.") |
| 54 | + raise ValueError( |
| 55 | + f"Cannot convert a variable with units of this type {da.pint.units}." |
| 56 | + ) |
47 | 57 | if dset.is_layered(da): |
48 | | - depth_name = dset.get_dim_name(da, "depth") |
49 | | - depth = da[depth_name] |
50 | | - if "bounds" not in depth.attrs: |
51 | | - raise ValueError( |
52 | | - "Cannot convert soil mositure data when depth bounds are not coordinates." |
53 | | - ) |
54 | | - depth_name = depth.attrs["bounds"] |
55 | | - if depth_name not in ds: |
| 58 | + depth_dim = dset.get_dim_name(da, "depth") |
| 59 | + depth = ds[depth_dim] |
| 60 | + bnd_name = depth.attrs["bounds"] if "bounds" in depth.attrs else None |
| 61 | + if not dset.has_bounds(ds, depth_dim, bnd_name): |
56 | 62 | raise ValueError( |
57 | 63 | "Cannot convert soil mositure data when depth bounds are not coordinates." |
58 | 64 | ) |
59 | | - depth = ds[depth_name] * da.pint.registry.meter |
60 | | - da = da / depth.diff(dim=depth.dims[-1]) |
| 65 | + depth = ds[bnd_name] |
| 66 | + interval_dim = set(depth.dims).difference([depth_dim]).pop() |
| 67 | + da = ( |
| 68 | + da / depth.diff(dim=interval_dim).squeeze() |
| 69 | + ) # <-- this was failing, see Note |
| 70 | + da = da.pint.quantify() / ureg.meter |
61 | 71 | else: |
62 | 72 | # If not layered, we are assuming this is mrsos which is the moisture in |
63 | 73 | # the top 10cm |
64 | | - depth = 0.1 * da.pint.registry.meter |
65 | | - da = da / depth |
| 74 | + depth = 0.1 * ureg.meter |
| 75 | + da = da.pint.quantify() / depth |
66 | 76 | water_density = 998.2071 * ureg.kilogram / ureg.meter**3 |
67 | 77 | da = (da / water_density).pint.to("dimensionless") |
68 | 78 | da = da.pint.dequantify() |
|
0 commit comments