Warm pool

L’objectif est d’estimer l’accumulation d’eau à la fin de l’été au fond du Golfe de Gascogne, dans le modèles et les observations satellites.

Préalables

Un préalable essentiel aux diagnostics est la colocalisation des SST du modèle et satellite, lue toutes les deux vers minuit. Cela implique deux considérations importantes :

  • Les scripts doivent être capables de sappliquer à des données satellites sur grille curvilinéaire.
  • La colocalisation implique l’interpolation spatiale d’un des deux champs vers la grille de plus basse résolution.

La première étape consiste donc à comparer les résolutions des champs en entrée :

from vacumm.misc.grid import resol
from vacumm.misc.grid.basemap import merc

# Résolution du modèle
grid_mod = MARS3D(cfgmod, dates).get_grid()
xres_mod, yres_mod = resol(grid_mod)

# Résolution du modèle
grid_sat = Satellite(cfgsat, dates).get_grid()
xres_sat, yres_sat = resol(grid_sat)

# Conversions en mètres
xres_mod, yres_mod = merc(xres_mod, yres_mod)
xres_sat, yres_sat = merc(xres_sat, yres_sat)

# Resolution commune X & Y
res_mod = N.sqrt(xres_mod**2+yres_mod**2)
res_sat = N.sqrt(xres_sat**2+yres_sat**2)

Nous devons maintenant choisir quelle sera la grille cible (modèle ou satellite ?). On se base pour cela sur un facteur limite à définir, que l’on utilise pour comparer la résolution des deux grilles, en favorisant le type rectangulaire rectangulaire :

# Plus le facteur est faible, plus la résolution de la grille
# satellite curvilinéaire doit être fine pour que cette dernière soit choisie
ratio_curvgrid_choice = 0.8

# Maintenant, le choix
from vacumm.misc.grid import isgrid
if isgrid(grid_sat, curv=True):  # La grille satellite est bien curvilinéaire

    choose_sat = (res_mod / res_sat) < ratio_grid_choice

else: # Cas de deux grilles rectangulaires

    choose_sat = res_sat > res_mod

Nous devons maintenant colocaliser les champs :

# Minuit
from vacumm.misc.atime import midnight_interval
midnight = midnight_interval(date)
cdms2.setAutoBounds(1)

# Lecture des données vers minuit.
mod = MARS3D(cfgmod, midnight).load_sst()
sat = Satellite(cfgsat, midnight).load_sst()

# Regrillage
from vacumm.misc.grid.regridding import regrid2d
if choose_sat: # Vers grille satellite

    mod = regrid2d(mod, grid_sat)

else: # Vers grille modèle

    sat = regrid2d(sat, grid_mod)

Note

Par défaut, la méthode d’interpolation est choisie par la fonction regrid_method() en se basant sur le rapport des résolutions des deux grilles. Pour plus d’information, voir la section “user.desc.proc.regridding”.

Warning

Le regrillage vers une grille satellite curvilinéaire nécessite d’avoir l’exécutable scrip accessible dans la variable d’environnement PATH.

On suppose par la suite que les SST sont colocalisées.

Extension de la Warm pool à une date donnée

La warm pool satellite est tracée avec une palette grise sur un fond de SST du modèle, dont la warm pool est elle aussi mise en valeur (avec un contour, par exemple rouge) :

# Inits
from vacumm.misc.plot import map2

# Définition de la warm pool
wp_temp = 20
wp_color = 'r'
wp_linewidth = 1.5

# SST du modèle
levels_mod = N.arange(N.floor(mod.min()), N.ceil(mod.max()))
map2(mod, levels=levels_mod, show=False)
if (mod.asma().min()<=wp_temp and mod.asma().max()>=wp_temp):
    map2(mod, levels=wp_temp, linewidth=wp_linewidth,
        contour_colors=wp_color, fill=False, show=False)
else:
    print u'Pas de warm pool dans le modèle'

# SST satellite seulement sur la warm pool
sat = MV2.masked_less(sat, wp_temp) # On masque ailleurs
levels_sat = N.arange(N.floor(sat.min()), N.ceil(sat.max()))
if (sat.asma().min()<=wp_temp and sat.asma().max()>=wp_temp):
    map2(mod, levels=levels_sat, cmap='cmap_grey')
else:
    print u'Pas de warm pool dans les données satellites'

Évolution temporelle de la warm pool

Le but est de comparer une mesure de l’évolution temporelle de la warm pool du modèle et des données satellites. Cette dernière est estimée en $km^2$ dans trois cas différents, à partir des SST colocalisées :

  • Dans le modèle.
  • Dans le modèle, mais avec un masque commun aux observations.
  • Dans les observations, avec un masque commun au modèle.

À titre indicatif, une estimation du recouvrement des warm pool modélisée et observée est fournie à travers deux indices au cours du temps, en se basant sur les champs colocalisés avec même masque :

  • La fraction de warm pool commune rapportée à la warm pool modélisée.
  • La fraction de warm pool commune rapportée à la warm pool observée.

