Create 3d Greenland thermal forcing from ISMIP7-provided 2d and EN4 data - #979
Open
trhille wants to merge 12 commits into
Open
Create 3d Greenland thermal forcing from ISMIP7-provided 2d and EN4 data#979trhille wants to merge 12 commits into
trhille wants to merge 12 commits into
Conversation
Handle the OCX scenario in the ismip7_forcing test group. OCX has no distinct ESM model: it uses RACMO2.3p2-ERA (atmosphere) and EN4 (ocean) at data version v1, with a named grid resolution (ocean-1000m) in the ocean file names. get_params() now applies OCX-specific overrides (sources, versions, ocean grid token) when scenario == OCX, so the [ismip7] model option is ignored and a single config file drives both the atmosphere and ocean_thermal test cases. Adds ismip7_forcing_ocx_gis.cfg and updates the user and developer docs.
Two fixes that block processing of forcing whose remapped files or source
directories differ from the CESM test data:
- Drop the ncremap-mangled 'time_bnds' variable when combining remapped
files. ncremap collides the bounds 'nv' dimension with the destination
mesh corner dimension and pads with a fill value that overflows time
decoding. The steps build their own xtime and never use time_bnds.
- Skip input files whose trailing token is not a year (e.g. climatology
averages like *_1978-2007_avg.nc) instead of crashing on int('avg').
Restructure processed-forcing output from
{output_base_path}/atmosphere_forcing/{model}_{scenario}/ (and the analogous
ocean_thermal_forcing path) to {output_base_path}/{group}/atmosphere/ and
{output_base_path}/{group}/ocean_thermal_forcing/, matching the directory
layout the ismip7_run test group globs for. 'group' is {model}_{scenario} for
the ESM scenarios and {scenario} (i.e. OCX) for OCX, whose atmosphere and
ocean sources differ but must share one directory. Documents the layout and
how to point ismip7_run's forcing_basepath / ocx_forcing_path at it.
…lation helper process_smb.py, process_temperature.py, process_smb_gradient.py, and process_temperature_gradient.py remapped the raw source file directly with ncremap, unlike process_runoff.py, ocean_thermal, and the fracture pathway, which all extrapolate fill/missing values on the source grid first. Without that step, no-data cells near the ice-sheet margin (e.g. ocean cells in the RACMO OCX source) leak the ~9.97e36 netCDF fill sentinel into valid neighboring cells during conservative/bilinear remapping. Add the same pre-remap extrapolation to the four affected steps. Also promote the previously-duplicated extrapolate_source() (from fracture/remap_utils.py, formerly re-implemented again in ocean_thermal/process_thermal_forcing.py and process_runoff.py) to a single shared compass.landice.tests.ismip7_forcing.remap_utils module, and update all five call sites to use it instead of per-file private copies.
extrapolate_source moved from fracture.remap_utils to the shared compass.landice.tests.ismip7_forcing.remap_utils module (828699b). Update the api.rst autosummary entry accordingly; this was breaking the strict Sphinx build (autosummary failed to import the moved symbol).
Port the standalone greenland_thermal_forcing tool into a new optional step of the ocean_thermal test case. build_3d_thermal_forcing (GrIS only, gated by process_ocean_thermal_3d) converts the 2-D GrIS thermal forcing into a 30-level 3-D field for MALI's nonlocal (Jourdain et al. 2020) melt scheme: seven regional EN4 vertical profiles, per-cell seafloor anchoring to the 2-D forcing, and per-region deltaT calibration. It auto-chains from the 2-D output the same run just produced and writes ismip6shelfMelt_3dThermalForcing (plus deltaT/gamma0/zOcean/basin), supplementing the 2-D file. The ported science lives in ocean_thermal/greenland_3d.py; the 3-D-specific parameters are supplied via a JSON config (config_file), while mesh, 2-D forcing, output, and diagnostics paths are injected from the compass config. Also standardize ocean scenario output names to 2dThermalForcing (GrIS) / 3dThermalForcing (AIS) and update the ismip7_run tf globs accordingly (AIS ingests 3-D, GrIS 2-D). Adds docs and no new dependencies (shapely/h5py/dask already present). Not yet wired: ismip7_run GrIS streams to ingest the 3-D field (follow-up).
Add use_3d_thermal_forcing (default false) to [ismip7_run_gris]. When true, ismip7_gris looks for *3dThermalForcing_*.nc instead of *2dThermalForcing_*.nc, reads ismip6shelfMelt_3dThermalForcing + ismip6shelfMelt_zOcean at annual intervals (instead of ismip6_2dThermalForcing at monthly intervals), adds an ismip7_params stream reading ismip6shelfMelt_deltaT/_basin/_gamma0 from melt_params_path (mirroring the AIS convention, and consuming the output of the new build_3d_thermal_forcing step in landice/ismip7_forcing/ocean_thermal), and sets config_use_3d_thermal_forcing_for_face_melt = .true. melt_params_path is now validated as required when use_3d_thermal_forcing is true. streams.landice.template gates the TF stream's variable list and the new ismip7_params stream with Jinja2 conditionals on use_3d_thermal_forcing; verified both branches render as valid XML with the expected variables. Updates user and developer docs.
…SON config - _find_variable() returned dataset.variables[name] (a low-level xr.Variable), not a full DataArray. Variable.isel() doesn't accept drop=True, so passing a positional indexer dict together with drop=True raised 'cannot specify both keyword and positional arguments to .isel'. Return dataset[name] instead. - calibrate_regional_delta_t raised when a region had zero initially-floating cells, aborting the whole calibration. Skip such regions instead: print a diagnostic, leave their monthly means as NaN, set deltaT=0, and leave achieved melt as NaN (undefined, since there's no floating ice to melt). Also add greenland_3d_tf_config.json, a real example JSON config for the build_3d_thermal_forcing step (region-mask/EN4/GeoJSON paths only; mesh, forcing_2d, output, and diagnostics are always injected by the step), and wire it into ismip7_forcing_ocx_gis.cfg via process_ocean_thermal_3d and [ismip7_ocean_thermal_3d] config_file.
scipy's classic/CDF-2 (NETCDF3_64BIT) writer produces a header that netCDF-C (ncdump, MALI) rejects with "Unknown file format" when a dataset mixes a record (unlimited-Time) variable with a scalar (0-D) variable, as in this output (ismip6shelfMelt_gamma0 alongside xtime and ismip6shelfMelt_3dThermalForcing). scipy can read its own broken file back, masking the problem. Switch to engine="netcdf4", which writes a conformant CDF-2 file and still streams record-by-record.
deltaT/gamma0 are calibrated once against OCX and must be held fixed for every ESM scenario (recalibrating per-ESM would force all their historical mean melt rates toward the same targets and erase the differences between ESM ocean forcings). Config gains scenario and melt_params_file; Config.calibrate_delta_t gates calibration to scenario == OCX. Non-OCX runs load deltaT/gamma0/basin from an existing melt_params_file instead of recalibrating, validating that the stored basin layout and gamma0 match. Also split ismip6shelfMelt_basin/gamma0/deltaT out of the time-varying 3-D thermal forcing output into their own file (write_melt_params/ read_melt_params), independent of ismip6shelfMelt_3dThermalForcing. This matches how ismip7_run already expects a separate melt_params_path file (see ismip7_gris streams.landice.template ismip7_params stream). build_3d_thermal_forcing.py always points melt_params_file at the OCX output directory, regardless of which scenario is currently being processed.
The xtime char array from the 2-D forcing was carried over and run through xarray's character coder a second time, splitting each byte into a spurious length-1 dimension. Build xtime fresh from the decoded timestamps following the existing compass idiom (ljust(64) strings -> dtype 'S') and let xarray encode the StrLen char dimension, so xtime is written as a proper char(Time, StrLen) variable.
A 285-year monthly 3D forcing field would be ~113 GB in a single file. Write the output in blocks of output_years_per_file years (default 10), one file per block named by its block start year (..._<YYYY>.nc), which the run side addresses with a $Y filename_template plus a filename_interval. Also correct the GrIS 3D thermal-forcing cadence to monthly on the run side; the annual filename cadence only applies to Antarctica, whose ISMIP7 3D forcing is provided annually. - greenland_3d.py: add Config.output_years_per_file; year_chunks and chunk_output_path helpers; write one file per block via _write_forcing_chunk. - greenland_3d_tf_config.json: add output_years_per_file example. - ismip7_gris/set_up_experiment.py: symlink the whole chunk series, derive filename_interval and reference_time, force monthly TF. - ismip7_gris/streams.landice.template: templated TF filename_interval and reference_time.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Create 3d Greenland thermal forcing from ISMIP7-provided 2d and EN4 data. Note that this is currently branched from #978. We will rebase once that is merged.
Checklist
api.rst) has any new or modified class, method and/or functions listedE3SM-Projectsubmodule has been updated with relevant E3SM changesMALI-Devsubmodule has been updated with relevant MALI changesTestingin this PR) any testing that was used to verify the changes