Source code for compass.ocean.iceshelf

import os
import shutil
import subprocess

import numpy
import xarray
from mpas_tools.cime.constants import constants
from mpas_tools.io import write_netcdf

from compass.io import symlink
from compass.model import partition, run_model


[docs] def compute_land_ice_pressure_and_draft(ssh, modify_mask, ref_density): """ Compute the pressure from and overlying ice shelf and the ice-shelf draft Parameters ---------- ssh : xarray.DataArray The sea surface height (the ice draft) modify_mask : xarray.DataArray A mask that is 1 where ``landIcePressure`` can be deviate from 0 ref_density : float A reference density for seawater displaced by the ice shelf Returns ------- landIcePressure : xarray.DataArray The pressure from the overlying land ice on the ocean landIceDraft : xarray.DataArray The ice draft, equal to the initial ``ssh`` """ gravity = constants['SHR_CONST_G'] landIcePressure = \ modify_mask * numpy.maximum(-ref_density * gravity * ssh, 0.) landIceDraft = ssh return landIcePressure, landIceDraft
[docs] def adjust_ssh(variable, iteration_count, step, update_pio=True, convert_to_cdf5=False, delta_ssh_threshold=None): """ Adjust the sea surface height or land-ice pressure to be dynamically consistent with one another. A series of short model runs are performed, each with Parameters ---------- variable : {'ssh', 'landIcePressure'} The variable to adjust iteration_count : int The number of iterations of adjustment step : compass.Step the step for performing SSH or land-ice pressure adjustment update_pio : bool, optional Whether to update PIO tasks and stride convert_to_cdf5 : bool, optional Whether to convert files to CDF5 format with ncks after writing them out. This is intended to improve MPAS-Ocean performance, since reading in NETCDF4 format files can be very slow. """ ntasks = step.ntasks config = step.config logger = step.logger out_filename = None if variable not in ['ssh', 'landIcePressure']: raise ValueError(f"Unknown variable to modify: {variable}") if update_pio: step.update_namelist_pio('namelist.ocean') partition(ntasks, config, logger) with xarray.open_dataset('adjusting_init0.nc') as ds: ds = ds.isel(Time=0) orig_ssh = ds.ssh orig_land_ice_pressure = ds.landIcePressure on_a_sphere = ds.attrs['on_a_sphere'].lower() == 'yes' modify_mask = numpy.logical_and(ds.maxLevelCell > 0, ds.sshAdjustmentMask == 1) for iter_index in range(iteration_count): logger.info(f" * Iteration {iter_index + 1}/{iteration_count}") in_filename = f'adjusting_init{iter_index}.nc' out_filename = f'adjusting_init{iter_index + 1}.nc' symlink(in_filename, 'adjusting_init.nc') logger.info(" * Running forward model") run_model(step, update_pio=False, partition_graph=False) logger.info(" - Complete") logger.info(" * Updating SSH or land-ice pressure") with xarray.open_dataset(in_filename) as ds: # keep the data set with Time for output ds_out = ds ds = ds.isel(Time=0) init_ssh = ds.ssh if 'minLevelCell' in ds: minLevelCell = ds.minLevelCell - 1 else: minLevelCell = xarray.zeros_like(ds.maxLevelCell) with xarray.open_dataset('output_ssh.nc') as ds_ssh: # get the last time entry ds_ssh = ds_ssh.isel(Time=-1) final_ssh = ds_ssh.ssh topDensity = ds_ssh.density.isel(nVertLevels=minLevelCell) delta_ssh = modify_mask * (final_ssh - init_ssh) # then, modify the SSH or land-ice pressure if variable == 'ssh': ssh = final_ssh.expand_dims(dim='Time', axis=0) ds_out['ssh'] = ssh # also update the landIceDraft variable, which will be used to # compensate for the SSH due to land-ice pressure when # computing sea-surface tilt ds_out['landIceDraft'] = ssh # we also need to stretch layerThickness to be compatible with # the new SSH stretch = ((final_ssh + ds.bottomDepth) / (init_ssh + ds.bottomDepth)) ds_out['layerThickness'] = ds_out.layerThickness * stretch landIcePressure = ds.landIcePressure.values else: # Moving the SSH up or down by deltaSSH would change the # land-ice pressure by density(SSH)*g*deltaSSH. If deltaSSH is # positive (moving up), it means the land-ice pressure is too # small and if deltaSSH is negative (moving down), it means # land-ice pressure is too large, the sign of the second term # makes sense. gravity = constants['SHR_CONST_G'] deltaLandIcePressure = topDensity * gravity * delta_ssh landIcePressure = numpy.maximum( 0.0, ds.landIcePressure + deltaLandIcePressure) ds_out['landIcePressure'] = \ landIcePressure.expand_dims(dim='Time', axis=0) final_ssh = init_ssh if convert_to_cdf5: name, ext = os.path.splitext(out_filename) write_filename = f'{name}_before_cdf5{ext}' else: write_filename = out_filename write_netcdf(ds_out, write_filename) if convert_to_cdf5: args = ['ncks', '-O', '-5', write_filename, out_filename] subprocess.check_call(args) # Write the largest change in SSH and its lon/lat to a file with open(f'maxDeltaSSH_{iter_index:03d}.log', 'w') as \ log_file: mask = landIcePressure > 0. icell = numpy.abs(delta_ssh.where(mask)).argmax().values ds_cell = ds.isel(nCells=icell) delta_ssh_max = delta_ssh.isel(nCells=icell).values if on_a_sphere: coords = (f'lon/lat: ' f'{numpy.rad2deg(ds_cell.lonCell.values):f} ' f'{numpy.rad2deg(ds_cell.latCell.values):f}') else: coords = (f'x/y: {1e-3 * ds_cell.xCell.values:f} ' f'{1e-3 * ds_cell.yCell.values:f}') string = (f'delta_ssh_max: ' f'{delta_ssh_max:g}, {coords}') logger.info(f' {string}') log_file.write(f'{string}\n') string = (f'ssh: {final_ssh.isel(nCells=icell).values:g}, ' f'landIcePressure: ' f'{landIcePressure.isel(nCells=icell).values:g}') logger.info(f' {string}') log_file.write(f'{string}\n') if delta_ssh_threshold is not None: if abs(delta_ssh_max) < delta_ssh_threshold: break logger.info(" - Complete\n") if out_filename is not None: shutil.copy(out_filename, 'adjusted_init.nc') with xarray.open_dataset('adjusted_init.nc') as ds: ds = ds.isel(Time=0) final_ssh = ds.ssh final_land_ice_pressure = ds.landIcePressure delta_ssh = final_ssh - orig_ssh masked_delta_ssh = modify_mask * delta_ssh delta_land_ice_pressure = \ final_land_ice_pressure - orig_land_ice_pressure masked_delta_land_ice_pressure = \ modify_mask * delta_land_ice_pressure ds_out = xarray.Dataset() ds_out['delta_ssh'] = delta_ssh ds_out['masked_delta_ssh'] = masked_delta_ssh ds_out['delta_land_ice_pressure'] = delta_land_ice_pressure ds_out['masked_delta_land_ice_pressure'] = \ masked_delta_land_ice_pressure write_netcdf(ds_out, 'total_delta.nc')