Pour procéder, nous devons effectuer une boucle temporelle sur les dates à minuit de l’interval de temps considéré. Afin d’optimiser un peu les traitements, nous pouvons créer une classe dédiée à la colocalisation des SST, dont le coeur se base ce qui a été présenté précédemment :

class Coloc(object):
    """Colocalize model and observation SST

    :Example:

        >>> coloc = Coloc(grid_mod, grid_sst, ratio = ratio_curvgrid_choice)
        >>> sst_mod, sst_sat = coloc(sst_mod, sst_sat)
    """

    def __init__(self, grid_mod, grid_sst, ratio=0.8):

        self.grid_mod = grid_mod
        self.grid_sat = grid_sat

        # Cf. plus haut
        [...]
        self.choose_sat = ...

    def __call__(self, sst_mod, sst_sat):

        if self.choose_sat:
            sst_mod = regrid2(sst_mod, self.grid_sat)
        else:
            sst_sat = regrid2(sst_sat, self.grid_mod)
        return sst_mod, sst_sat

La boucle temporelle peut s’écrire ainsi en supposant que l’ont traite les données par blocs de nmaxdays :

# Taille des blocs XYT en jours
nmaxdays = 30

# Colocalisateur
coloc = Coloc(grid_mod, grid_sat, ratio = ratio_curvgrid_choice)


# Initialisations
ext = dict(MOD=[], mod=[], sat=[]) # Extensions
rec = dict(mod=[], sat=[]) # Recouvrements
ctimes = []
areas = None

# On boucle sur les jours
from vacumm.misc.atime import IterDates, Intervals
for interval in Intervals((date_min, date_max), (nmaxdays, 'day')):

    # Lecture
    block_mod = MARS3D(cfgmod, interval).load_sst()
    block_sat = Satellite(cfgsat, interval).load_sst()

    # Colocalisation
    mod, sat = coloc(mod, sat)

    # Surface de chaque cellule
    if areas is None:
        from vacumm.misc.grid import meshweights, get_xy
        xx, yy = get_xy(mod.getGrid(), num=True)
        areas = meshweights(xx, yy, proj=True) # proj -> m2
        areas *= 1e-6 # km2

    # Boucle à minuit
    for date in IterDates(midnight_interval(interval, (1, 'day'))):

        # Extraction
        try:
            mod = block_mod(time=(date, date, 'ccb'), squeeze=1)
        except:
            continue
        try:
            sat = block_sat(time=(date, date, 'ccb'), squeeze=1)
        except:
            continue

        # Calcul des masques
        wp_mod = (mod.asma()>wp_temp).astype('f').filled(0.)
        wp_sat = (sat.asma()>wp_temp).astype('f').filled(0.)

        # Aire totale dans le modèle
        ext['MOD'].append((wp_mod*areas).sum())

        # Fusion des masques modèle/satellite
        mask = (sat.mask | mod.mask).astype('f')

        # Aire du modèle masqué
        ext['mod'].append((wp_mod*mask*areas).sum())

        # Aire des données satellites
        ext['sat'].append((wp_sat*mask*areas).sum())

        # Recouvrement
        intersect = (wp_mod*wp_sat).sum()
        rec['mod'].append(-1. if ext['mod']==0. else intersect/ext['mod'])
        rec['sat'].append(-1. if ext['sat']==0. else intersect/ext['sat'])

        # Dates
        ctimes.extend(mod.getTime().asComponentTime())

# Concatenations et formattages
from vacumm.misc.axes import create_time
taxis = create_time(ctimes)
for dd in ext, rec:
    for key, values in dd.items():
        dd[key] = MV2.array(values)
        dd[key].setAxis(0, taxis)
for var in ext.values():
    var.units = 'km^2'
for var in rec.values():
    var[:] = MV2.masked_less(var, 0.)
    var.units = '%%'
    var[:] *= 100.
ext['MOD'].long_name = 'Full model warm pool extent'
ext['mod'].long_name = 'Model warm pool extent restricted to satellite'
ext['sat'].long_name = 'Satellite warm pool extent'
rec['mod'].long_name = 'Fraction of model warm pool common to satellite'
rec['sat'].long_name = 'Fraction of satellite warm pool common to model'

# Tracé des extensions
from vacumm.misc.plot import curve2
curve2(ext['MOD'], 'r--', vmin=0, show=False)
curve2(ext['mod'], 'r-', show=False)
curve2(ext['sat'], 'b-', show=False).legend(loc='upper left')

# Tracé des fractions de recouvrement
curve2(rec['mod'], 'r+', twin='x', vmin=0, vmax=100, show=False)
curve2(rec['sat'], 'bs').legend(loc='upper right')

Note

On suppose ici que date_max désigne une borne ouverte de l’intervalle de temps : elle n’est donc jamais atteinte explicitement. Ainsi, l’intervalle ('2000', '2001') ne bouclera que sur l’année 2000.