2626# Physical constants
2727pref0 = 1e5 | units .Pa # Pa reference pressure
2828rd = 287.04 | units .J / units .kg / units .K # gas constant for dry air. J/kg K.
29- rv = 461.5 | units .J / units .kg / units .K # gas constant for water vapor. J/kg K.
30- cp = 1004. | units .J / units .kg / units .K # specific heat at constant pressure (dry air).
29+ rv = 461.5 | units .J / units .kg / units .K # gas constant for water vapor. J/kg K.
30+ cp = 1004. | units .J / units .kg / units .K # specific heat at constant pressure (dry air).
3131rlv = 2.53e6 | units .J / units .kg # latent heat for vaporisation
3232grav = 9.81 | units .m / units .s ** 2 # gravity acceleration. m/s^2
33- mair = 28.967 | units .g / units .mol # Molar mass of air
33+ mair = 28.967 | units .g / units .mol # molar mass of air
3434
3535
3636# Exner function
@@ -49,15 +49,13 @@ def iexner(p):
4949
5050def initBomex (i , j , itot = 25 , jtot = 25 , dx = 100 | units .m , qt_delta = 0 | units .g / units .kg , dirname = 'dales' , nudge = None ):
5151 workdir = '%s_%d_%d' % (dirname ,j ,i )
52- subprocess .call (["rm" , workdir , "-rf" ]) # remove a previous work-dir if it exists - TODO handle better
52+ subprocess .call (["rm" , workdir , "-rf" ]) # remove a previous work-dir if it exists
5353
54- #inputdir='/home/jansson/code/omuse/src/omuse/community/dales/dales-repo/cases/bomex'
55- inputdir = 'bomexh' # own bomex case with U velocity in positive direction. bomexh has extra vertical levels
54+ inputdir = 'bomexh' # own bomex case with U velocity in positive direction. bomexh has extra vertical levels.
5655 d = Dales (inputdir = inputdir , number_of_workers = 1 , workdir = workdir , channel_type = 'sockets' ,
5756 redirection = 'none' )
5857 #redirect_stdout_file='dales-output-%s'%workdir,
5958 #redirect_stderr_file='dales-error-%s'%workdir)
60- # case='bomex'
6159
6260 d .parameters_DOMAIN .itot = itot # number of grid cells in x
6361 d .parameters_DOMAIN .jtot = jtot # number of grid cells in y
@@ -84,8 +82,8 @@ def initBomex(i, j, itot = 25, jtot = 25, dx=100 | units.m, qt_delta = 0 | units
8482
8583 d .commit_parameters ()
8684 d .commit_grid ()
87-
88- #d.grid[:,:,:].QT -= i * 0.5 | units.g / units.kg
85+
86+ # optionally apply a bias in qt
8987 d .fields [:,:,:].QT += i * qt_delta
9088 return d
9189
@@ -128,8 +126,8 @@ def test_advect():
128126 advect (Q , U , 2500 | units .m , 10 | units .s )
129127 print (Q [0 ,:,2 ])
130128
131- # create a bubble perturbation, given a DALES grid which is used for grid size and coordinates
132- # if gaussian=True, a gaussian perturbation is generated, with standard deviation r, otherwise a
129+ # Create a bubble perturbation, given a DALES grid which is used for grid size and coordinates.
130+ # If gaussian=True, a gaussian perturbation is generated, with standard deviation r, otherwise a
133131# constant perturbation is generated inside a sphere of radius r.
134132#
135133# r, center are quantities, i.e. numbers with units.
@@ -141,7 +139,6 @@ def make_bubble(grid, r, center, gaussian=False):# r, center are quantities, i.e
141139 else :
142140 return numpy .where (rr < r * r , 1 , 0 )
143141
144- # try without units, for speed.
145142def variability_nudge (les , ql_ref , DT , constantT = False ):
146143 # this cannot be used before the LES has been stepped - otherwise qsat and ql are not defined.
147144
@@ -213,18 +210,13 @@ def get_ql_diff_additive(a):
213210 # Nudge towards just below saturation.
214211 i , j = numpy .unravel_index (numpy .argmax (qt [:, :, k ] - qsat [:, :, k ]), qt [:, :, k ].shape )
215212 beta [k ] = (qsat [i , j , k ] - qt_av [k ]) / (qt [i , j , k ] - qt_av [k ])
216- # print (qt[i,j,k].value_in(units.mfu))
217- # print (qsat[i,j,k].value_in(units.mfu))
218- # print(qt_av[k].value_in(units.mfu))
219- # print(ql[k].value_in(units.mfu))
220- # print(les.ql_ref[k].value_in(units.mfu))
221213 #log.info(
222214 # '%d nudging towards non-saturation. Max at (%d,%d). qt:%f, qsat:%f, qt_av[k]:%f, beta:%f, ql_avg:%f, '
223215 # 'ql_ref:%f' % (k, i, j, qt[i, j, k].value_in(units.mfu), qsat[i, j, k].value_in(units.mfu),
224216 # qt_av[k].value_in(units.mfu), beta[k], ql[k].value_in(units.mfu), ql_ref[k].value_in(units.mfu)))
225217 if beta [k ] < 0 :
226218 # this happens when qt_av > qsat
227- # log.info(' beta<0, setting beta=1 ')
219+ # log.info(' beta<0, setting beta=1 ')
228220 beta [k ] = 1
229221 else :
230222 continue # no clouds, no nudge - don't print anything
@@ -233,7 +225,7 @@ def get_ql_diff_additive(a):
233225 log .info (' beta %f too large at %3d' % (beta [k ], k ))
234226
235227 # try additive noise instead
236- # Add the random noise field defined in the beginning, with amplitude a.
228+ # add the random noise field defined in the beginning, with amplitude a.
237229 # solve for the a needed to match ql in this layer
238230 a_min = 0
239231 a_max = 5
@@ -256,15 +248,14 @@ def get_ql_diff_additive(a):
256248 dQT = (beta [k ]- 1 ) * (qt [:,:,k ] - qt_av [k ])
257249 qt [:,:,k ] += dQT
258250 if constantT :
259- #ql_target = numpy.maximum((beta[k] * (qt[:, :, k] - qt_av[k]) + qt_av[k] - qsat[:, :, k]), 0) # wrong!
260- #ql_target = numpy.maximum((qt[:, :, k] + (a * R[:,:]) - qsat[:, :, k]), 0)
261- #ql_target = numpy.maximum((qt[:, :, k] + dQT - qsat[:, :, k]), 0)
251+ # calculate change in theta_l (dTHL) required to keep *temperature*
252+ # constant, when qt changes
262253 ql_target = numpy .maximum ((qt [:, :, k ] - qsat [:, :, k ]), 0 )
263254 dQL = ql_target - ql [:,:,k ]
264255 dTHL = - rlv .number / (cp .number * exner (p [k ])) * dQL
265256 thl [:,:,k ] += dTHL
266- #les.fields[:,:,k].THL += dTHL | units.K
267257
258+ # write qt and thl fields back to the LES
268259 les .fields .QT = qt
269260 if constantT :
270261 les .fields .THL = thl | units .K
@@ -416,54 +407,26 @@ def run_single(steps=60, DT=60 | units.s, n=25, nx=4, ny=1, qt_delta=0|units.g/u
416407 d .stop ()
417408
418409
419- # with different qt at different grid points
420- #run (steps=360, DT=60 | units.s, qt_delta=-1|units.shu, nx=4, ny=1, couple=False, name='dales')
421- #run (steps=360, DT=60 | units.s, qt_delta=-1|units.shu, nx=4, ny=1, couple=True, name='dales-coupled')
422- #run_single(steps=360, DT=60 | units.s, qt_delta=-1|units.shu, nx=4, ny=1, name='dales-single')
423-
424- # add moist bubble at leftmost grid point
425-
410+ # add moist bubble in the leftmost DALES
426411A = 1.5 | units .g / units .kg
427412
428- #run (steps=30, DT=60 | units.s, nx=4, ny=1, couple=False, name='bubble', bubble=True, bubbleA=A)
413+ # uncoupled models
414+ #run (steps=30, DT=60 | units.s, nx=4, ny=1, couple=False, name='bubble', bubble=True, bubbleA=A)
415+
416+ # coupled models - regular SP
429417run (steps = 30 , DT = 60 | units .s , nx = 4 , ny = 1 , couple = True , name = 'bubble-coupled' , bubble = True , bubbleA = A )
418+
419+ # single wide DALES
430420run_single (steps = 30 , DT = 60 | units .s , nx = 4 , ny = 1 , name = 'bubble-single' , bubble = True , bubbleA = A )
431421
432- # with variance nudging
422+ # SP with variance nudging at constant thl
433423# run (steps=30, DT=60 | units.s, nx=4, ny=1, couple=True, name='bubble-coupled-var', bubble=True, bubbleA=A, nudge='variance')
434424# with variance nudging at constant T
435- run (steps = 30 , DT = 60 | units .s , nx = 4 , ny = 1 , couple = True , name = 'bubble-coupled-var-T' , bubble = True , bubbleA = A , nudge = 'variance' , constantT = True )
436-
437- # + spinup - still crashes
438- #run (steps=30, DT=60 | units.s, spinup=3600 | units.s, nx=4, ny=1, couple=True, name='bubble-coupled-var-spinup', bubble=True, bubbleA=A, nudge='variance')
439-
440- # with strong nudging
441- # run (steps=30, DT=60 | units.s, nx=4, ny=1, couple=True, name='bubble-coupled-strong', bubble=True, bubbleA=A, nudge='strong')
442-
443-
444425
426+ # SP with variance nudging at constant thl
427+ run (steps = 30 , DT = 60 | units .s , nx = 4 , ny = 1 , couple = True , name = 'bubble-coupled-var-T' , bubble = True , bubbleA = A , nudge = 'variance' , constantT = True )
445428
446429
447430
448431
449- # wishlist
450- # set up cases with position-dependent perturbation X
451- # - where should this be done? initBomex function, pass it i,j X
452- #
453- # use own input case directory for more options? X
454- # - no subsidence TODO
455- # - output: LWP surface field, 3D fields?
456- # - less statistics, for it is slow X
457- #
458- # get current state and output it. X
459- # - profiles, needed for SP anyway
460-
461- # 4 runs:
462- # one big DALES with gradient or steps in QT X
463- # 4 independent DALES with different QT X
464- # basic SP: advect averages from one LES to the next X
465- # improved SP: advect also variability
466- #
467- # plotting
468- #
469432
0 commit comments