source: codes/icosagcm/devel/Python/test/py/Baroclinic_3D_ullrich.py @ 787

Last change on this file since 787 was 787, checked in by dubos, 6 years ago

devel/Python : XIOS output on dual mesh

File size: 13.6 KB
Line 
1from dynamico import unstructured as unst
2from dynamico import dyn
3from dynamico import time_step
4from dynamico import DCMIP
5from dynamico import meshes
6from dynamico import xios
7from dynamico import precision as prec
8from dynamico.meshes import Cartesian_mesh as Mesh
9
10import math as math
11import numpy as np
12import time
13import argparse
14import logging
15from numpy import pi, log, exp, sin, cos
16
17# Baroclinic instability test based on Ullrich et al. 2015, QJRMS
18
19parser = argparse.ArgumentParser()
20
21parser.add_argument("--mpi_ni", type=int, default=1,
22                    help="number of x processors")
23parser.add_argument("--mpi_nj", type=int, default=1,
24                    help="number of y processors")
25parser.add_argument("--T", type=float, default=3600.,
26                    help="Length of time slice in seconds")
27parser.add_argument("--N", type=int, default=48,
28                    help="Number of time slices")
29parser.add_argument("--log",type=str,default='DEBUG',choices=('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'),
30                    help="Set verbosity level of logging")
31parser.add_argument("--Davies_N1", type=int, default=3)
32parser.add_argument("--Davies_N2", type=int, default=3)
33parser.add_argument("--llm", type=int, default=50)
34parser.add_argument("--nx", type=int, default=200)
35parser.add_argument("--ny", type=int, default=30)
36parser.add_argument("--dt", type=float, default=360., help='Time step in seconds')
37parser.add_argument("--hydrostatic", action='store_true')
38parser.add_argument("--betaplane", action='store_true')
39
40args = parser.parse_args()
41
42
43def baroclinic_3D(Lx,nx,Ly,ny,llm,ztop=25000.):
44    Rd    = 287.0      # Gas constant for dryy air (j kg^-1 K^-1)
45    T0    = 288.0      # Reference temperature (K)
46    lap   = 0.005      # Lapse rate (K m^-1)
47    b     = 2.         # Non dimensional vertical width parameter
48    u0    = 35.        # Reference zonal wind speed (m s^-1)
49    a     = 6.371229e6 # Radius of the Earth (m)
50    ptop  = 2000.
51    y0    = .5*Ly
52    Cpd   = 1004.5
53    p0    = 1e5
54    up    = 1. # amplitude (m/s)
55    xc,yc,lp = 0.,Ly/2.,600000.
56
57    omega  = 7.292e-5  # Angular velocity of the Earth (s^-1)   
58    phi0   = 45.*np.pi/180.0 # Reference latitude North pi/4 (deg)
59    f0     = 2*omega*np.sin(phi0) 
60    beta0  = 2*omega*np.cos(phi0)/a if args.betaplane else 0.
61    fb     = f0 - beta0*y0
62
63    def Phi_xy(y):
64        fc = y*y - (Ly*y/pi)*sin(2*pi*y/Ly)
65        fd = Ly*Ly/(2*pi*pi)*cos(2*pi*y/Ly)
66        return .5*u0*( fb*(y-y0-Ly/(2*pi)*sin(2*pi*y/Ly)) + .5*beta0*(fc-fd-(Ly*Ly/3.)- Ly*Ly/(2*pi*pi)) )
67
68    def Phi_xyeta(y,eta): return T0*g/lap*(1-eta**(Rd*lap/g)) + Phi_xy(y)*log(eta)*exp(-((log(eta)/b)**2))
69    def ulon(x,y,eta):
70        u = -u0*(sin(pi*y/Ly)**2)*log(eta)*(eta**(-log(eta)/(b*b))) 
71#        u = u + up*exp(-(((x-xc)**2+(y-yc)**2)/lp**2))
72        return  u
73
74    def tmean(eta) : return T0*eta**(Rd*lap/g)
75    def T(y,eta) : return tmean(eta)+(Phi_xy(y)/Rd)*(((2/(b*b))*(log(eta))**2)-1)*exp(-((0.5*log(eta))**2)) 
76    def p(eta): return p0*eta     # eta  = p/p_s
77
78    def eta(alpha) : return (1.-(lap*ztop*alpha/T0))**(g/(Rd*lap)) # roughly equispaced levels
79    def coriolis(x,y): return f0+beta0*y # here y is 0 at domain center
80
81    filename = 'cart_%03d_%03d.nc'%(nx,ny)
82    logging.info('Reading Cartesian mesh ...')
83    meshfile = meshes.DYNAMICO_Format(filename)
84    pmesh = meshes.Unstructured_PMesh(comm,meshfile)
85    pmesh.partition_curvilinear(args.mpi_ni,args.mpi_nj)
86    nqdyn, radius = 1, None
87    mesh  = meshes.Local_Mesh(pmesh, llm, nqdyn, radius, coriolis)
88
89    alpha_k = (np.arange(llm)  +.5)/llm
90    alpha_l = (np.arange(llm+1)+ 0.)/llm
91    x_ik, alpha_ik = np.meshgrid(mesh.lon_i,       alpha_k, indexing='ij')
92    y_ik, alpha_ik = np.meshgrid(mesh.lat_i+y0, alpha_k, indexing='ij')
93    x_il, alpha_il = np.meshgrid(mesh.lon_i,       alpha_l, indexing='ij')
94    y_il, alpha_il = np.meshgrid(mesh.lat_i+y0, alpha_l, indexing='ij')
95    x_ek, alpha_ek = np.meshgrid(mesh.lon_e,       alpha_k, indexing='ij')
96    y_ek, alpha_ek = np.meshgrid(mesh.lat_e+y0, alpha_k, indexing='ij')
97
98    print('ztop(ptop) according to Eq. 7:', T0/lap*(1.-(ptop/p0)**(Rd*lap/g))) 
99    print(np.shape(alpha_k),np.shape(alpha_l))
100    thermo = dyn.Ideal_perfect(Cpd, Rd, p0, T0)
101
102    eta_il = eta(alpha_il)
103    eta_ik = eta(alpha_ik)
104    eta_ek = eta(alpha_ek)
105    print('min max eta_il', np.min(eta_il),np.max(eta_il))
106
107    Phi_il = Phi_xyeta(y_il, eta_il)
108    Phi_ik = Phi_xyeta(y_ik, eta_ik)
109    p_ik, p_il = p(eta_ik), p(eta_il)
110    T_ik = T(y_ik, eta_ik)  #ik full level(40), il(41)
111
112    gas = thermo.set_pT(p_ik,T_ik)
113    mass_ik = mesh.field_mass()
114    for l in range(llm): 
115        mass_ik[:,l]=(Phi_il[:,l+1]-Phi_il[:,l])/(g*gas.v[:,l])
116#        mass_ik[:,l]=(p_il[:,l]-p_il[:,l+1])/g
117    Sik, Wil  = gas.s*mass_ik, mesh.field_w()
118   
119    u_ek = mesh.ucov3D(ulon(x_ek, y_ek, eta_ek), 0.*eta_ek)
120
121    print('ztop (m) = ', Phi_il[0,-1]/g, ztop)
122    ptop = p(eta(1.))
123    print( 'ptop (Pa) = ', gas.p[0,-1], ptop)
124
125    params=dyn.Struct()
126    params.ptop=ptop
127    params.dx=dx
128    params.dx_g0=dx/g
129    params.g = g
130
131    # define parameters for lower BC
132    pbot = p(eta_il[:,0])
133    print( 'min p, T :', pbot.min(), tmean(pbot/p0))
134    gas_bot = thermo.set_pT(pbot, tmean(pbot/p0))
135    params.pbot = gas_bot.p
136    params.rho_bot = 1e6/gas_bot.v
137   
138    return thermo, mesh, params, prec.asnum([mass_ik,Sik,u_ek,Phi_il,Wil]), gas
139
140def diagnose(Phi,S,m,W):
141    s=S/m
142    for l in range(llm):
143        v[:,l]=(Phi[:,l+1]-Phi[:,l])/(g*m[:,l])
144        w[:,l]=.5*params.g*(W[:,l+1]+W[:,l])/m[:,l]
145        z[:,l]=.5*(Phi[:,l+1]+Phi[:,l])/params.g
146    gas = thermo.set_vs(v,s)
147    return gas, w, z
148
149def grad(mesh, s_ik):
150    llm=s_ik.shape[1]
151    edge_num, left, right = mesh.edge_num, mesh.left, mesh.right
152    grad_ek = prec.zeros((edge_num, llm))
153    for e in range(edge_num):
154        left_e = left[e]-1 # Fortran => Python indexing
155        right_e = right[e]-1 # Fortran => Python indexing
156        grad_ek[e,:] = s_ik[right_e,:]-s_ik[left_e,:]
157    return grad_ek
158
159def div(mesh, u_ek):
160    llm=u_ek.shape[1]
161    primal_num, primal_deg, primal_edge = mesh.primal_num, mesh.primal_deg, mesh.primal_edge
162    primal_ne, le_de, Ai = mesh.primal_ne, mesh.le_de, mesh.Ai
163    div_ik = prec.zeros((primal_num, llm))
164    for i in range(primal_num):
165        div_i = 0.
166        for iedge in range(primal_deg[i]):
167            e = primal_edge[i,iedge]-1 # Fortran => Python indexing
168            div_i = div_i + u_ek[e,:]*primal_ne[i,iedge]*le_de[e]
169        div_ik[i,:] = div_i / Ai[i]
170    return div_ik
171
172def curl(mesh, u_ek):
173    llm=u_ek.shape[1]
174    dual_num, dual_deg, dual_edge, dual_ne, Av = mesh.dual_num, mesh.dual_deg, mesh.dual_edge, mesh.dual_ne, mesh.Av
175    curl_vk = prec.zeros((dual_num, llm))
176    for v in range(dual_num):
177        curl_v = 0.
178        for iedge in range(dual_deg[v]):
179            e = dual_edge[v,iedge]-1 # Fortran => Python indexing
180            curl_v = curl_v + u_ek[e,:]*dual_ne[v,iedge]
181        curl_vk[v,:] = curl_v / Av[v]
182    return curl_vk
183
184class Davies:
185    def __init__(self,N1,N2,x_i,y_i,x_e,y_e):
186        self.N1, self.N2 = N1, N2
187        self.beta_i = self.mask(x_i,y_i)
188        self.beta_e = self.mask(x_e,y_e)
189        self.x_e,self.y_e = x_e,y_e
190    def mask0(self,c,c0,delta): # 1D building block for Davies relaxation
191        N1, N2 = self.N1, self.N2
192        m = np.zeros(c.size)
193        c1,c2 = c0-N1*delta, c0-(N1+N2)*delta
194        for i in range(c.size):
195            ci=c[i]
196            m[i] = (1.+np.cos((ci-c2)*np.pi/(N2*delta)))/2.0
197            if ci < c2 : m[i]=1.
198            if ci > c1 : m[i]=0.
199        return m
200    def relax(self, llm, step, flow):
201        beta_i, beta_e = self.beta_i, self.beta_e
202        m,S,u,Phi,W=flow
203        for l in range(llm):
204            step.mass[:,l]        = beta_i*step.mass[:,l]        + (1.-beta_i)*m[:,l]
205            step.theta_rhodz[:,l] = beta_i*step.theta_rhodz[:,l] + (1.-beta_i)*S[:,l]
206            step.u[:,l]           = beta_e*step.u[:,l]           + (1.-beta_e)*u[:,l]
207        for l in range(llm+1):
208            step.geopot[:,l]      = beta_i*step.geopot[:,l]      + (1.-beta_i)*Phi[:,l]
209            step.W[:,l]           = beta_i*step.W[:,l]           + (1.-beta_i)*W[:,l]
210
211class myDavies(Davies):
212    def mask(self,x,y):
213        logging.debug('davies dy = %f' % dy)
214        return self.mask0(y,.5*Ly,dy)*self.mask0(-y,.5*Ly,dy)
215
216
217       
218with xios.Client() as client: # setup XIOS which creates the DYNAMICO communicator
219    comm = client.comm
220    mpi_rank, mpi_size = comm.Get_rank(), comm.Get_size()
221
222    #-------------Logging verbosity configuration---------------------------
223    myloglevel = args.log
224    myloglevel = getattr(logging, myloglevel.upper())
225    logging.basicConfig(filename='out.log',filemode='w',level=myloglevel, 
226        format='%(processName)s:%(name)s:%(filename)s:%(module)s:%(funcName)s:%(lineno)d:%(levelname)s:%(message)s' )
227
228    logging.debug('%d/%d starting'%(mpi_rank,mpi_size))
229
230    g, Lx, Ly = 9.81, 4e7, 6e6
231    nx, ny, llm = args.nx, args.ny, args.llm
232    dx,dy = Lx/nx,Ly/ny
233
234    unst.setvar('g',g)
235
236    thermo, mesh, params, flow0, gas0 = baroclinic_3D(Lx,nx,Ly,ny,llm)
237
238    mass_bl,mass_dak,mass_dbk = meshes.compute_hybrid_coefs(flow0[0])
239    print( 'Type of mass_bl, mass_dak, mass_dbk : ', [x.dtype for x in mass_bl, mass_dak, mass_dbk])
240    unst.ker.dynamico_init_hybrid(mass_bl,mass_dak,mass_dbk)
241
242    T  = args.T
243    dt = args.dt
244    dz = flow0[3].max()/(params.g*llm)
245    nt = int(math.ceil(T/dt))
246    dt = T/nt
247    logging.info( 'Time step : %d x %g = %g s' % (nt,dt,nt*dt))
248   
249    dt=0. # FIXME
250
251    caldyn_thermo, caldyn_eta = unst.thermo_entropy, unst.eta_mass
252
253    if False: # time stepping in Python
254        caldyn = unst.Caldyn_NH(caldyn_thermo,caldyn_eta, mesh,thermo,params,params.g)
255        scheme = time_step.ARK2(caldyn.bwd_fast_slow, dt)
256        def next_flow(m,S,u,Phi,W):
257            return scheme.advance((m,S,u,Phi,W),nt)
258
259    else: # time stepping in Fortran
260        scheme = time_step.ARK2(None, dt)
261        if args.hydrostatic:
262            caldyn_step = unst.caldyn_step_HPE(mesh,scheme,1, caldyn_thermo,caldyn_eta,thermo,params,params.g)
263        else:
264            caldyn_step = unst.caldyn_step_NH(mesh,scheme,1, caldyn_thermo,caldyn_eta,thermo,params,params.g)
265        davies = myDavies(args.Davies_N1, args.Davies_N2, mesh.lon_i, mesh.lat_i, mesh.lon_e,mesh.lat_e)
266        print('mask min/max :', davies.beta_i.min(), davies.beta_i.max() )
267        logging.debug('mask min/max :')
268        logging.debug(davies.beta_i.min())
269        logging.debug(davies.beta_i.max())
270        def next_flow(m,S,u,Phi,W):
271            # junk,fast,slow = caldyn.bwd_fast_slow(flow, 0.)
272            caldyn_step.mass[:,:], caldyn_step.theta_rhodz[:,:], caldyn_step.u[:,:] = m,S,u
273            caldyn_step.geopot[:,:], caldyn_step.W[:,:] = Phi,W
274            kappa = dx**4/43200.
275            for i in range(nt):
276                caldyn_step.next()
277                davies.relax(llm, caldyn_step, flow0)
278                m,S = caldyn_step.mass, caldyn_step.theta_rhodz
279                s = S/m
280                laps = mesh.field_mass()
281                bilaps = mesh.field_mass()
282                unst.ker.dynamico_scalar_laplacian(s,laps)
283                unst.ker.dynamico_scalar_laplacian(laps,bilaps)
284#                grads = grad(mesh,s)
285#                laps = div(mesh,grads)
286#                gradlaps = grad(mesh,laps) # bilaplacian
287#                bilaps = div(mesh,gradlaps)               
288                caldyn_step.theta_rhodz[:] = S - dt*kappa*bilaps*m # Euler step
289            return (caldyn_step.mass.copy(), caldyn_step.theta_rhodz.copy(), caldyn_step.u.copy(),
290                    caldyn_step.geopot.copy(), caldyn_step.W.copy())
291
292    m,S,u,Phi,W = flow0
293    if caldyn_thermo == unst.thermo_theta:
294        s=S/m
295        theta = thermo.T0*np.exp(s/thermo.Cpd)
296        S=m*theta
297        title_format = 'Potential temperature at t=%g s (K)'
298    else:
299        title_format = 'Specific entropy at t=%g s (J/K/kg)'
300
301    w=mesh.field_mass()
302    z=mesh.field_mass()
303
304    #T, nslice, dt = 3600., 1, 3600.
305    Nslice = args.N
306
307    with xios.Context_Curvilinear(mesh,1, 24*3600) as context:
308        # now XIOS knows about the mesh and we can write to disk
309        v = mesh.field_mass() # specific volume (diagnosed)
310       
311        for i in range(Nslice):
312            context.update_calendar(i+1)
313
314            # Diagnose quantities of interest from prognostic variables m,S,u,Phi,W
315            gas, w, z = diagnose(Phi,S,m,W)
316            ps = caldyn_step.ps
317            curl_vk = curl(mesh, u)
318            du_fast, du_slow = caldyn_step.du_fast[0,:,:], caldyn_step.du_slow[0,:,:] 
319            div_fast, div_slow = div(mesh,du_fast), div(mesh,du_slow)
320            # write to disk
321            context.send_field_primal('ps', ps)
322            context.send_field_primal('temp', gas.T)
323            context.send_field_primal('p', gas.p)
324            context.send_field_primal('theta', gas.s)
325            context.send_field_primal('uz', w)
326            context.send_field_primal('z', z)
327            context.send_field_primal('div_fast', div_fast)
328            context.send_field_primal('div_slow', div_slow)
329            print curl_vk.size
330            context.send_field_dual('curl', curl_vk)
331
332            print( 'ptop, model top (m) :', unst.getvar('ptop'), Phi.max()/unst.getvar('g'))
333
334            time1, elapsed1 =time.time(), unst.getvar('elapsed')
335            m,S,u,Phi,W = next_flow(m,S,u,Phi,W)
336            time2, elapsed2 =time.time(), unst.getvar('elapsed')
337            factor = 1000./nt
338            print( 'ms per full time step : ', factor*(time2-time1), factor*(elapsed2-elapsed1))
339            factor = 1e9/(4*nt*nx*ny*llm)
340            print( 'nanosec per gridpoint per full time step : ', factor*(time2-time1), factor*(elapsed2-elapsed1))
341
342logging.info('************DONE************')
Note: See TracBrowser for help on using the repository browser.