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 @ 13835

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

Improvment of STATION_ASF over sea-ice...

  • Property svn:executable set to *
File size: 10.4 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)
92cf_in    = []
93list_exp = []
94list_frc = []
95for fn in ldir:
96    fpn = dir_out+'/'+fn
97    if path.isfile(fpn):
98        if fn[:np]==cpref and fn[-ns:]==csuff and cforcing in fn:
99            print('\n file: '+fn)
100            clab = fn[np:-nc-ns]
101            [ cexp, cfrc ] = str.split(clab, '_', 1)
102            print('  ===> Experiment = '+cexp+', Forcing = '+cfrc)
103            list_exp.append(cexp)
104            list_frc.append(cfrc)
105            cf_in.append(fpn)
106nbf = len( set(list_frc) )
107
108if nbf == 0:
109    print('PROBLEM: we found no files corresponding to a forcing!')
110    sys.exit(0)
111print("list_frc =>",list_frc)
112if not nbf == 1:
113    print('PROBLEM: we found files for more that one forcing: ', set(list_frc))
114    sys.exit(0)
115
116nb_exp = len(list_exp)
117
118
119print('\n\nThere are '+str(nb_exp)+' experiments to compare:')
120for ja in range(nb_exp): print('  * '+list_exp[ja]+'\n'+'     ==> '+cf_in[ja]+'\n')
121
122if nb_exp > len(l_color):
123    print('PROBLEM: the max number of experiments for comparison is '+str(len(l_color))+' for now...')
124    sys.exit(0)
125
126# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
127# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
128
129
130def round_bounds( x1, x2,  base=5, prec=3 ):
131    rmin =  base * round( floor(float(x1)/base), prec )
132    rmax =  base * round(  ceil(float(x2)/base), prec )
133    return rmin, rmax
134
135
136# Getting time array from the first file:
137id_in = Dataset(cf_in[0])
138vt = id_in.variables['time_counter'][:]
139cunit_t = id_in.variables['time_counter'].units ; print(' "time_counter" is in "'+cunit_t+'"')
140id_in.close()
141Nt = len(vt)
142
143vtime = num2date(vt, units=cunit_t) ; # something human!
144vtime = vtime.astype(dtype='datetime64[D]')
145
146ii=Nt/300
147ib=max(ii-ii%10,1)
148xticks_d=int(30*ib)
149
150rat = 100./float(rDPI)
151params = { 'font.family':'Open Sans',
152           'font.size':       int(15.*rat),
153           'legend.fontsize': int(15.*rat),
154           'xtick.labelsize': int(15.*rat),
155           'ytick.labelsize': int(15.*rat),
156           'axes.labelsize':  int(16.*rat)
157}
158mpl.rcParams.update(params)
159font_inf = { 'fontname':'Open Sans', 'fontweight':'normal', 'fontsize':18.*rat }
160font_x   = { 'fontname':'Open Sans', 'fontweight':'normal', 'fontsize':15.*rat }
161
162
163# Now we compare output variables from bulk algorithms between them:
164
165nb_var = len(L_VNEM)
166
167xF  = nmp.zeros((Nt,nb_exp))
168xFa = nmp.zeros((Nt,nb_exp))
169
170
171for jv in range(nb_var):
172    print('\n *** Treating variable: '+L_VARO[jv]+' !')
173
174    for ja in range(nb_exp):
175        #
176        id_in = Dataset(cf_in[ja])
177        xF[:,ja] = id_in.variables[L_VNEM[jv]][:,1,1] # only the center point of the 3x3 spatial domain!
178        if ja == 0: cvar_lnm = id_in.variables[L_VNEM[jv]].long_name
179        id_in.close()
180
181    idx_okay = nmp.where( nmp.abs(xF) < 1.e+10 )
182
183    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
184    fig = plt.figure(num = jv, figsize=size_fig, facecolor='w', edgecolor='k')
185    ax1 = plt.axes([0.083, 0.23, 0.9, 0.7])
186    ax1.set_xticks(vtime[::xticks_d])
187    ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d %H:%M:%S'))
188    plt.xticks(rotation='60', **font_x)
189
190    for ja in range(nb_exp):
191        fplot = nmp.ma.masked_where( xF[:,ja]==0., xF[:,ja] )
192        plt.plot(vtime, fplot, '-', color=l_color[ja], \
193                 linestyle=l_style[ja], linewidth=l_width[ja], label=list_exp[ja], zorder=10+ja)
194
195    fmin, fmax = round_bounds( nmp.min(xF[idx_okay]) , nmp.max(xF[idx_okay]), base=L_BASE[jv], prec=L_PREC[jv])
196    ax1.set_ylim(fmin, fmax) ; ax1.set_xlim(vtime[0],vtime[Nt-1])
197    plt.ylabel(L_VARL[jv]+' ['+L_VUNT[jv]+']')
198
199    ax1.grid(color='k', linestyle='-', linewidth=0.3)
200    plt.legend(loc='best', ncol=1, shadow=True, fancybox=True)
201    ax1.annotate(cvar_lnm+', station: '+cforcing, xy=(0.5, 1.04), xycoords='axes fraction', \
202                 ha='center', bbox={'facecolor':'w', 'alpha':1., 'pad':10}, \
203                 zorder=50, **font_inf)
204    plt.savefig(L_VARO[jv]+'_'+cforcing+'_'+crealm+'.'+fig_ext, dpi=int(rDPI), transparent=False)
205    plt.close(jv)
206    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
207
208
209
210
211    def symetric_range( pmin, pmax ):
212        # Returns a symetric f-range that makes sense for the anomaly of "f" we're looking at...
213        from math import floor, copysign, log, ceil
214        zmax = max( abs(pmax) , abs(pmin) )
215        romagn = floor(log(zmax, 10)) ; # order of magnitude of the anomaly  we're dealing with
216        rmlt = 10.**(int(romagn)) / 2.
217        frng = copysign( ceil(abs(zmax)/rmlt)*rmlt , zmax)
218        return frng
219
220
221    if L_ANOM[jv]:
222
223        for ja in range(nb_exp): xFa[:,ja] = xF[:,ja] - nmp.mean(xF,axis=1)
224
225        if nmp.sum(nmp.abs(xFa[:,:])) == 0.0:
226            print('     Well! Seems that for variable '+L_VARO[jv]+', choice of algo has no impact a all!')
227            print('          ==> skipping anomaly plot...')
228
229        else:
230
231            yrng = symetric_range( nmp.min(xFa) , nmp.max(xFa) )
232
233            #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
234            fig = plt.figure(num = 10+jv, figsize=size_fig, facecolor='w', edgecolor='k')
235            ax1 = plt.axes([0.083, 0.23, 0.9, 0.7])
236            ax1.set_xticks(vtime[::xticks_d])
237            ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d %H:%M:%S'))
238            plt.xticks(rotation='60', **font_x)
239
240            for ja in range(nb_exp):
241                fplot = nmp.ma.masked_where( xF[:,ja]==0., xF[:,ja] )
242                plt.plot(vtime, fplot, '-', color=l_color[ja], \
243                         linewidth=l_width[ja], label=list_exp[ja], zorder=10+ja)
244
245            ax1.set_ylim(-yrng,yrng) ; ax1.set_xlim(vtime[0],vtime[Nt-1])
246            plt.ylabel(L_VARL[jv]+' ['+L_VUNT[jv]+']')
247            ax1.grid(color='k', linestyle='-', linewidth=0.3)
248            plt.legend(bbox_to_anchor=(0.45, 0.2), ncol=1, shadow=True, fancybox=True)
249            ax1.annotate('Anomaly of '+cvar_lnm+', station: '+cforcing, xy=(0.5, 1.04), xycoords='axes fraction', \
250                         ha='center', bbox={'facecolor':'w', 'alpha':1., 'pad':10}, \
251                         zorder=50, **font_inf)
252            plt.savefig(L_VARO[jv]+'_'+cforcing+'_anomaly_'+crealm+'.'+fig_ext, dpi=int(rDPI), transparent=False)
253            plt.close(10+jv)
254            #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Note: See TracBrowser for help on using the repository browser.