maxmoc.py 8.28 KB
Newer Older
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
# coding=utf-8
import netCDF4
import numpy as np
import os
from autosubmit.config.log import Log
from earthdiagnostics.constants import Basins
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
from earthdiagnostics.box import Box
from earthdiagnostics.diagnostic import Diagnostic
from earthdiagnostics.utils import Utils


class MaxMoc(Diagnostic):
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
    """
    Compute an Atlantic MOC index by finding the maximum  of the annual
    mean meridional overturning in a latitude / depth region

    :original author: Virginie Guemas <virginie.guemas@bsc.es>
    :contributor: Javier Vegas-Regidor<javier.vegas@bsc.es>

    :created: March 2012
    :last modified: June 2016
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
    :param data_manager: data management object
    :type data_manager: DataManager
    :param startdate: startdate
    :type startdate: str
    :param member: member number
    :type member: int
    :param year: year to compute
    :type year: int
    :param basin: basin to compute
    :type basin: Basin
    :param box: box to compute
    :type box: Box
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
    """

    def __init__(self, data_manager, startdate, member, year, basin, box):
        Diagnostic.__init__(self, data_manager)
        self.basin = basin
        self.startdate = startdate
        self.member = member
        self.year = year
        self.required_vars = ['vo']
        self.generated_vars = ['vsftmyz']
        self.box = box

Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
    def __str__(self):
        return 'Max moc Startdate: {0} Member: {1} Year: {2} Basin: {3}'.format(self.startdate, self.member,
                                                                                self.year, self.box)

    @classmethod
    def generate_jobs(cls, diags, options):
        """
        Creates a job for each complete year to compute the diagnostic

        :param diags: Diagnostics manager class
        :type diags: Diags
        :param options: minimum latitude, maximum latitude, minimum depth, maximum depth, basin=global
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        :type options: list[str]
        num_options = len(options) - 1
        if num_options < 4:
            raise Exception('You must specify the box to use')
        if num_options > 5:
            raise Exception('You must specify between 4 and 5 parameters for area moc diagnostic')
        box = Box()
        box.min_lat = int(options[1])
        box.max_lat = int(options[2])
        box.min_depth = int(options[3])
        box.max_depth = int(options[4])
        if num_options > 4:
            basin = Basins.parse(options[5])
        else:
            basin = Basins.Global

        job_list = list()
        for startdate in diags.startdates:
            for member in diags.members:
                years = diags.exp_manager.get_full_years(startdate)
                if len(years) == 0:
                    Log.user_warning('No complete years are available with the given configuration. '
                                     'MaxMoc can not be computed')
                for year in years:
                    job_list.append(MaxMoc(diags.data_manager, startdate, member, year, basin, box))
        return job_list

    def compute(self):
        """
        Runs the diagnostic
        """
        nco = Utils.nco

        temp = self.data_manager.get_year('ocean', 'vsftmyz', self.startdate, self.member, self.year)

        handler = Utils.openCdf(temp)
        if 'i' in handler.dimensions:
            handler.close()
            nco.ncwa(input=temp, output=temp, options='-O -a i')
        else:
            handler.close()
        handler = Utils.openCdf(temp)
        basin_index = np.where(handler.variables['basin'][:] == self.basin.fullname)
        if len(basin_index) == 0:
            raise Exception("Basin {1} is not defined in {0}", temp, self.basin.fullname)
        basin_index = basin_index[0][0]

        lev = handler.variables['lev'][:]
        lat = handler.variables['lat'][:]

        if self.box.min_lat == self.box.max_lat:
            lat_inds = ((np.abs(lat-self.box.min_lat)).argmin(),)
        else:
            lat_inds = np.where((lat > self.box.min_lat) & (lat < self.box.max_lat))[0]

        if self.box.min_depth == self.box.max_depth:
            lev_inds = ((np.abs(lev - self.box.min_depth)).argmin(),)
        else:
            lev_inds = np.where((lev > self.box.min_depth) & (lev < self.box.max_depth))[0]

        Log.info('Computing year {0}', str(self.year))
        moc = handler.variables['vsftmyz'][:, lev_inds, lat_inds, basin_index]
        handler.close()
        os.remove(temp)

        moc = np.mean(moc, 0)

        maximum = np.amax(moc)
        max_index = np.unravel_index(np.argmax(moc), moc.shape)
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        # noinspection PyUnresolvedReferences
        max_lev = lev[lev_inds[max_index[0]]]
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        # noinspection PyUnresolvedReferences
        max_lat = lat[lat_inds[max_index[1]]]

        minimum = np.amin(moc)
        minimum_index = np.unravel_index(np.argmin(moc), moc.shape)
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        # noinspection PyUnresolvedReferences
        min_lev = lev[lev_inds[minimum_index[0]]]
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        # noinspection PyUnresolvedReferences
        min_lat = lat[lat_inds[minimum_index[1]]]

        Log.info('Maximum {0} Sv, latitude {1} depth {2} m', maximum, max_lat, max_lev)
        Log.info('Minimum {0} Sv, latitude {1} depth {2} m', minimum, min_lat, min_lev)

        handler = self._create_output_file(temp)
        var = handler.createVariable('vsftmyzmax', float, ('time',))
        var.long_name = 'Maximum_Overturing'
        var.units = 'Sverdrup'
        var.valid_min = -1000.
        var.valid_max = 1000.
        var[0] = maximum
        handler.close()
        self.data_manager.send_file(temp, 'ocean', 'vsftmyzmax', self.startdate, self.member, box=self.box,
                                    frequency='yr', year=self.year)

        handler = self._create_output_file(temp)
        var = handler.createVariable('vsftmyzmaxlat', float, ('time',))
        var.long_name = 'Latitude_of_Maximum_Overturing'
        var.units = 'Degrees'
        var.valid_min = -90.
        var.valid_max = 90.
        var[0] = max_lat
        handler.close()
        self.data_manager.send_file(temp, 'ocean', 'vsftmyzmax', self.startdate, self.member, box=self.box,
                                    frequency='yr', year=self.year)

        handler = self._create_output_file(temp)
        var = handler.createVariable('vsftmyzmaxlev', float, ('time',))
        var.long_name = 'Depth_of_Maximum_Overturing'
        var.units = 'Meters'
        var.valid_min = 0.
        var.valid_max = 10000.
        var[0] = max_lev
        handler.close()
        self.data_manager.send_file(temp, 'ocean', 'vsftmyzmax', self.startdate, self.member, box=self.box,
                                    frequency='yr', year=self.year)

        handler = self._create_output_file(temp)
        var = handler.createVariable('vsftmyzmin', float, ('time',))
        var.long_name = 'Minimum_Overtuning'
        var.units = 'Sverdrup'
        var.valid_min = -1000.
        var.valid_max = 1000.
        var[0] = minimum
        handler.close()
        self.data_manager.send_file(temp, 'ocean', 'vsftmyzmax', self.startdate, self.member, box=self.box,
                                    frequency='yr', year=self.year)

        handler = self._create_output_file(temp)
        var = handler.createVariable('vsftmyzminlat', float, ('time',))
        var.long_name = 'Latitude_of_Minimum_Overtuning'
        var.units = 'Degrees'
        var.valid_min = -90.
        var.valid_max = 90.
        var[0] = min_lat
        handler.close()
        self.data_manager.send_file(temp, 'ocean', 'vsftmyzmax', self.startdate, self.member, box=self.box,
                                    frequency='yr', year=self.year)

        handler = self._create_output_file(temp)
        var = handler.createVariable('vsftmyzminlev', float, ('time',))
        var.long_name = 'Depth_of_Minimum_Overtuning'
        var.units = 'Meters'
        var.valid_min = 0.
        var.valid_max = 10000.
        var[0] = min_lev
        handler.close()
        self.data_manager.send_file(temp, 'ocean', 'vsftmyzmax', self.startdate, self.member, box=self.box,
                                    frequency='yr', year=self.year)

    def _create_output_file(self, temp):
        handler = netCDF4.Dataset(temp, 'w')
        handler.createDimension('time')

        time = handler.createVariable('time', 'i2', ('time',))
        time.calendar = 'gregorian'
        time.units = 'days since January 1, {0}'.format(self.year)
        return handler