sivolume.py 5.76 KB
Newer Older
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
# coding=utf-8
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
"""Computes the seaice extent, area and volume"""
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
import six

import iris
import iris.analysis
import iris.coords
import iris.util
from bscearth.utils.log import Log

from earthdiagnostics.constants import Basins
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
from earthdiagnostics.diagnostic import (
    Diagnostic,
    DiagnosticBasinListOption,
)
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
from earthdiagnostics.modelingrealm import ModelingRealms
from earthdiagnostics.utils import Utils, TempFile


class Sivolume(Diagnostic):
    """
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
    Compute the sea ice volume from sivol in both hemispheres or a region.
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed

    Parameters
    ----------
    data_manager: DataManager
    startdate: str
    member: int
    chunk: init
    variable: str
    basin: list of Basin
    mask: numpy.array
    """

Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
    alias = "sivolume"
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
    "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,
    ):
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        Diagnostic.__init__(self, data_manager)
        self.startdate = startdate
        self.member = member
        self.chunk = chunk
        self.masks = masks
        self.generated = {}
        self.var_manager = var_manager
        self.data_convention = data_convention
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        self.sivol_varname = self.var_manager.get_variable("sivol").short_name
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed

        self.results = {}
        self.sivoln = {}
        self.sivols = {}
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed

    def __str__(self):
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        return (
            "Sivolume Startdate: {0.startdate} Member: {0.member} "
            "Chunk: {0.chunk} Basins: {1} ".format(
                self, ",".join(str(basin) for basin in self.masks.keys())
            )
        )
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed

Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
    def __hash__(self):
        return hash(str(self))

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

        :param diags: Diagnostics manager class
        :type diags: Diags
        :param options: basin
        :type options: list[str]
        :return:
        """
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        options_available = (
            DiagnosticBasinListOption("basins", Basins().Global.name),
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        )
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        options = cls.process_options(options, options_available)

Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        basins = options["basins"]
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        if not basins:
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
            Log.error("Basins not recognized")
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
            return ()

        masks = {}
        basins.sort()
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        e1t = iris.load_cube("mesh_hgr.nc", "e1t")
        e2t = iris.load_cube("mesh_hgr.nc", "e2t")
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        area = e1t * e2t

        for basin in basins:
            masks[basin] = Utils.get_mask(basin) * area.data

        job_list = list()
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        for (
            startdate,
            member,
            chunk,
        ) in diags.config.experiment.get_chunk_list():
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
            job_list.append(
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
                    diags.data_manager,
                    startdate,
                    member,
                    chunk,
                    masks,
                    diags.config.var_manager,
                    diags.config.data_convention,
                )
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        return job_list

    def request_data(self):
        """Request data required by the diagnostic"""
        self.sivol = self.request_chunk(
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
            ModelingRealms.seaIce,
            self.sivol_varname,
            self.startdate,
            self.member,
            self.chunk,
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        )

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

    def _declare_var(self, var_name):
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        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

    def compute(self):
        """Run the diagnostic"""
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        coordinates = " ".join(
            (
                "time",
                "leadtime",
                "time_centered",
                self.data_convention.lon_name,
                self.data_convention.lat_name,
            )
        )
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed

        handler = Utils.open_cdf(self.sivol.local_file)
        handler.variables[self.sivol_varname].coordinates = coordinates
        handler.close()
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        sivol = iris.load_cube(self.sivol.local_file)
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed

        for basin, mask in six.iteritems(self.masks):
            self.sivoln[basin] = self.sum(sivol, mask, north=True)
            self.sivols[basin] = self.sum(sivol, mask, north=False)
        del sivol

Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        self.save("sivoln", self.sivoln)
        self.save("sivols", self.sivols)
        del self.sivols
        del self.sivoln
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed

    def sum(self, data, mask, north=True):
        if north:
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
            condition = data.coord("latitude").points > 0
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        else:
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
            condition = data.coord("latitude").points < 0
        weights = (
            iris.util.broadcast_to_shape(
                condition, data.shape, data.coord_dims("latitude")
            )
            * mask
        )
        return data.collapsed(
            ("latitude", "longitude"), iris.analysis.SUM, weights=weights
        )
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed

    def save(self, var, results):
        cubes = iris.cube.CubeList()
        for basin, result in six.iteritems(results):
            result.var_name = var
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
            result.units = "m^3"
            result.add_aux_coord(
                iris.coords.AuxCoord(basin.name, var_name="region")
            )
            cubes.append(result)
        self._save_file(cubes.merge_cube(), var)
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed

    def _save_file(self, data, var):
        generated_file = self.generated[var]
        temp = TempFile.get()
        data.remove_coord('latitude')
        data.remove_coord('longitude')
        data.remove_coord(data.coord(var_name='i'))
        data.remove_coord(data.coord(var_name='j'))
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        iris.save(data, temp, zlib=True)
        generated_file.set_local_file(temp)