From ceeebd5784e7a3fbdc24e6370dfff74e0917d862 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CGennaro=E2=80=9D?= <“gennaro@lanl.gov”> Date: Fri, 23 Jun 2023 12:06:18 -0600 Subject: [PATCH 1/5] Add Risk Index Outcome analysis task --- mpas_analysis/__main__.py | 6 + mpas_analysis/default.cfg | 62 ++++ mpas_analysis/sea_ice/__init__.py | 2 + .../climatology_map_risk_index_outcome.py | 279 ++++++++++++++++++ 4 files changed, 349 insertions(+) create mode 100755 mpas_analysis/sea_ice/climatology_map_risk_index_outcome.py diff --git a/mpas_analysis/__main__.py b/mpas_analysis/__main__.py index 4bbb7c88b..a7108cf29 100644 --- a/mpas_analysis/__main__.py +++ b/mpas_analysis/__main__.py @@ -334,6 +334,12 @@ def build_analysis_list(config, controlConfig): analyses.append(sea_ice.ClimatologyMapSeaIceProduction( config=config, mpas_climatology_task=seaIceClimatologyTask, hemisphere='SH', control_config=controlConfig)) + analyses.append(sea_ice.ClimatologyMapRiskIndexOutcome( + config=config, mpas_climatology_task=seaIceClimatologyTask, + hemisphere='NH', control_config=controlConfig)) + analyses.append(sea_ice.ClimatologyMapRiskIndexOutcome( + config=config, mpas_climatology_task=seaIceClimatologyTask, + hemisphere='SH', control_config=controlConfig)) analyses.append(sea_ice.ClimatologyMapSeaIceMelting( config=config, mpas_climatology_task=seaIceClimatologyTask, hemisphere='SH', control_config=controlConfig)) diff --git a/mpas_analysis/default.cfg b/mpas_analysis/default.cfg index 3fcd11d33..1ba6cb988 100755 --- a/mpas_analysis/default.cfg +++ b/mpas_analysis/default.cfg @@ -4804,6 +4804,68 @@ referenceLongitude = 180 vertical = False +[climatologyMapRiskIndexOutcomeNH] +## options related to plotting maps of risk index outcomes for navigability +## in sea-ice covered water from climatologies + +# colormap for model/observations +colormapNameResult = RdYlBu +# whether the colormap is indexed or continuous +colormapTypeResult = indexed +# color indices into colormapName for filled contours +colormapIndicesResult = numpy.array(numpy.linspace(0, 255, 9), int) +# colormap levels/values for contour boundaries +colorbarLevelsResult = numpy.linspace(-10., 30., 8) + +# Months or seasons to plot (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, +# Nov, Dec, JFM, AMJ, JAS, OND, ANN) +seasons = ['MAM','SON'] + +# comparison grid(s) on which to plot analysis +comparisonGrids = ['arctic'] + +# Polar Class of vessel according to IMO. Range is 1 to 12 (increments of 1). +polarClass = 6 + +# reference lat/lon for sea ice plots in the northern hemisphere +minimumLatitude = 50 +referenceLongitude = 0 + +# arrange subplots vertically? +#vertical = False + + +[climatologyMapRiskIndexOutcomeSH] +## options related to plotting maps of risk index outcomes for navigability +## in sea-ice covered water from climatologies + +# colormap for model/observations +colormapNameResult = RdYlBu +# whether the colormap is indexed or continuous +colormapTypeResult = indexed +# color indices into colormapName for filled contours +colormapIndicesResult = numpy.array(numpy.linspace(0, 255, 9), int) +# colormap levels/values for contour boundaries +colorbarLevelsResult = numpy.linspace(-10., 30., 8) + +# Months or seasons to plot (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, +# Nov, Dec, JFM, AMJ, JAS, OND, ANN) +seasons = ['MAM','SON'] + +# comparison grid(s) on which to plot analysis +comparisonGrids = ['antarctic'] + +# Polar Class of vessel according to IMO. Range is 1 to 12 (increments of 1). +polarClass = 6 + +# reference lat/lon for sea ice plots in the northern hemisphere +minimumLatitude = -50 +referenceLongitude = 180 + +# arrange subplots vertically? +#vertical = False + + [climatologyMapSeaIceProductionNH] # options related to plotting horizontally remapped climatologies of # sea ice production against control model results and observations diff --git a/mpas_analysis/sea_ice/__init__.py b/mpas_analysis/sea_ice/__init__.py index dfc8ccca7..f203bda5d 100644 --- a/mpas_analysis/sea_ice/__init__.py +++ b/mpas_analysis/sea_ice/__init__.py @@ -31,3 +31,5 @@ ClimatologyMapSeaIceSnowMelt from mpas_analysis.sea_ice.climatology_map_area_pond import \ ClimatologyMapSeaIcePondArea +from mpas_analysis.sea_ice.climatology_map_risk_index_outcome import \ + ClimatologyMapRiskIndexOutcome diff --git a/mpas_analysis/sea_ice/climatology_map_risk_index_outcome.py b/mpas_analysis/sea_ice/climatology_map_risk_index_outcome.py new file mode 100755 index 000000000..0aaba2cf1 --- /dev/null +++ b/mpas_analysis/sea_ice/climatology_map_risk_index_outcome.py @@ -0,0 +1,279 @@ +# Copyright (c) 2017, Los Alamos National Security, LLC (LANS) +# and the University Corporation for Atmospheric Research (UCAR). +# +# Unless noted otherwise source code is licensed under the BSD license. +# Additional copyright and license information can be found in the LICENSE file +# distributed with this code, or at http://mpas-dev.github.com/license.html +# + +import xarray as xr +from pyremap import LatLon2DGridDescriptor + +from mpas_analysis.shared import AnalysisTask + +from mpas_analysis.shared.climatology import RemapMpasClimatologySubtask, \ + RemapObservedClimatologySubtask + +from mpas_analysis.shared.plot import PlotClimatologyMapSubtask + +from mpas_analysis.shared.io.utility import build_obs_path + + +class ClimatologyMapRiskIndexOutcome(AnalysisTask): + """ + An analysis task for evaluating the Risk Index Outcome + for navigation in sea-ice covered water. + (https://www.imorules.com/GUID-2C1D86CB-5D58-490F-B4D4-46C057E1D102.html) + """ + # Authors + # ------- + # Gennaro D'Angelo, Milena Veneziani + + def __init__(self, config, mpas_climatology_task, hemisphere, + control_config=None): + """ + Construct the analysis task. + + Parameters + ---------- + config : mpas_tools.config.MpasConfigParser + Configuration options + + mpas_climatology_task : mpas_analysis.shared.climatology.MpasClimatologyTask + The task that produced the climatology to be remapped and plotted + + hemisphere : {'NH', 'SH'} + The hemisphere to plot + + control_config : mpas_tools.config.MpasConfigParser, optional + Configuration options for a control run (if any) + """ + # Authors + # ------- + # Gennaro D'Angelo, Milena Veneziani + + task_name = f'climatologyMapRiskIndexOutcome{hemisphere}' + + field_name = 'riskIndexOutcome' + + tags = ['climatology', 'horizontalMap', field_name] + if hemisphere == 'NH': + tags = tags + ['arctic'] + else: + tags = tags + ['antarctic'] + + # call the constructor from the base class (AnalysisTask) + super().__init__(config=config, taskName=task_name, + componentName='seaIce', tags=tags) + + self.mpas_climatology_task = mpas_climatology_task + + section_name = self.taskName + + if hemisphere == 'NH': + hemisphere_long= 'Northern' + else: + hemisphere_long= 'Southern' + + # read in what seasons we want to plot + seasons = config.getexpression(section_name, 'seasons') + + if len(seasons) == 0: + raise ValueError(f'config section {section_name} does not contain ' + f'valid list of seasons') + + comparison_grid_names = config.getexpression(section_name, + 'comparisonGrids') + + if len(comparison_grid_names) == 0: + raise ValueError(f'config section {section_name} does not contain ' + f'valid list of comparison grids') + + variable_list = ['timeMonthly_avg_iceAreaCell', + 'timeMonthly_avg_iceVolumeCell'] + + remap_climatology_subtask = RemapMpasRiskIndexOutcomeClimatology( + mpas_climatology_task=mpas_climatology_task, + parent_task=self, + climatology_name=f'{field_name}{hemisphere}', + variable_list=variable_list, + comparison_grid_names=comparison_grid_names, + seasons=seasons) + + self.add_subtask(remap_climatology_subtask) + + for season in seasons: + for comparison_grid_name in comparison_grid_names: + + if control_config is None: + remap_observations_subtask = None + gallery_name = None + ref_title_label = None + ref_field_name = None + diff_title_label = 'Model - Observations' + + else: + control_run_name = control_config.get('runs', 'mainRunName') + gallery_name = None + ref_title_label = f'Control: {control_run_name}' + field_name = field_name + diff_title_label = 'Main - Control' + + image_caption = f'{season} Climatology Map of ' \ + f'{hemisphere_long}-Hemisphere Risk Index Outcome' + gallery_group = f'{hemisphere_long}-Hemisphere Risk Index Outcome' + # make a new subtask for this season and comparison grid + subtask = PlotClimatologyMapSubtask( + parentTask=self, season=season, + comparisonGridName=comparison_grid_name, + remapMpasClimatologySubtask=remap_climatology_subtask, + remapObsClimatologySubtask=None, + controlConfig=control_config) + + subtask.set_plot_info( + outFileLabel=f'risk_index_outcome{hemisphere}', + fieldNameInTitle='Risk Index Outcome', + mpasFieldName=field_name, + refFieldName=field_name, + refTitleLabel=ref_title_label, + diffTitleLabel=diff_title_label, + unitsLabel=r'', + imageCaption=image_caption, + galleryGroup=gallery_group, + groupSubtitle=None, + groupLink=f'{hemisphere.lower()}_rio', + galleryName=gallery_name, + extend=None) + + self.add_subtask(subtask) + + +class RemapMpasRiskIndexOutcomeClimatology(RemapMpasClimatologySubtask): + """ + A subtask for computing climatologies of risk index outcome from + climatologies of sea-ice concentration and thickness. + """ + def __init__(self, mpas_climatology_task, parent_task, climatology_name, + variable_list, seasons, comparison_grid_names): + + """ + Construct the analysis task and adds it as a subtask of the + ``parent_task``. + Parameters + ---------- + mpas_climatology_task : mpas_analysis.shared.climatology.MpasClimatologyTask + The task that produced the climatology to be remapped + parent_task : mpas_analysis.shared.AnalysisTask + The parent task, used to get the ``taskName``, ``config`` and + ``componentName`` + climatology_name : str + A name that describes the climatology (e.g. a short version of + the important field(s) in the climatology) used to name the + subdirectories for each stage of the climatology + variable_list : list of str + A list of variable names in ``timeSeriesStatsMonthly`` to be + included in the climatologies + seasons : list of str, optional + A list of seasons (keys in ``shared.constants.monthDictionary``) + to be computed or ['none'] (not ``None``) if only monthly + climatologies are needed. + comparison_grid_names : list of {'latlon', 'antarctic'} + The name(s) of the comparison grid to use for remapping. + """ + + subtask_name = f'remapMpasClimatology_RiskIndexOutcome' + # call the constructor from the base class + # (RemapMpasClimatologySubtask) + super().__init__( + mpas_climatology_task, parent_task, climatology_name, + variable_list, seasons, comparison_grid_names) + + self.mpas_climatology_task = mpas_climatology_task + self.variable_list = variable_list + + def setup_and_check(self): + """ + Perform steps to set up the analysis and check for errors in the setup. + """ + + # first, call setup_and_check from the base class + # (RemapMpasClimatologySubtask), which will set up remappers and add + # variables to mpas_climatology_task + super().setup_and_check() + + # don't add the variables and seasons to mpas_climatology_task until + # we're sure this subtask is supposed to run + self.mpas_climatology_task.add_variables(self.variable_list, + self.seasons) + + def customize_masked_climatology(self, climatology, season): + """ + Compute the risk index outcome from sea-ice concentration and thickness. + + Parameters + ---------- + climatology : xarray.Dataset + the climatology data set + season : str + The name of the season to be masked + Returns + ------- + climatology : xarray.Dataset + the modified climatology data set + """ + + rio = self._compute_risk_index_outcome(climatology) + + climatology['riskIndexOutcome'] = rio + climatology.riskIndexOutcome.attrs['units'] = '' + climatology = climatology.drop_vars(self.variable_list) + + return climatology + + def _compute_risk_index_outcome(self, climatology): + """ + Compute the risk index outcome from sea-ice concentration and thickness. + (https://www.imorules.com/GUID-2C1D86CB-5D58-490F-B4D4-46C057E1D102.html) + """ + ds_restart = xr.open_dataset(self.restartFileName) + ds_restart = ds_restart.isel(Time=0) + + scale_factor = 10 + pc = self.config.get(self.taskName, 'polarClass') - 1 + + concentration = climatology['timeMonthly_avg_iceAreaCell'] + thickness = climatology['timeMonthly_avg_iceVolumeCell'] + +# pic = ["PC1", "PC2", "PC3", "PC4", "PC5", "PC6", "PC7", "IA Super",\ +# "IA", "IB", "IC", "Not Ice Strengthened"] + + h_riv = np.array([0.5, 10, 15, 30, 50, 70, 100, 120, 170, 200, 250]) * 0.01 + riv = np.array([[ 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1, 1 ],\ + [ 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 0 ],\ + [ 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 0,-1 ],\ + [ 3, 3, 3, 3, 2, 2, 2, 2, 1, 0,-1,-2 ],\ + [ 3, 3, 3, 3, 2, 2, 1, 1, 0,-1,-2,-2 ],\ + [ 3, 2, 2, 2, 2, 1, 1, 0,-1,-2,-3,-3 ],\ + [ 3, 2, 2, 2, 1, 1, 0,-1,-2,-3,-3,-3 ],\ + [ 3, 2, 2, 2, 2, 1, 0,-1,-2,-3,-4,-4 ],\ + [ 3, 2, 2, 2, 1, 0,-1,-2,-3,-4,-5,-5 ],\ + [ 3, 2, 2, 1, 0,-1,-2,-3,-4,-5,-6,-6 ],\ + [ 3, 2, 1, 0,-1,-2,-3,-4,-5,-6,-7,-8 ],\ + [ 3, 1, 0,-1,-2,-3,-4,-5,-6,-7,-8,-8 ]]) + + riv_iceCell = np.nan*np.ones(np.shape(thickness)) + + riv_mask = np.where(thickness < h_riv[0]) + riv_iceCell[riv_mask] = riv[pc, 0] + + for ind in range(len(h_riv)-1): +# riv_mask = np.logical_and(thickness >= h_riv[ind], thickness < h_riv[ind+1]) + riv_mask = np.where(thickness >= h_riv[ind]) + riv_iceCell[riv_mask] = riv[pc, ind+1] + + riv_mask = np.where(thickness >= h_riv[-1]) + riv_iceCell[riv_mask] = riv[pc, -1] + + rio = ((1.0 - concentration) * riv[pc, 0] + concentration * riv_iceCell) * units_scale_factor + + return rio From a4d81ec5b52c8d913d7d4a85cee3e05344ae742e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CGennaro=E2=80=9D?= <“gennaro@lanl.gov”> Date: Mon, 26 Jun 2023 11:57:00 -0600 Subject: [PATCH 2/5] Errors corrected and description added --- mpas_analysis/default.cfg | 12 +++--- .../climatology_map_risk_index_outcome.py | 37 +++++++++++++++---- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/mpas_analysis/default.cfg b/mpas_analysis/default.cfg index 1ba6cb988..9a1ddd80d 100755 --- a/mpas_analysis/default.cfg +++ b/mpas_analysis/default.cfg @@ -4813,13 +4813,13 @@ colormapNameResult = RdYlBu # whether the colormap is indexed or continuous colormapTypeResult = indexed # color indices into colormapName for filled contours -colormapIndicesResult = numpy.array(numpy.linspace(0, 255, 9), int) +colormapIndicesResult = numpy.array(numpy.linspace(0, 255, 10), int) # colormap levels/values for contour boundaries -colorbarLevelsResult = numpy.linspace(-10., 30., 8) +colorbarLevelsResult = numpy.linspace(-10., 30., 9) # Months or seasons to plot (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, # Nov, Dec, JFM, AMJ, JAS, OND, ANN) -seasons = ['MAM','SON'] +seasons = ['AMJ','OND'] # comparison grid(s) on which to plot analysis comparisonGrids = ['arctic'] @@ -4844,13 +4844,13 @@ colormapNameResult = RdYlBu # whether the colormap is indexed or continuous colormapTypeResult = indexed # color indices into colormapName for filled contours -colormapIndicesResult = numpy.array(numpy.linspace(0, 255, 9), int) +colormapIndicesResult = numpy.array(numpy.linspace(0, 255, 10), int) # colormap levels/values for contour boundaries -colorbarLevelsResult = numpy.linspace(-10., 30., 8) +colorbarLevelsResult = numpy.linspace(-10., 30., 9) # Months or seasons to plot (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, # Nov, Dec, JFM, AMJ, JAS, OND, ANN) -seasons = ['MAM','SON'] +seasons = ['AMJ','OND'] # comparison grid(s) on which to plot analysis comparisonGrids = ['antarctic'] diff --git a/mpas_analysis/sea_ice/climatology_map_risk_index_outcome.py b/mpas_analysis/sea_ice/climatology_map_risk_index_outcome.py index 0aaba2cf1..e739f9756 100755 --- a/mpas_analysis/sea_ice/climatology_map_risk_index_outcome.py +++ b/mpas_analysis/sea_ice/climatology_map_risk_index_outcome.py @@ -7,6 +7,7 @@ # import xarray as xr +import numpy as np from pyremap import LatLon2DGridDescriptor from mpas_analysis.shared import AnalysisTask @@ -208,7 +209,7 @@ def setup_and_check(self): def customize_masked_climatology(self, climatology, season): """ - Compute the risk index outcome from sea-ice concentration and thickness. + Compute the risk index outcome from sea-ice concentration and (floe) thickness. Parameters ---------- @@ -232,22 +233,27 @@ def customize_masked_climatology(self, climatology, season): def _compute_risk_index_outcome(self, climatology): """ - Compute the risk index outcome from sea-ice concentration and thickness. + Compute the risk index outcome from sea-ice concentration and (floe) thickness, + as outlined in the International Maritime Organization (IMO) document. (https://www.imorules.com/GUID-2C1D86CB-5D58-490F-B4D4-46C057E1D102.html) """ ds_restart = xr.open_dataset(self.restartFileName) ds_restart = ds_restart.isel(Time=0) + # sea-ice concentration conversion from range [0,1] to range [0,10] scale_factor = 10 - pc = self.config.get(self.taskName, 'polarClass') - 1 - - concentration = climatology['timeMonthly_avg_iceAreaCell'] - thickness = climatology['timeMonthly_avg_iceVolumeCell'] + # polar class array index should be in the range [0,11], but not checked! + pc = self.config.getint(self.taskName, 'polarClass') - 1 +# this are labels that should appear in the plot, to indicate the Polar Class of the vessel (included here for the moment) # pic = ["PC1", "PC2", "PC3", "PC4", "PC5", "PC6", "PC7", "IA Super",\ # "IA", "IB", "IC", "Not Ice Strengthened"] + # reference floe thicknesses for calculation of Risk Index Values + # (this values were agreed upon by Elizabeth Hunke, Andrew Roberts, + # and Gennaro D'Angelo based on literature and IMO description) h_riv = np.array([0.5, 10, 15, 30, 50, 70, 100, 120, 170, 200, 250]) * 0.01 + # table of Risk Index Values (defined by IMO) riv = np.array([[ 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1, 1 ],\ [ 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 0 ],\ [ 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 0,-1 ],\ @@ -261,19 +267,34 @@ def _compute_risk_index_outcome(self, climatology): [ 3, 2, 1, 0,-1,-2,-3,-4,-5,-6,-7,-8 ],\ [ 3, 1, 0,-1,-2,-3,-4,-5,-6,-7,-8,-8 ]]) - riv_iceCell = np.nan*np.ones(np.shape(thickness)) + concentration = climatology['timeMonthly_avg_iceAreaCell'] + thickness = climatology['timeMonthly_avg_iceVolumeCell'] + # these lines may not be required, but rio should not be used + # to check concentration and thickness + concentration[np.where(concentration<0.0)] = 0.0 + concentration[np.where(concentration>1.0)] = 1.0 + thickness[np.where(concentration==0.0)] = 0.0 + + # introduce riv array and set values in open water + riv_iceCell = np.nan*np.ones(np.shape(thickness)) riv_mask = np.where(thickness < h_riv[0]) riv_iceCell[riv_mask] = riv[pc, 0] + # set riv values for chosen Polar Class of vessel for ind in range(len(h_riv)-1): # riv_mask = np.logical_and(thickness >= h_riv[ind], thickness < h_riv[ind+1]) riv_mask = np.where(thickness >= h_riv[ind]) riv_iceCell[riv_mask] = riv[pc, ind+1] + # set riv for highest floe thickness riv_mask = np.where(thickness >= h_riv[-1]) riv_iceCell[riv_mask] = riv[pc, -1] - rio = ((1.0 - concentration) * riv[pc, 0] + concentration * riv_iceCell) * units_scale_factor + # Risk Index Outcome for single-category ice. There are only + # two terms per cell: one coming from the fraction of the cell + # covered by open water and one coming from the fraction covered + # by sea ice (rio <= 30 by IMO definition) + rio = scale_factor * ((1.0 - concentration) * riv[pc, 0] + concentration * riv_iceCell) return rio From f312b83d98d0375d36493d19ce9d961c890f8865 Mon Sep 17 00:00:00 2001 From: Milena Veneziani Date: Tue, 4 Feb 2025 09:32:11 -0700 Subject: [PATCH 3/5] Updated minor comments in config file --- mpas_analysis/default.cfg | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/mpas_analysis/default.cfg b/mpas_analysis/default.cfg index 9a1ddd80d..a30c72b82 100755 --- a/mpas_analysis/default.cfg +++ b/mpas_analysis/default.cfg @@ -4824,10 +4824,12 @@ seasons = ['AMJ','OND'] # comparison grid(s) on which to plot analysis comparisonGrids = ['arctic'] -# Polar Class of vessel according to IMO. Range is 1 to 12 (increments of 1). +# Polar Class of vessel according to IMO. Range is 1 to 12 (increments of 1), +# corresponding to the following vessel types: 'PC1', 'PC2', 'PC3', 'PC4', +# 'PC5', 'PC6', 'PC7', 'IA Super', 'IA', 'IB', 'IC', 'Not Ice Strengthened' polarClass = 6 -# reference lat/lon for sea ice plots in the northern hemisphere +# minimum lat and reference lon for sea ice plots in the northern hemisphere minimumLatitude = 50 referenceLongitude = 0 @@ -4855,10 +4857,12 @@ seasons = ['AMJ','OND'] # comparison grid(s) on which to plot analysis comparisonGrids = ['antarctic'] -# Polar Class of vessel according to IMO. Range is 1 to 12 (increments of 1). +# Polar Class of vessel according to IMO. Range is 1 to 12 (increments of 1), +# corresponding to the following vessel types: 'PC1', 'PC2', 'PC3', 'PC4', +# 'PC5', 'PC6', 'PC7', 'IA Super', 'IA', 'IB', 'IC', 'Not Ice Strengthened' polarClass = 6 -# reference lat/lon for sea ice plots in the northern hemisphere +# minimum lat and reference lon for sea ice plots in the southern hemisphere minimumLatitude = -50 referenceLongitude = 180 From cbfd36b8f9cb48a79b9c74ee72531fca0c4c58da Mon Sep 17 00:00:00 2001 From: Milena Veneziani Date: Sun, 16 Mar 2025 11:28:21 -0600 Subject: [PATCH 4/5] Update of RIO computation. Moves RIV table to file. --- mpas_analysis/default.cfg | 72 +++++-- .../climatology_map_risk_index_outcome.py | 203 +++++++++++------- 2 files changed, 191 insertions(+), 84 deletions(-) diff --git a/mpas_analysis/default.cfg b/mpas_analysis/default.cfg index a30c72b82..d9af6e8e4 100755 --- a/mpas_analysis/default.cfg +++ b/mpas_analysis/default.cfg @@ -4805,15 +4805,25 @@ vertical = False [climatologyMapRiskIndexOutcomeNH] -## options related to plotting maps of risk index outcomes for navigability -## in sea-ice covered water from climatologies +## options related to plotting maps of the Risk Index Outcome (RIO) for navigability +## in sea-ice covered water from climatologies. The Risk Index Outcome is a navigability +## metric defined by the International Maritime Organization (IMO). The index ranges +## from -80 to 30. It depends on the sea-ice concentration and on Risk Index Values, +## which are assigned to a vessel according to its structural properties and according +## to ice thickness (and age). The maximum RIO refers to navigation in open water. +## Navigation under conditions of negative RIO values is restricted to some types +## of vessels. Navigation should generally be avoided where RIO < -10. + +# table of Risk Index Values (IMO, MSC.1/Circ.1519 6 June 2016) +# https://www.imorules.com/GUID-2C1D86CB-5D58-490F-B4D4-46C057E1D102.html +rivNH = RIV/riv_MSC.1_Circ.1519_6_June_2016.csv # colormap for model/observations colormapNameResult = RdYlBu # whether the colormap is indexed or continuous colormapTypeResult = indexed # color indices into colormapName for filled contours -colormapIndicesResult = numpy.array(numpy.linspace(0, 255, 10), int) +colormapIndicesResult = [0, 56, 85, 170, 198, 227, 241, 248, 255, 255] # colormap levels/values for contour boundaries colorbarLevelsResult = numpy.linspace(-10., 30., 9) @@ -4824,11 +4834,25 @@ seasons = ['AMJ','OND'] # comparison grid(s) on which to plot analysis comparisonGrids = ['arctic'] -# Polar Class of vessel according to IMO. Range is 1 to 12 (increments of 1), -# corresponding to the following vessel types: 'PC1', 'PC2', 'PC3', 'PC4', -# 'PC5', 'PC6', 'PC7', 'IA Super', 'IA', 'IB', 'IC', 'Not Ice Strengthened' +# Polar Class of vessels, according to IMO, for which RIO maps will be generated. +# Range is 1 to 12. Here the values 1-7 refer to the ‘Polar Classes’ PC1-PC7, +# assigned by the International Association of Classification Societies (IACS). +# A PC1 vessel is capable of year-round operations in all polar waters. PC2 and PC3 +# vessels can navigate in 2.5 m (or more) thick ice. The values 8-11 correspond +# to the Finnish-Swedish ice classes 'IA Super', 'IA', 'IB' and 'IC'. +# The last value, 12, refers to ‘Not-Ice-Strengthened’ ships, all vessels without +# a Polar Class. polarClass = 6 +# reference floe thicknesses (m) for calculation of the Risk Index Value (RIV) +# The thicknesses are intended to render the type of ice as defined by IMO. +# There must be as many values as there are types of ice in the RIV table. +# The first value has to be zero. +h_to_typeofice = [0, 0.005, 0.1, 0.15, 0.3, 0.5, 0.7, 1, 1.2, 1.7, 2, 2.5] + +# whether to use sea-ice categories for sea-ice concentration and thickness +useIceCategories = False + # minimum lat and reference lon for sea ice plots in the northern hemisphere minimumLatitude = 50 referenceLongitude = 0 @@ -4838,15 +4862,25 @@ referenceLongitude = 0 [climatologyMapRiskIndexOutcomeSH] -## options related to plotting maps of risk index outcomes for navigability -## in sea-ice covered water from climatologies +## options related to plotting maps of the Risk Index Outcome (RIO) for navigability +## in sea-ice covered water from climatologies. The Risk Index Outcome is a navigability +## metric defined by the International Maritime Organization (IMO). The index ranges +## from -80 to 30. It depends on the sea-ice concentration and on Risk Index Values, +## which are assigned to a vessel according to its structural properties and according +## to ice thickness (and age). The maximum RIO refers to navigation in open water. +## Navigation under conditions of negative RIO values is restricted to some types +## of vessels. Navigation should generally be avoided where RIO < -10. + +# table of Risk Index Values (IMO, MSC.1/Circ.1519 6 June 2016) +# https://www.imorules.com/GUID-2C1D86CB-5D58-490F-B4D4-46C057E1D102.html +rivSH = RIV/riv_MSC.1_Circ.1519_6_June_2016.csv # colormap for model/observations colormapNameResult = RdYlBu # whether the colormap is indexed or continuous colormapTypeResult = indexed # color indices into colormapName for filled contours -colormapIndicesResult = numpy.array(numpy.linspace(0, 255, 10), int) +colormapIndicesResult = [0, 56, 85, 170, 198, 227, 241, 248, 255, 255] # colormap levels/values for contour boundaries colorbarLevelsResult = numpy.linspace(-10., 30., 9) @@ -4857,11 +4891,25 @@ seasons = ['AMJ','OND'] # comparison grid(s) on which to plot analysis comparisonGrids = ['antarctic'] -# Polar Class of vessel according to IMO. Range is 1 to 12 (increments of 1), -# corresponding to the following vessel types: 'PC1', 'PC2', 'PC3', 'PC4', -# 'PC5', 'PC6', 'PC7', 'IA Super', 'IA', 'IB', 'IC', 'Not Ice Strengthened' +# Polar Class of vessels, according to IMO, for which RIO maps will be generated. +# Range is 1 to 12. Here the values 1-7 refer to the ‘Polar Classes’ PC1-PC7, +# assigned by the International Association of Classification Societies (IACS). +# A PC1 vessel is capable of year-round operations in all polar waters. PC2 and PC3 +# vessels can navigate in 2.5 m (or more) thick ice. The values 8-11 correspond +# to the Finnish-Swedish ice classes 'IA Super', 'IA', 'IB' and 'IC'. +# The last value, 12, refers to ‘Not-Ice-Strengthened’ ships, all vessels without +# a Polar Class. polarClass = 6 +# reference floe thicknesses (m) for calculation of the Risk Index Value (RIV) +# The thicknesses are intended to render the type of ice as defined by IMO. +# There must be as many values as there are types of ice in the RIV table. +# The first value has to be zero. +h_to_typeofice = [0, 0.005, 0.1, 0.15, 0.3, 0.5, 0.7, 1, 1.2, 1.7, 2, 2.5] + +# whether to use sea-ice categories for sea-ice concentration and thickness +useIceCategories = False + # minimum lat and reference lon for sea ice plots in the southern hemisphere minimumLatitude = -50 referenceLongitude = 180 diff --git a/mpas_analysis/sea_ice/climatology_map_risk_index_outcome.py b/mpas_analysis/sea_ice/climatology_map_risk_index_outcome.py index e739f9756..c7eb0a278 100755 --- a/mpas_analysis/sea_ice/climatology_map_risk_index_outcome.py +++ b/mpas_analysis/sea_ice/climatology_map_risk_index_outcome.py @@ -55,7 +55,7 @@ def __init__(self, config, mpas_climatology_task, hemisphere, task_name = f'climatologyMapRiskIndexOutcome{hemisphere}' - field_name = 'riskIndexOutcome' + field_name = 'RiskIndexOutcome' tags = ['climatology', 'horizontalMap', field_name] if hemisphere == 'NH': @@ -81,17 +81,53 @@ def __init__(self, config, mpas_climatology_task, hemisphere, if len(seasons) == 0: raise ValueError(f'config section {section_name} does not contain ' - f'valid list of seasons') + f'a valid list of seasons') comparison_grid_names = config.getexpression(section_name, 'comparisonGrids') if len(comparison_grid_names) == 0: raise ValueError(f'config section {section_name} does not contain ' - f'valid list of comparison grids') + f'a valid list of comparison grids') - variable_list = ['timeMonthly_avg_iceAreaCell', - 'timeMonthly_avg_iceVolumeCell'] + # read in what polar class of vessel we want to plot + polarclass = config.getexpression(section_name, 'polarClass') + + if polarclass < 0 or polarclass > 12: + raise ValueError(f'config section {section_name} does not contain ' + f'a valid instance of Polar Class') + + # convert to 0-based array index + polarclass = np.int_(polarclass) - 1 + + # read in table of Risk Index Values + #riv_csv = 'riv_MSC.1_Circ.1519_6_June_2016.csv' + riv_csv = build_obs_path(config, 'seaIce', + relativePathOption='riv{}'.format(hemisphere), + relativePathSection=section_name) + IceClassLabels = np.genfromtxt(riv_csv, delimiter=',', skip_header=1, + dtype=str, usecols=(0,)) + redClassLabels = np.char.replace(IceClassLabels, ' ', '') + redClassLabels = np.array([s[:9] for s in redClassLabels]) + riskindexvalue = np.genfromtxt(riv_csv, delimiter=',', skip_header=1) + riskindexvalue = riskindexvalue[:, 1:] + riskindexvalue = np.array(riskindexvalue) + + # read in reference floe thicknesses for calculation of Risk Index Values + # Default values: [0.0, 0.5, 10, 15, 30, 50, 70, 100, 120, 170, 200, 250]/100 + # (the default values were agreed upon by Elizabeth Hunke, Andrew Roberts, + # and Gennaro D'Angelo based on literature and IMO description) + h_to_typeofice = config.getexpression(section_name, 'h_to_typeofice') + h_to_typeofice = np.array(h_to_typeofice) + + # read in what type of variables we want to plot + useIceCategories = config.getexpression(section_name, 'useIceCategories') + if useIceCategories: + variable_list = ['timeMonthly_avg_iceAreaCategory', + 'timeMonthly_avg_iceVolumeCategory'] + else: + variable_list = ['timeMonthly_avg_iceAreaCell', + 'timeMonthly_avg_iceVolumeCell'] remap_climatology_subtask = RemapMpasRiskIndexOutcomeClimatology( mpas_climatology_task=mpas_climatology_task, @@ -99,8 +135,11 @@ def __init__(self, config, mpas_climatology_task, hemisphere, climatology_name=f'{field_name}{hemisphere}', variable_list=variable_list, comparison_grid_names=comparison_grid_names, - seasons=seasons) - + seasons=seasons, + polarclass = polarclass, + riskindexvalue=riskindexvalue, + h_to_typeofice=h_to_typeofice) + self.add_subtask(remap_climatology_subtask) for season in seasons: @@ -120,9 +159,12 @@ def __init__(self, config, mpas_climatology_task, hemisphere, field_name = field_name diff_title_label = 'Main - Control' - image_caption = f'{season} Climatology Map of ' \ - f'{hemisphere_long}-Hemisphere Risk Index Outcome' - gallery_group = f'{hemisphere_long}-Hemisphere Risk Index Outcome' + image_caption = f'Climatology Map of ' \ + f'{hemisphere_long}-Hemisphere Risk Index Outcome, ' \ + f'{IceClassLabels[polarclass]}' + gallery_group = f'{hemisphere_long}-Hemisphere Risk Index Outcome, ' \ + f'{IceClassLabels[polarclass]}' + # make a new subtask for this season and comparison grid subtask = PlotClimatologyMapSubtask( parentTask=self, season=season, @@ -132,8 +174,10 @@ def __init__(self, config, mpas_climatology_task, hemisphere, controlConfig=control_config) subtask.set_plot_info( - outFileLabel=f'risk_index_outcome{hemisphere}', - fieldNameInTitle='Risk Index Outcome', + outFileLabel=f'risk_index_outcome{hemisphere}_' \ + f'{redClassLabels[polarclass]}', + fieldNameInTitle=f'Risk Index Outcome, ' \ + f'{IceClassLabels[polarclass]}', mpasFieldName=field_name, refFieldName=field_name, refTitleLabel=ref_title_label, @@ -144,18 +188,19 @@ def __init__(self, config, mpas_climatology_task, hemisphere, groupSubtitle=None, groupLink=f'{hemisphere.lower()}_rio', galleryName=gallery_name, - extend=None) + extend='min') self.add_subtask(subtask) class RemapMpasRiskIndexOutcomeClimatology(RemapMpasClimatologySubtask): """ - A subtask for computing climatologies of risk index outcome from + A subtask for computing climatologies of Risk Index Outcome from climatologies of sea-ice concentration and thickness. """ def __init__(self, mpas_climatology_task, parent_task, climatology_name, - variable_list, seasons, comparison_grid_names): + variable_list, seasons, comparison_grid_names, + polarclass, riskindexvalue, h_to_typeofice): """ Construct the analysis task and adds it as a subtask of the @@ -180,6 +225,15 @@ def __init__(self, mpas_climatology_task, parent_task, climatology_name, climatologies are needed. comparison_grid_names : list of {'latlon', 'antarctic'} The name(s) of the comparison grid to use for remapping. + polarclass : integer + Polar Class of vessel for which Risk Index Outcomes are computed + riskindexvalue : list of integers + Risk Index Values for a vessel of given Polar Class and type of + sea ice (values defined by the International Maritime Organization, + IMO). + h_to_typeofice: list of reference ice thicknesses + Values that establish an equvalence between IMO type of ice + (and age of ice) and ice floe thickness """ subtask_name = f'remapMpasClimatology_RiskIndexOutcome' @@ -191,6 +245,9 @@ def __init__(self, mpas_climatology_task, parent_task, climatology_name, self.mpas_climatology_task = mpas_climatology_task self.variable_list = variable_list + self.polarclass = polarclass + self.riskindexvalue = riskindexvalue + self.h_to_typeofice = h_to_typeofice def setup_and_check(self): """ @@ -209,7 +266,7 @@ def setup_and_check(self): def customize_masked_climatology(self, climatology, season): """ - Compute the risk index outcome from sea-ice concentration and (floe) thickness. + Compute the Risk Index Outcome from sea-ice concentration and (floe) thickness. Parameters ---------- @@ -225,76 +282,78 @@ def customize_masked_climatology(self, climatology, season): rio = self._compute_risk_index_outcome(climatology) - climatology['riskIndexOutcome'] = rio - climatology.riskIndexOutcome.attrs['units'] = '' + climatology['RiskIndexOutcome'] = rio + climatology.RiskIndexOutcome.attrs['units'] = '' climatology = climatology.drop_vars(self.variable_list) return climatology def _compute_risk_index_outcome(self, climatology): """ - Compute the risk index outcome from sea-ice concentration and (floe) thickness, + Compute the Risk Index Outcome from sea-ice concentration and (floe) thickness, as outlined in the International Maritime Organization (IMO) document. (https://www.imorules.com/GUID-2C1D86CB-5D58-490F-B4D4-46C057E1D102.html) """ + + # whether to use sea-ice categories for sea-ice concentration and thickness + useIceCategories = self.config.getexpression(self.taskName, + 'useIceCategories') + ds_restart = xr.open_dataset(self.restartFileName) ds_restart = ds_restart.isel(Time=0) + riv = self.riskindexvalue + h2toi = self.h_to_typeofice + pc = self.polarclass + # sea-ice concentration conversion from range [0,1] to range [0,10] scale_factor = 10 - # polar class array index should be in the range [0,11], but not checked! - pc = self.config.getint(self.taskName, 'polarClass') - 1 - -# this are labels that should appear in the plot, to indicate the Polar Class of the vessel (included here for the moment) -# pic = ["PC1", "PC2", "PC3", "PC4", "PC5", "PC6", "PC7", "IA Super",\ -# "IA", "IB", "IC", "Not Ice Strengthened"] - # reference floe thicknesses for calculation of Risk Index Values - # (this values were agreed upon by Elizabeth Hunke, Andrew Roberts, - # and Gennaro D'Angelo based on literature and IMO description) - h_riv = np.array([0.5, 10, 15, 30, 50, 70, 100, 120, 170, 200, 250]) * 0.01 - # table of Risk Index Values (defined by IMO) - riv = np.array([[ 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1, 1 ],\ - [ 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 0 ],\ - [ 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 0,-1 ],\ - [ 3, 3, 3, 3, 2, 2, 2, 2, 1, 0,-1,-2 ],\ - [ 3, 3, 3, 3, 2, 2, 1, 1, 0,-1,-2,-2 ],\ - [ 3, 2, 2, 2, 2, 1, 1, 0,-1,-2,-3,-3 ],\ - [ 3, 2, 2, 2, 1, 1, 0,-1,-2,-3,-3,-3 ],\ - [ 3, 2, 2, 2, 2, 1, 0,-1,-2,-3,-4,-4 ],\ - [ 3, 2, 2, 2, 1, 0,-1,-2,-3,-4,-5,-5 ],\ - [ 3, 2, 2, 1, 0,-1,-2,-3,-4,-5,-6,-6 ],\ - [ 3, 2, 1, 0,-1,-2,-3,-4,-5,-6,-7,-8 ],\ - [ 3, 1, 0,-1,-2,-3,-4,-5,-6,-7,-8,-8 ]]) - - concentration = climatology['timeMonthly_avg_iceAreaCell'] - thickness = climatology['timeMonthly_avg_iceVolumeCell'] - - # these lines may not be required, but rio should not be used - # to check concentration and thickness - concentration[np.where(concentration<0.0)] = 0.0 - concentration[np.where(concentration>1.0)] = 1.0 - thickness[np.where(concentration==0.0)] = 0.0 - - # introduce riv array and set values in open water - riv_iceCell = np.nan*np.ones(np.shape(thickness)) - riv_mask = np.where(thickness < h_riv[0]) - riv_iceCell[riv_mask] = riv[pc, 0] - - # set riv values for chosen Polar Class of vessel - for ind in range(len(h_riv)-1): -# riv_mask = np.logical_and(thickness >= h_riv[ind], thickness < h_riv[ind+1]) - riv_mask = np.where(thickness >= h_riv[ind]) - riv_iceCell[riv_mask] = riv[pc, ind+1] - - # set riv for highest floe thickness - riv_mask = np.where(thickness >= h_riv[-1]) - riv_iceCell[riv_mask] = riv[pc, -1] - - # Risk Index Outcome for single-category ice. There are only - # two terms per cell: one coming from the fraction of the cell - # covered by open water and one coming from the fraction covered - # by sea ice (rio <= 30 by IMO definition) - rio = scale_factor * ((1.0 - concentration) * riv[pc, 0] + concentration * riv_iceCell) + if useIceCategories: + concentration = climatology['timeMonthly_avg_iceAreaCategory'] + thickness = climatology['timeMonthly_avg_iceVolumeCategory'] + else: + concentration = climatology['timeMonthly_avg_iceAreaCell'] + thickness = climatology['timeMonthly_avg_iceVolumeCell'] + + # remove 1-dimensional coordinates + concentration = concentration.squeeze() + thickness = thickness.squeeze() + + # correct out-of-range values + concentration = np.clip(concentration, a_min=0, a_max=1) + # compute sea-ice floe thickness (ice thickness averaged + # over the fraction of the cell area covered by ice) + with np.errstate(divide='ignore', invalid='ignore'): + thickness = np.where(concentration > 0, + np.divide(thickness, concentration), 0) + + # define RIV array and set RIV values according to sea-ice + # floe thickness and reference values. There are as many RIV + # values as there are cells and ice categories + riv_iceCell = np.full_like(thickness, np.nan) + for ind in range(len(h2toi)): + riv_mask = np.where(thickness >= h2toi[ind]) + riv_iceCell[riv_mask] = riv[pc, ind] + + if concentration.ndim > 1: + # Risk Index Outcome for multi-category ice. The RIO is derived + # as a concentration-weighted sum of RIVs over each ice category + siconc = concentration.sum(dim='nCategories') + siconc = np.clip(siconc, a_min=0, a_max=1) + rio_ow = (1.0 - siconc) * riv[pc, 0] + rio_si = concentration * riv_iceCell + rio_si = rio_si.sum(dim='nCategories') + rio = rio_ow + rio_si + else: + # Risk Index Outcome for single-category ice. There are only two + # terms per cell: one coming from the fraction of the cell covered + # by open water and one coming from the fraction covered by sea ice + rio = (1.0 - concentration) * riv[pc, 0] + concentration * riv_iceCell + + # out-of-range corrections + rio = np.clip(rio, a_min=riv[pc, -1], a_max=riv[pc, 0]) + # re-scaling + rio *= scale_factor return rio From 5295bbb2b58442eb27ebedc40c31bef86c8f3993 Mon Sep 17 00:00:00 2001 From: Milena Veneziani Date: Mon, 31 Mar 2025 16:40:29 -0600 Subject: [PATCH 5/5] Adds RIO documentation and RIV entry in obs --- docs/developers_guide/api.rst | 1 + docs/users_guide/analysis_tasks.rst | 3 + .../climatologyMapRiskIndexOutcomeNH.rst | 92 ++++++++++++++++++ .../climatologyMapRiskIndexOutcomeSH.rst | 92 ++++++++++++++++++ .../tasks/examples/risk_index_outcome_nh.png | Bin 0 -> 505701 bytes .../tasks/examples/risk_index_outcome_sh.png | Bin 0 -> 305726 bytes mpas_analysis/default.cfg | 8 +- mpas_analysis/obs/observational_datasets.xml | 48 +++++++++ 8 files changed, 240 insertions(+), 4 deletions(-) create mode 100644 docs/users_guide/tasks/climatologyMapRiskIndexOutcomeNH.rst create mode 100644 docs/users_guide/tasks/climatologyMapRiskIndexOutcomeSH.rst create mode 100644 docs/users_guide/tasks/examples/risk_index_outcome_nh.png create mode 100644 docs/users_guide/tasks/examples/risk_index_outcome_sh.png diff --git a/docs/developers_guide/api.rst b/docs/developers_guide/api.rst index c6cb11fd6..474ed1840 100644 --- a/docs/developers_guide/api.rst +++ b/docs/developers_guide/api.rst @@ -157,6 +157,7 @@ Sea ice tasks ClimatologyMapSeaIceVolumeTendencyTransp TimeSeriesSeaIce ClimatologyMapIcebergConc + ClimatologyMapRiskIndexOutcome Shared modules diff --git a/docs/users_guide/analysis_tasks.rst b/docs/users_guide/analysis_tasks.rst index d5f2c83c9..0af368f70 100644 --- a/docs/users_guide/analysis_tasks.rst +++ b/docs/users_guide/analysis_tasks.rst @@ -80,3 +80,6 @@ Analysis Tasks tasks/timeSeriesSeaIceAreaVol tasks/climatologyMapIcebergConcSH + + tasks/climatologyMapRiskIndexOutcomeNH + tasks/climatologyMapRiskIndexOutcomeSH diff --git a/docs/users_guide/tasks/climatologyMapRiskIndexOutcomeNH.rst b/docs/users_guide/tasks/climatologyMapRiskIndexOutcomeNH.rst new file mode 100644 index 000000000..df561eabf --- /dev/null +++ b/docs/users_guide/tasks/climatologyMapRiskIndexOutcomeNH.rst @@ -0,0 +1,92 @@ +.. _task_climatologyMapRiskIndexOutcomeNH: + +climatologyMapRiskIndexOutcomeNH +================================ + +An analysis task for plotting maps of Arctic risk index outcome (rio) for navigation in ice-covered waters. + +Component and Tags:: + + component: seaIce + tags: climatology, horizontalMap, RiskIndexOutcome + +Configuration Options +--------------------- + +The following configuration options are available for this task:: + + [climatologyMapRiskIndexOutcomeNH] + ## options related to plotting maps of the Risk Index Outcome (RIO) for navigability + ## in sea-ice covered water from climatologies. The Risk Index Outcome is a navigability + ## metric defined by the International Maritime Organization (IMO). The index ranges + ## from -80 to 30. It depends on the sea-ice concentration and on Risk Index Values, + ## which are assigned to a vessel according to its structural properties and according + ## to ice thickness (and age). The maximum RIO refers to navigation in open water. + ## Navigation under conditions of negative RIO values is restricted to some types + ## of vessels. Navigation should generally be avoided where RIO < -10. + + # table of Risk Index Values (IMO, MSC.1/Circ.1519 6 June 2016) + # https://www.imorules.com/GUID-2C1D86CB-5D58-490F-B4D4-46C057E1D102.html + rivNH = RIV/riv_MSC.1_Circ.1519_6_June_2016.csv + + # colormap for model/observations + colormapNameResult = RdYlBu + # whether the colormap is indexed or continuous + colormapTypeResult = indexed + # color indices into colormapName for filled contours + colormapIndicesResult = [0, 56, 85, 170, 198, 227, 241, 248, 255, 255] + # colormap levels/values for contour boundaries + colorbarLevelsResult = numpy.linspace(-10., 30., 9) + + # Months or seasons to plot (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, + # Nov, Dec, JFM, AMJ, JAS, OND, ANN) + seasons = ['AMJ', 'OND'] + + # comparison grid(s) on which to plot analysis + comparisonGrids = ['arctic'] + + # Polar Class of vessels, according to IMO, for which RIO maps will be generated. + # Range is 1 to 12. Here the values 1-7 refer to the ‘Polar Classes’ PC1-PC7, + # assigned by the International Association of Classification Societies (IACS). + # A PC1 vessel is capable of year-round operations in all polar waters. PC2 and PC3 + # vessels can navigate in 2.5 m (or more) thick ice. The values 8-11 correspond + # to the Finnish-Swedish ice classes 'IA Super', 'IA', 'IB' and 'IC'. + # The last value, 12, refers to ‘Not-Ice-Strengthened’ ships, all vessels without + # a Polar Class. + polarClass = 6 + + # reference floe thicknesses (m) for calculation of the Risk Index Value (RIV) + # The thicknesses are intended to render the type of ice as defined by IMO. + # There must be as many values as there are types of ice in the RIV table. + # The first value has to be zero. + h_to_typeofice = [0, 0.005, 0.1, 0.15, 0.3, 0.5, 0.7, 1, 1.2, 1.7, 2, 2.5] + + # whether to use sea-ice categories for sea-ice concentration and thickness + useIceCategories = False + + # minimum lat and reference lon for sea ice plots in the northern hemisphere + minimumLatitude = 50 + referenceLongitude = 0 + + # arrange subplots vertically? + vertical = False + +The option ``vertical = True`` can be used to plot 3 panels one above another +(resulting in a tall, thin image) rather than next to each other, the default +(resulting in a short, wide image). + +For details on the remaining configuration options, see: + * :ref:`config_colormaps` + * :ref:`config_seasons` + * :ref:`config_comparison_grids` + +Observations +------------ +:ref:`imo_riv` + +Example Result +-------------- + +.. image:: examples/risk_index_outcome_nh.png + :width: 720 px + :align: center diff --git a/docs/users_guide/tasks/climatologyMapRiskIndexOutcomeSH.rst b/docs/users_guide/tasks/climatologyMapRiskIndexOutcomeSH.rst new file mode 100644 index 000000000..7a8f52134 --- /dev/null +++ b/docs/users_guide/tasks/climatologyMapRiskIndexOutcomeSH.rst @@ -0,0 +1,92 @@ +.. _task_climatologyMapRiskIndexOutcomeSH: + +climatologyMapRiskIndexOutcomeSH +================================ + +An analysis task for plotting maps of Antarctic risk index outcome (rio) for navigation in ice-covered waters. + +Component and Tags:: + + component: seaIce + tags: climatology, horizontalMap, RiskIndexOutcome + +Configuration Options +--------------------- + +The following configuration options are available for this task:: + + [climatologyMapRiskIndexOutcomeSH] + ## options related to plotting maps of the Risk Index Outcome (RIO) for navigability + ## in sea-ice covered water from climatologies. The Risk Index Outcome is a navigability + ## metric defined by the International Maritime Organization (IMO). The index ranges + ## from -80 to 30. It depends on the sea-ice concentration and on Risk Index Values, + ## which are assigned to a vessel according to its structural properties and according + ## to ice thickness (and age). The maximum RIO refers to navigation in open water. + ## Navigation under conditions of negative RIO values is restricted to some types + ## of vessels. Navigation should generally be avoided where RIO < -10. + + # table of Risk Index Values (IMO, MSC.1/Circ.1519 6 June 2016) + # https://www.imorules.com/GUID-2C1D86CB-5D58-490F-B4D4-46C057E1D102.html + rivSH = RIV/riv_MSC.1_Circ.1519_6_June_2016.csv + + # colormap for model/observations + colormapNameResult = RdYlBu + # whether the colormap is indexed or continuous + colormapTypeResult = indexed + # color indices into colormapName for filled contours + colormapIndicesResult = [0, 56, 85, 170, 198, 227, 241, 248, 255, 255] + # colormap levels/values for contour boundaries + colorbarLevelsResult = numpy.linspace(-10., 30., 9) + + # Months or seasons to plot (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, + # Nov, Dec, JFM, AMJ, JAS, OND, ANN) + seasons = ['AMJ', 'OND'] + + # comparison grid(s) on which to plot analysis + comparisonGrids = ['antarctic'] + + # Polar Class of vessels, according to IMO, for which RIO maps will be generated. + # Range is 1 to 12. Here the values 1-7 refer to the ‘Polar Classes’ PC1-PC7, + # assigned by the International Association of Classification Societies (IACS). + # A PC1 vessel is capable of year-round operations in all polar waters. PC2 and PC3 + # vessels can navigate in 2.5 m (or more) thick ice. The values 8-11 correspond + # to the Finnish-Swedish ice classes 'IA Super', 'IA', 'IB' and 'IC'. + # The last value, 12, refers to ‘Not-Ice-Strengthened’ ships, all vessels without + # a Polar Class. + polarClass = 6 + + # reference floe thicknesses (m) for calculation of the Risk Index Value (RIV) + # The thicknesses are intended to render the type of ice as defined by IMO. + # There must be as many values as there are types of ice in the RIV table. + # The first value has to be zero. + h_to_typeofice = [0, 0.005, 0.1, 0.15, 0.3, 0.5, 0.7, 1, 1.2, 1.7, 2, 2.5] + + # whether to use sea-ice categories for sea-ice concentration and thickness + useIceCategories = False + + # minimum lat and reference lon for sea ice plots in the southern hemisphere + minimumLatitude = -50 + referenceLongitude = 180 + + # arrange subplots vertically? + vertical = False + +The option ``vertical = True`` can be used to plot 3 panels one above another +(resulting in a tall, thin image) rather than next to each other, the default +(resulting in a short, wide image). + +For details on the remaining configuration options, see: + * :ref:`config_colormaps` + * :ref:`config_seasons` + * :ref:`config_comparison_grids` + +Observations +------------ +:ref:`imo_riv` + +Example Result +-------------- + +.. image:: examples/risk_index_outcome_sh.png + :width: 720 px + :align: center diff --git a/docs/users_guide/tasks/examples/risk_index_outcome_nh.png b/docs/users_guide/tasks/examples/risk_index_outcome_nh.png new file mode 100644 index 0000000000000000000000000000000000000000..a580dc65f6f052fa9b89a76d73e340732d68537c GIT binary patch literal 505701 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