siasiesiv.py 7.85 KB
Newer Older
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
# coding=utf-8
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
"""Compute the sea ice extent , area and volume  in both hemispheres or a specified region"""
sloosvel's avatar
sloosvel committed
import os
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
import numpy as np

sloosvel's avatar
sloosvel committed
import netCDF4

import iris
import iris.analysis
import iris.coords
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
from bscearth.utils.log import Log
from earthdiagnostics.constants import Basins
from earthdiagnostics.diagnostic import Diagnostic, DiagnosticBasinListOption, DiagnosticBoolOption
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
from earthdiagnostics.modelingrealm import ModelingRealms
from earthdiagnostics.utils import Utils, TempFile
sloosvel's avatar
sloosvel committed
import diagonals.siasie as siasie
from diagonals.mesh_helpers.nemo import Nemo

# noinspection PyUnresolvedReferences


Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
    Compute the sea ice extent , area and volume  in both hemispheres or a specified region.

Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
    Parameters
    ----------
    data_manager: DataManager
    startdate: str
    member: int
    chunk: init
    domain: ModellingRealm
    variable: str
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
    mask: numpy.array
    omit_vol: bool
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
    """
    alias = 'siasiesiv'
    "Diagnostic alias for the configuration file"

Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
    def __init__(self, data_manager, startdate, member, chunk, masks, var_manager, data_convention, omit_vol):
        Diagnostic.__init__(self, data_manager)
        self.startdate = startdate
        self.member = member
        self.chunk = chunk
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        self.generated = {}
        self.var_manager = var_manager
        self.omit_volume = omit_vol
        self.sic_varname = self.var_manager.get_variable('sic').short_name
        self.sit_varname = self.var_manager.get_variable('sit').short_name
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        self.data_convention = data_convention
sloosvel's avatar
sloosvel committed
        for var in ('siarean', 'siareas', 'siextentn', 'siextents'):
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
    def __str__(self):
        return 'Siasiesiv Startdate: {0.startdate} Member: {0.member} Chunk: {0.chunk} ' \
               'Basins: {1} Omit volume: {0.omit_volume}'.format(self,
                                                                 ','.join(str(basin) for basin in self.masks.keys()))
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed

    def generate_jobs(cls, diags, options):
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        Create a job for each chunk to compute the diagnostic

        :param diags: Diagnostics manager class
        :type diags: Diags
        :param options: basin
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        :type options: list[str]
        options_available = (DiagnosticBasinListOption('basins', [Basins().Global]),
sloosvel's avatar
sloosvel committed
                             DiagnosticBoolOption('omit_volume', True))
        options = cls.process_options(options, options_available)
        basins = options['basins']
        if not basins:
        basins.sort()
        for basin in basins:
        for startdate, member, chunk in diags.config.experiment.get_chunk_list():
            job_list.append(Siasiesiv(diags.data_manager, startdate, member, chunk, masks,
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
                                      diags.config.var_manager, diags.config.data_convention,
                                      options['omit_volume']))
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
    def request_data(self):
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        """Request data required by the diagnostic"""
        if not self.omit_volume:
            self.sit = self.request_chunk(ModelingRealms.seaIce, self.sit_varname,
                                          self.startdate, self.member, self.chunk)
        self.sic = self.request_chunk(ModelingRealms.seaIce, self.sic_varname,
                                      self.startdate, self.member, self.chunk)
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed

    def declare_data_generated(self):
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        """Declare data to be generated by the diagnostic"""
        if not self.omit_volume:
            self._declare_var('sivols')
            self._declare_var('sivoln')

Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        self._declare_var('siareas')
        self._declare_var('siextents')

        self._declare_var('siarean')
        self._declare_var('siextentn')

    def _declare_var(self, var_name):
        self.generated[var_name] = self.declare_chunk(ModelingRealms.seaIce, var_name,
                                                      self.startdate, self.member, self.chunk)
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        """Run the diagnostic"""
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed

        self._fix_coordinates_attribute(
            self.sic.local_file, self.sic_varname
        )
        sic = iris.load_cube(self.sic.local_file)
        if sic.units.origin == '%' and sic.data.max() < 2:
sloosvel's avatar
sloosvel committed
        sic_slices = []
        for sic_data in sic.slices_over('time'):
            sic_data.data = np.ma.filled(sic_data.data, 0.0).astype(np.float32)
            sic_slices.append(sic_data)
        mesh = Nemo('mesh_hgr.nc', 'mask_regions.nc')
        areacello = mesh.get_areacello(cell_point='T')
        gphit = mesh.get_grid_latitude(cell_point='T')
        self.results['siextentn'], self.results['siextents'], self.results['siarean'], self.results['siareas'] = siasie.compute(gphit, areacello, sic_slices, self.masks)
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
    def _fix_coordinates_attribute(self, filepath, var_name):
        add_coordinates = {
            'time', 'leadtime', 'time_centered',
            self.data_convention.lon_name, self.data_convention.lat_name
        }
        handler = Utils.open_cdf(filepath)
        coordinates = handler.variables[var_name].coordinates.split()
        handler.variables[var_name].coordinates = \
            ' '.join(set(coordinates) | add_coordinates)
        handler.close()

sloosvel's avatar
sloosvel committed
        for var, time in six.iteritems(self.results):
sloosvel's avatar
sloosvel committed
            for time, basin in six.iteritems(time):
                for basin, result in six.iteritems(basin):
                    cube = iris.cube.Cube(result)
                    cube.var_name = var
                    cube.units = 'm^2'
                    cube.add_aux_coord(iris.coords.AuxCoord(basin.name, var_name='region'))
                    cube.add_aux_coord(iris.coords.AuxCoord(time, var_name='time'))
                    results.append(cube)
            self._save_file(results.merge_cube(), var)
sloosvel's avatar
sloosvel committed

    def _save_file(self, data, var):
        generated_file = self.generated[var]
        region = data.coord('region').points
        data.remove_coord('region')
        iris.save(data, temp, zlib=True)
        if len(region) > 1:
sloosvel's avatar
sloosvel committed
            Utils.rename_variable(temp, 'dim1', 'region', False)
            handler = Utils.open_cdf(temp)
            var = handler.createVariable('region2', str, ('region',))
            var[...] = region
            handler.close()
            Utils.rename_variable(temp, 'region2', 'region', True)
        else:
            handler = Utils.open_cdf(temp)
            if 'region' not in handler.dimensions:
                new_file = TempFile.get()
                new_handler = Utils.open_cdf(new_file, 'w')

                new_handler.createDimension('region', 1)
                for dimension in handler.dimensions:
                    Utils.copy_dimension(handler, new_handler, dimension)

                for variable in handler.variables.keys():
                    if variable in (var, 'region'):
                        continue
                    Utils.copy_variable(handler, new_handler, variable)
                old_var = handler.variables[var]
                new_var = new_handler.createVariable(var, old_var.dtype, ('region',) + old_var.dimensions,
                                                     zlib=True, fill_value=1.0e20)
                Utils.copy_attributes(new_var, old_var)
                new_var[0, :] = old_var[:]

                new_var = new_handler.createVariable('region', str, ('region',))
                new_var[0] = region[0]

                new_handler.close()
                os.remove(temp)
                temp = new_file
            handler.close()
        generated_file.set_local_file(temp)