New URL for NEMO forge!   http://forge.nemo-ocean.eu

Since March 2022 along with NEMO 4.2 release, the code development moved to a self-hosted GitLab.
This present forge is now archived and remained online for history.
plot_station_asf_ICE.py in NEMO/branches/2020/dev_r13648_ASINTER-04_laurent_bulk_ice/tests/STATION_ASF/EXPREF – NEMO

source: NEMO/branches/2020/dev_r13648_ASINTER-04_laurent_bulk_ice/tests/STATION_ASF/EXPREF/plot_station_asf_ICE.py @ 13781

Last change on this file since 13781 was 13781, checked in by laurent, 3 years ago

Improvements diags and doc for STATION_ASF

  • Property svn:executable set to *
File size: 10.2 KB
Line 
1#!/usr/bin/env python3
2# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
3#
4##########################################################################################
5# Post-diagnostic of STATION_ASF for air-ice fluxes (over sea-ice)
6#
7#  L. Brodeau, 2020
8##########################################################################################
9
10import sys
11from os import path, listdir
12import argparse as ap
13from math import floor, ceil, copysign, log
14import numpy as nmp
15from netCDF4 import Dataset,num2date
16import matplotlib as mpl
17mpl.use('Agg')
18import matplotlib.pyplot as plt
19import matplotlib.dates as mdates
20
21dir_figs='.'
22size_fig=(13,8.5)
23size_fig0=(12,10)
24
25clr_red = '#AD0000'
26clr_mod = '#008ab8'
27
28rDPI=100.
29
30l_color = [ '0.85'    ,  '#ffed00' , '#008ab8' , '0.4'  ] ; # colors to differentiate algos on the plot
31l_width = [  4        ,     3      ,    2      ,  1     ] ; # line-width to differentiate algos on the plot
32l_style = [ '-'       ,    '-'     ,   '-'     , '--'   ] ; # line-style
33
34
35# Variables to compare between algorithms
36############################################
37crealm = 'sea-ice'
38L_VNEM = [   'Cd_ice',   'Ch_ice',   'qla_ice' ,   'qsb_ice'   ,  'qt_ice'    ,  'qlw_ice'  ,  'qsr_ice'  , 'taum_ai'   ]
39L_VARO = [     'Cd'  ,     'Ch'  ,   'Qlat'    ,    'Qsen'     ,   'Qnet'     ,   'Qlw'     ,    'Qsw'    ,  'Tau'      ]
40L_VARL = [ r'$C_{D}$', r'$C_{H}$', r'$Q_{lat}$', r'$Q_{sens}$' , r'$Q_{net}$' , r'$Q_{lw}$' , r'$Q_{sw}$' , r'$|\tau|$' ]
41L_VUNT = [     ''    ,     ''    , r'$W/m^2$'  , r'$W/m^2$'    , r'$W/m^2$'   , r'$W/m^2$'  , r'$W/m^2$'  , r'$N/m^2$'  ]
42L_BASE = [    0.0005 ,    0.0005 ,      5.     ,     5.        ,     5.       ,    5.       ,     5.      ,    0.05     ]
43L_PREC = [     3     ,     3     ,      0     ,      0        ,      0      ,      0        ,     0       ,     2       ]
44L_ANOM = [   False   ,   False   ,   True      ,    True       ,    True      ,    True     ,    True     ,   True      ]
45
46# About STATION_ASF output files to read:
47cpref = 'STATION_ASF-'          ; np = len(cpref)
48csuff = '_icemod.nc'            ; ns = len(csuff)
49cclnd = '_1h_YYYY0101_YYYY1231' ; nc = len(cclnd)
50
51
52################## ARGUMENT PARSING / USAGE ################################################################################################
53parser = ap.ArgumentParser(description='Generate pixel maps of a given scalar.')
54#
55requiredNamed = parser.add_argument_group('required arguments')
56requiredNamed.add_argument('-d', '--dirout' , required=True,                 help='Path to (production) directory where STATION_ASF was run')
57requiredNamed.add_argument('-f', '--forcing', required=True, default="PAPA", help='Name of forcing (ex: PAPA, ERA5_arctic')
58#
59parser.add_argument('-C', '--conf',   default="STATION_ASF",  help='specify NEMO config (ex: STATION_ASF)')
60parser.add_argument('-s', '--ystart', default="2018",         help='specify first year of experiment (ex: 2018)')
61parser.add_argument('-e', '--yend',   default="2018",         help='specify last  year of experiment (ex: 2018)')
62#
63parser.add_argument('-t', '--itype',   default="png",         help='specify the type of image you want to create (ex: png, svg, etc.)')
64#parser.add_argument('-l', '--lev' , type=int, default=0,    help='specify the level to use if 3D field (default: 0 => 2D)')
65#parser.add_argument('-I', '--ice' , action='store_true',    help='draw sea-ice concentration layer onto the field')
66#
67args = parser.parse_args()
68#
69cdir_data = args.dirout
70cforcing  = args.forcing
71#
72CONF      = args.conf
73cy1       = args.ystart
74cy2       = args.yend
75#
76fig_ext   = args.itype
77#jk    = args.lev
78#lshow_ice = args.ice
79#
80#print(''); print(' *** cdir_data = ', cdir_data); print(' *** cforcing  = ', cforcing)
81#print(' *** CONF = ', CONF); print(' *** cy1 = ', cy1); print(' *** cy2 = ', cy2)
82###############################################################################################################################################
83
84
85
86# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
87# Populating and checking existence of files to be read
88# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
89
90dir_out = cdir_data+'/output'
91ldir = listdir(dir_out)
92
93cf_in    = []
94list_exp = []
95list_frc = []
96for fn in ldir:
97    fpn = dir_out+'/'+fn
98    if path.isfile(dir_out+'/'+fn):
99        if fn[:np]==cpref and fn[-ns:]==csuff and cforcing in fn:
100            print('\n file: '+fn)
101            clab = fn[np:-nc-ns]
102            [ cexp, cfrc ] = str.split(clab, '_', 1)
103            print('  ===> Experiment = '+cexp+', Forcing = '+cfrc)
104            list_exp.append(cexp)
105            list_frc.append(cfrc)
106            cf_in.append(fpn)
107nbf = len( set(list_frc) )
108if not nbf == 1:
109    print('PROBLEM: we found files for more that one forcing: ', set(list_frc))
110    sys.exit(0)
111
112nb_exp = len(list_exp)
113
114
115print('\n\nThere are '+str(nb_exp)+' experiments to compare:')
116for ja in range(nb_exp): print('  * '+list_exp[ja]+'\n'+'     ==> '+cf_in[ja]+'\n')
117
118if nb_exp > len(l_color):
119    print('PROBLEM: the max number of experiments for comparison is '+str(len(l_color))+' for now...')
120    sys.exit(0)
121
122
123
124#-----------------------------------------------------------------
125
126
127def round_bounds( x1, x2,  base=5, prec=3 ):
128    rmin =  base * round( floor(float(x1)/base), prec )
129    rmax =  base * round(  ceil(float(x2)/base), prec )
130    return rmin, rmax
131
132
133# Getting time array from the first file:
134id_in = Dataset(cf_in[0])
135vt = id_in.variables['time_counter'][:]
136cunit_t = id_in.variables['time_counter'].units ; print(' "time_counter" is in "'+cunit_t+'"')
137id_in.close()
138Nt = len(vt)
139
140vtime = num2date(vt, units=cunit_t) ; # something human!
141vtime = vtime.astype(dtype='datetime64[D]')
142
143ii=Nt/300
144ib=max(ii-ii%10,1)
145xticks_d=int(30*ib)
146
147rat = 100./float(rDPI)
148params = { 'font.family':'Open Sans',
149           'font.size':       int(15.*rat),
150           'legend.fontsize': int(15.*rat),
151           'xtick.labelsize': int(15.*rat),
152           'ytick.labelsize': int(15.*rat),
153           'axes.labelsize':  int(16.*rat)
154}
155mpl.rcParams.update(params)
156font_inf = { 'fontname':'Open Sans', 'fontweight':'normal', 'fontsize':18.*rat }
157font_x   = { 'fontname':'Open Sans', 'fontweight':'normal', 'fontsize':15.*rat }
158
159
160# Now we compare output variables from bulk algorithms between them:
161
162nb_var = len(L_VNEM)
163
164xF  = nmp.zeros((Nt,nb_exp))
165xFa = nmp.zeros((Nt,nb_exp))
166
167
168for jv in range(nb_var):
169    print('\n *** Treating variable: '+L_VARO[jv]+' !')
170
171    for ja in range(nb_exp):
172        #
173        id_in = Dataset(cf_in[ja])
174        xF[:,ja] = id_in.variables[L_VNEM[jv]][:,1,1] # only the center point of the 3x3 spatial domain!
175        if ja == 0: cvar_lnm = id_in.variables[L_VNEM[jv]].long_name
176        id_in.close()
177
178    idx_okay = nmp.where( nmp.abs(xF) < 1.e+10 )
179
180    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
181    fig = plt.figure(num = jv, figsize=size_fig, facecolor='w', edgecolor='k')
182    ax1 = plt.axes([0.083, 0.23, 0.9, 0.7])
183    ax1.set_xticks(vtime[::xticks_d])
184    ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d %H:%M:%S'))
185    plt.xticks(rotation='60', **font_x)
186
187    for ja in range(nb_exp):
188        fplot = nmp.ma.masked_where( xF[:,ja]==0., xF[:,ja] )
189        plt.plot(vtime, fplot, '-', color=l_color[ja], \
190                 linestyle=l_style[ja], linewidth=l_width[ja], label=list_exp[ja], zorder=10+ja)
191
192    fmin, fmax = round_bounds( nmp.min(xF[idx_okay]) , nmp.max(xF[idx_okay]), base=L_BASE[jv], prec=L_PREC[jv])
193    ax1.set_ylim(fmin, fmax) ; ax1.set_xlim(vtime[0],vtime[Nt-1])
194    plt.ylabel(L_VARL[jv]+' ['+L_VUNT[jv]+']')
195
196    ax1.grid(color='k', linestyle='-', linewidth=0.3)
197    plt.legend(loc='best', ncol=1, shadow=True, fancybox=True)
198    ax1.annotate(cvar_lnm+', station: '+cforcing, xy=(0.5, 1.04), xycoords='axes fraction', \
199                 ha='center', bbox={'facecolor':'w', 'alpha':1., 'pad':10}, \
200                 zorder=50, **font_inf)
201    plt.savefig(L_VARO[jv]+'_'+cforcing+'_'+crealm+'.'+fig_ext, dpi=int(rDPI), transparent=False)
202    plt.close(jv)
203    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
204
205
206
207
208    def symetric_range( pmin, pmax ):
209        # Returns a symetric f-range that makes sense for the anomaly of "f" we're looking at...
210        from math import floor, copysign, log, ceil
211        zmax = max( abs(pmax) , abs(pmin) )
212        romagn = floor(log(zmax, 10)) ; # order of magnitude of the anomaly  we're dealing with
213        rmlt = 10.**(int(romagn)) / 2.
214        frng = copysign( ceil(abs(zmax)/rmlt)*rmlt , zmax)
215        return frng
216
217
218    if L_ANOM[jv]:
219
220        for ja in range(nb_exp): xFa[:,ja] = xF[:,ja] - nmp.mean(xF,axis=1)
221
222        if nmp.sum(nmp.abs(xFa[:,:])) == 0.0:
223            print('     Well! Seems that for variable '+L_VARO[jv]+', choice of algo has no impact a all!')
224            print('          ==> skipping anomaly plot...')
225
226        else:
227
228            yrng = symetric_range( nmp.min(xFa) , nmp.max(xFa) )
229
230            #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
231            fig = plt.figure(num = 10+jv, figsize=size_fig, facecolor='w', edgecolor='k')
232            ax1 = plt.axes([0.083, 0.23, 0.9, 0.7])
233            ax1.set_xticks(vtime[::xticks_d])
234            ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d %H:%M:%S'))
235            plt.xticks(rotation='60', **font_x)
236
237            for ja in range(nb_exp):
238                fplot = nmp.ma.masked_where( xF[:,ja]==0., xF[:,ja] )
239                plt.plot(vtime, fplot, '-', color=l_color[ja], \
240                         linewidth=l_width[ja], label=list_exp[ja], zorder=10+ja)
241
242            ax1.set_ylim(-yrng,yrng) ; ax1.set_xlim(vtime[0],vtime[Nt-1])
243            plt.ylabel(L_VARL[jv]+' ['+L_VUNT[jv]+']')
244            ax1.grid(color='k', linestyle='-', linewidth=0.3)
245            plt.legend(bbox_to_anchor=(0.45, 0.2), ncol=1, shadow=True, fancybox=True)
246            ax1.annotate('Anomaly of '+cvar_lnm+', station: '+cforcing, xy=(0.5, 1.04), xycoords='axes fraction', \
247                         ha='center', bbox={'facecolor':'w', 'alpha':1., 'pad':10}, \
248                         zorder=50, **font_inf)
249            plt.savefig(L_VARO[jv]+'_'+cforcing+'_anomaly_'+crealm+'.'+fig_ext, dpi=int(rDPI), transparent=False)
250            plt.close(10+jv)
251            #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Note: See TracBrowser for help on using the repository browser.