Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix xarray-compatible Zarr generation for DegreeDays indicators #145

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 23 additions & 47 deletions src/hazard/models/degree_days.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,10 @@
MultiYearAverageIndicatorBase,
ThresholdBasedAverageIndicator,
)
from hazard.sources.osc_zarr import OscZarr
from hazard.protocols import OpenDataset, ReadWriteDataArray, WriteDataArray
from hazard.utilities.description_utilities import get_indicator_period_descriptions
from hazard.utilities.map_utilities import (
check_map_bounds,
transform_epsg4326_to_epsg3857,
)
from hazard.utilities.tiles import create_tiles_for_resource
from hazard.utilities.xarray_utilities import enforce_conventions_lat_lon

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -111,12 +109,15 @@ def _resource(self):
continue
scenarios.append(Scenario(id=s, years=list(self.central_years)))

path = (
f"chronic_heat/osc/v2/mean_degree_days_v2_above_{self.threshold_c}c"
+ "_{gcm}_{scenario}_{year}"
)
resource = HazardResource(
hazard_type="ChronicHeat",
indicator_id=f"mean_degree_days/above/{self.threshold_c}c",
indicator_model_gcm="{gcm}",
path=f"chronic_heat/osc/v2/mean_degree_days_v2_above_{self.threshold_c}c"
+ "_{gcm}_{scenario}_{year}",
path=path,
display_name=f"Mean degree days above {self.threshold_c}°C/" + "{gcm}",
description=description,
params={"gcm": list(self.gcms)},
Expand All @@ -134,9 +135,8 @@ def _resource(self):
),
bounds=[(-180.0, 85.0), (180.0, 85.0), (180.0, -60.0), (-180.0, -60.0)],
bbox=[-180.0, -60.0, 180.0, 85.0],
path=f"mean_degree_days_v2_above_{self.threshold_c}c_"
+ "{gcm}_{scenario}_{year}_map",
source="map_array",
path="maps/" + path,
source="map_array_pyramid",
),
units="degree days",
scenarios=scenarios,
Expand Down Expand Up @@ -178,35 +178,13 @@ def run_single(
pp = self._item_path(item)
logger.info(f"Writing array to {str(pp)}")
target.write(str(pp), average_deg_days)
pp = self._item_path(item)
pp_map = pp.with_name(pp.name + "_map")
self._generate_map(
str(pp),
str(pp_map),
item.resource.map.bounds if item.resource.map is not None else None,
target,
)
return

def _generate_map(
self,
path: str,
map_path: str,
bounds: Optional[List[Tuple[float, float]]],
target: ReadWriteDataArray,
):
logger.info(f"Generating map projection for file {path}; reading file")
da = target.read(path)
logger.info("Reprojecting to EPSG:3857")
# reprojected = transform_epsg4326_to_epsg3857(average_deg_days.sel(latitude=slice(85, -85)))
reprojected = transform_epsg4326_to_epsg3857(da)
# sanity check bounds:
(top, right, bottom, left) = check_map_bounds(reprojected)
if top > 85.05 or bottom < -85.05:
raise ValueError("invalid range")
logger.info(f"Writing map file {map_path}")
target.write(map_path, reprojected, spatial_coords=False)
return
def create_maps(self, source: OscZarr, target: OscZarr):
"""
Create map images.
"""
for resource in self.inventory():
create_tiles_for_resource(source, target, resource)

def _average_degree_days(
self,
Expand Down Expand Up @@ -386,16 +364,16 @@ def _resource(self) -> Dict[str, HazardResource]: # type: ignore[override]
os.path.join(os.path.dirname(__file__), "degree_days.md"), "r"
) as f:
description = f.read()
path = (
f"chronic_heat/osc/v2/mean_degree_days_{above_below}"
+ "_index_{gcm}_{scenario}_{year}"
)
resource = HazardResource(
hazard_type="ChronicHeat",
indicator_id=f"mean_degree_days/{above_below}/index",
indicator_model_gcm="{gcm}",
path="chronic_heat/osc/v2/mean_degree_days_"
+ f"{above_below}"
+ "_index_{gcm}_{scenario}_{year}",
display_name="Mean degree days "
+ f"{above_below}"
+ " index value/{gcm}",
path=path,
display_name=f"Mean degree days {above_below}" + " index value/{gcm}",
description=description,
params={"gcm": list(self.gcms)},
group_id="",
Expand All @@ -417,10 +395,8 @@ def _resource(self) -> Dict[str, HazardResource]: # type: ignore[override]
(-180.0, -60.0),
],
index_values=self.threshold_temps_c,
path="mean_degree_days_"
+ f"{above_below}"
+ "_index_{gcm}_{scenario}_{year}_map",
source="map_array",
path="maps/" + path,
source="map_array_pyramid",
),
units="degree days",
scenarios=[
Expand Down
15 changes: 6 additions & 9 deletions src/hazard/sources/osc_zarr.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logging
import os
from pathlib import PurePosixPath
from typing import Any, Dict, List, Optional, Tuple, Union
Expand All @@ -14,6 +15,8 @@

default_dev_bucket = "physrisk-hazard-indicators-dev01"

logger = logging.getLogger(__name__)


class OscZarr(ReadWriteDataArray):
__access_key = "OSC_S3_ACCESS_KEY_DEV"
Expand Down Expand Up @@ -150,13 +153,8 @@ def write(
):
if self.write_xarray_compatible_zarr and spatial_coords:
pp = PurePosixPath(path)
if da.name != pp.name:
raise ValueError(
f"when writing NetCDF style coordinates, final element of path (here {pp.name}) must be \
the same as the array name (here {da.name})"
)
parent_path = pp.parent
self.write_data_array(str(parent_path), da)
da.name = pp.name
self.write_data_array(str(pp.parent), da)
else:
self.write_zarr(path, da, chunks)

Expand Down Expand Up @@ -230,7 +228,6 @@ def write_data_array(self, path: str, da: xr.DataArray):
path (str): Relative path.
da (xr.DataArray): The DataArray.
"""
# we expect the data to be called 'data'
if "lon" not in da.dims and "longitude" not in da.dims and "x" not in da.dims:
raise ValueError("longitude or x dimension not found.")
if "lat" not in da.dims and "latitude" not in da.dims and "y" not in da.dims:
Expand All @@ -253,7 +250,7 @@ def write_data_array(self, path: str, da: xr.DataArray):
self.root.store,
compute=True,
group=path,
mode="w",
mode="a",
consolidated=False,
encoding={da_norm.name: options},
)
Expand Down