1 PROGRAM create_p3_lookuptable_2
3 !______________________________________________________________________________________
5 ! This program creates the lookup table 'p3_lookupTable_2.dat' for inter-category ice-ice
6 ! interactions for the multi-ice-category configuration of the P3 microphysics scheme.
8 !--------------------------------------------------------------------------------------
10 ! Last modified: 2022 June
11 !______________________________________________________________________________________
13 !______________________________________________________________________________________
15 ! To generate 'p3_lookupTable_2.dat' using this code, do the following steps :
17 ! 1. Break up this code into two parts (-top.f90 and -bottom.90). Search for the string
18 ! 'RUNNING IN PARALLEL MODE' and follow instructions. (In the future, a script
19 ! will be written to automate this.)
21 ! 2. Copy the 3 pieces of text below to create indivudual executable shell scripts.
23 ! 3. Run script 1 (./go_1-compile.ksh). This script will recreate several
24 ! versions of the full code, concatenating the -top.f90 and the -bottom.90 with
25 ! the following code (e.g.) in between (looping through values of i1:
29 ! Each version of full_code.f90 is then compiled, with a unique executable name.
30 ! Note, this is done is place the outer i1 loop in order to parallelized .
32 ! 4. Run script 2 (./go_2-submit.csh) This create temporary work directories,
33 ! moves each executable to each work directory, and runs the executables
34 ! simultaneously. (Note, it is assumed that the machine on which this is done
35 ! has multiple processors, though it is not necessary.)
37 ! 5. Run script 3 (./go_3-concatenate.ksh). This concatenates the individual
38 ! partial tables (the output in each working directory) into a single, final
39 ! file 'p3_lookupTable_2.dat' Once it is confirmed that the table is correct,
40 ! the work directories and their contents can be removed.
42 ! Note: For testing or to run in serial, compile with double-precision
43 ! e.g. ifort -r8 create_p3_lookupTable_2.f90
44 ! gfortran -fdefault-real-8 create_p3_lookupTable_2.f90
45 !______________________________________________________________________________________
47 !--------------------------------------------------------------------------------------------
48 !# Parallel script 1 (of 3): [copy text below (uncommented) to file 'go_1-compile.ksh']
49 !# - creates individual parallel codes, compiles each
53 ! for i1 in 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
56 ! rm cfg_input full_code.f90
58 ! cat > cfg_input << EOF
62 ! cat create_p3_lookupTable_2-top.f90 cfg_input create_p3_lookupTable_2-bottom.f90 > full_code.f90
64 ! echo 'Compiling 'exec_${i1}
65 ! #pgf90 -r8 full_code.f90
66 ! #gfortran -fdefault-real-8 full_code.f90
67 ! ifort -r8 full_code.f90
72 ! rm cfg_input full_code.f90
74 !--------------------------------------------------------------------------------------------
75 !# Parallel script 2 (of 3): [copy text below (uncommented) to file 'go_2-submit.ksh']
76 !# - creates individual work directories, launches each executable
80 !for exec in `ls exec_*`
82 ! echo Submitting: ${exec}
83 ! mkdir ${exec}-workdir
84 ! mv ${exec} ${exec}-workdir
90 !--------------------------------------------------------------------------------------------
91 !# Parallel script 3 (of 3): [copy text below (uncommented) to file 'go_3-concatenate.ksh]
92 !# - concatenates the output of each parallel job into a single output file.
98 ! for i in `ls exec*/*dat`
101 ! cat lt_total $i > lt_total_tmp
102 ! mv lt_total_tmp lt_total
105 ! mv lt_total p3_lookupTable_2.dat
107 ! echo 'Done. Work directories and contents can now be removed.'
108 ! echo 'Be sure to re-name the file with the appropriate extension, with the version number
109 ! echo 'corresponding to that in the header. (e.g. 'p3_lookupTable_2.dat-v5.0')'
111 !--------------------------------------------------------------------------------------------
115 character(len=16), parameter :: version = '5.3'
117 real :: pi,g,p,t,rho,mu,pgam,ds,cs,bas,aas,dcrit,eii
118 integer :: k,ii,jj,kk,dumii,j2
120 integer, parameter :: n_rhor = 5
121 integer, parameter :: n_Fr = 4
122 integer, parameter :: n_Qnorm = 25
124 integer, parameter :: num_bins1 = 1000 ! number of bins for numerical integration of fall speeds and total N
125 !integer, parameter :: num_bins2 = 9000 ! number of bins for solving PSD parameters [based on Dm_max = 2000.]
126 integer, parameter :: num_bins2 = 11000 ! number of bins for solving PSD parameters [based on Dm_max = 400000.]
127 real, parameter :: dd = 20.e-6 ! width of size bin (m)
129 integer :: i_rhor,i_rhor1,i_rhor2 ! indices for rho_rime [1 .. n_rhor]
130 integer :: i_Fr,i_Fr1,i_Fr2 ! indices for rime-mass-fraction loop [1 .. n_Fr]
131 integer :: i,i1,i2 ! indices for normalized (by N) Q loop [1 .. n_Qnorm] (i is i_Qnorm in LT1)
133 real :: N,q,qdum,dum1,dum2,cs1,ds1,lam,n0,lamf,qerror,del0,c0,c1,c2,sum1,sum2, &
134 sum3,sum4,xx,a0,b0,a1,b1,dum,bas1,aas1,aas2,bas2,gammq,d1,d2,delu,lamold, &
135 cap,lamr,dia,amg,dv,n0dum,sum5,sum6,sum7,sum8,dg,cg,bag,aag,dcritg,dcrits, &
136 dcritr,csr,dsr,duml,dum3,rhodep,cgpold,m1,m2,m3,dt,mur,initlamr,lamv,Fr, &
137 rdumii,lammin,lammax,cs2,ds2,intgrR1,intgrR2,intgrR3,intgrR4,q_agg,n_agg
139 real :: diagnostic_mui ! function to return diagnostic value of shape paramter, mu_i
140 real :: Q_normalized ! function
141 real :: lambdai ! function
143 real, parameter :: Dm_max = 40000.e-6 ! max. mean ice [m] size for lambda limiter
144 !real, parameter :: Dm_max = 2000.e-6 ! max. mean ice [m] size for lambda limiter
145 real, parameter :: Dm_min = 2.e-6 ! min. mean ice [m] size for lambda limiter
147 real, parameter :: thrd = 1./3.
148 real, parameter :: sxth = 1./6.
150 !real, dimension(n_Qnorm,n_Fr,n_rhor) :: qsave,nsave,qon1
151 real, dimension(n_Qnorm,n_Fr,n_rhor) :: qsave
152 real, dimension(n_rhor,n_Fr) :: dcrits1,dcritr1,csr1,dsr1,dcrits2,dcritr2,csr2,dsr2
153 real, dimension(n_rhor,n_Fr,n_Qnorm) :: n01,mu_i1,lam1,n02,mu_i2,lam2
154 real, dimension(n_rhor) :: cgp,crp
155 real, dimension(n_rhor,n_Fr) :: cgp1,cgp2
156 real, dimension(n_Fr) :: Fr_arr
157 real, dimension(n_rhor,n_Fr,num_bins1) :: fall1,fall2
158 real, dimension(num_bins1) :: num1,num2
160 logical, dimension(n_rhor,n_Fr,n_Qnorm) :: log_lamIsMax
162 character(len=2014) :: filename
164 !-------------------------------------------------------------------------------
166 ! set constants and parameters
168 ! assume 600 hPa, 253 K for p and T for fallspeed calcs (for reference air density)
171 p = 60000. ! air pressure (pa)
172 t = 253.15 ! temp (K)
173 rho = p/(287.15*t) ! air density (kg m-3)
174 mu = 1.496E-6*t**1.5/(t+120.)/rho ! viscosity of air
175 dv = 8.794E-5*t**1.81/p ! diffusivity of water vapor in air
178 ! parameters for surface roughness of ice particle
179 ! see mitchell and heymsfield 2005
182 c1 = 4./(del0**2*c0**0.5)
185 !--- specified mass-dimension relationship (cgs units) for unrimed crystals:
189 ! mg = cg*D^dg no longer used, since bulk volume is predicted
191 ! Heymsfield et al. 2006
193 ! cs=0.0040157+6.06e-5*(-20.)
194 ! sector-like branches (P1b)
203 ! radiating assemblages of plates (mitchell et al. 1990)
206 ! aggreagtes of side planes, bullets, etc. (Mitchell 1996)
209 ! Brown and Francis (1995)
211 ! cs = 0.01855 ! original, based on assumption of Dmax
212 cs = 0.0121 ! scaled value based on assumtion of Dmean from Hogan et al. 2012, JAMC
214 ! note: if using brown and francis, already in mks units!!!!!
215 ! uncomment line below if using other snow m-D relationships
216 ! cs=cs*100.**ds/1000. ! convert from cgs units to mks
219 ! applicable for prognostic graupel density
220 ! note: cg is not constant, due to variable density
224 !--- projected area-diam relationship (mks units) for unrimed crystals:
225 ! note: projected area = aas*D^bas
226 ! sector-like branches (P1b)
228 ! aas = 0.55*100.**bas/(100.**2)
231 ! aas = 0.0869*100.**bas/(100.**2)
232 ! graupel (values for hail)
234 ! aag=0.625*100.**bag/(100.**2)
235 ! aggreagtes of side planes, bullets, etc.
237 aas = 0.2285*100.**bas/(100.**2)
240 !--- projected area-diam relationship (mks units) for graupel:
242 ! note: projected area = aag*D^bag
247 ! calculate critical diameter separating small spherical ice from crystalline ice
248 ! "Dth" in Morrison and Grabowski 2008
250 ! ! !open file to write to look-up table (which gets used by P3 scheme)
251 ! ! open(unit=1,file='./p3_lookup_table_2.dat-v4',status='unknown')
253 !.........................................................
255 !dcrit = (pi/(6.*cs)*0.9)**(1./(ds-3.))
256 dcrit = (pi/(6.*cs)*900.)**(1./(ds-3.))
257 !dcrit=dcrit/100. ! convert from cm to m
259 !.........................................................
260 ! main loop over graupel density
262 ! 1D array for RIME density (not ice/graupel density)
264 crp(2) = 250.*pi*sxth
265 crp(3) = 450.*pi*sxth
266 crp(4) = 650.*pi*sxth
267 crp(5) = 900.*pi*sxth
269 ! array for rime fraction, Fr
275 !...........................................................................................
277 ! parameters for category 1
279 ! main loop over graupel density
281 i_rhor_loop_1: do i_rhor = 1,n_rhor
283 !------------------------------------------------------------------------
284 ! main loops around N, q, Fr for lookup tables
286 ! find threshold with rimed mass added
288 ! loop over rimed mass fraction (4 points)
290 i_Fr_loop_1: do i_Fr = 1,n_Fr ! loop for rime mass fraction, Fr
292 ! Rime mass fraction for the lookup table (specific values in model are interpolated between points)
295 ! ! ! calculate critical dimension separate graupel and nonspherical ice
296 ! ! ! "Dgr" in morrison and grabowski (2008)
298 ! ! ! dcrits = (cs/crp(i_rhor))**(1./(dg-ds)) ! calculated below for variable graupel density
299 ! ! ! dcrits = dcrits/100. ! convert from cm to m
301 ! ! ! check to make sure projected area at dcrit not greater than than of solid sphere
302 ! ! ! stop and give warning message if that is the case
304 ! ! ! if (pi/4.*dcrit**2.lt.aas*dcrit**bas) then
305 ! ! ! print*,'STOP, area > area of solid ice sphere, unrimed'
308 ! ! ! if (pi/4.*dcrits1(i_rhor,i_Fr)**2.lt.aag*dcrits1(i_rhor,i_Fr)**bag) then
309 ! ! ! print*,'STOP, area > area of solid ice sphere, graupel'
313 ! ! ! cg=cg*100.**dg/1000. ! convert from cgs units to mks
318 ! ! ! dd=real(jj)*30.e-6
319 ! ! ! write(6,'5e15.5')dd,aas*dd**bas,pi/4.*dd**2,
320 ! ! ! 1 cs*dd**ds,pi*sxth*917.*dd**3
323 ! calculate mass-dimension relationship for partially-rimed crystals
325 ! formula from morrison grabowski 2008
327 ! dcritr is critical size separating graupel from partially-rime crystal
328 ! same as "Dcr" in morrison and grabowski 2008
330 ! first guess, set cgp=crp
331 cgp1(i_rhor,i_Fr) = crp(i_rhor)
333 ! case of no riming (Fr = 0%), then we need to set dcrits and dcritr to arbitrary large values
337 dcrits1(i_rhor,i_Fr) = 1.e+6
338 dcritr1(i_rhor,i_Fr) = dcrits1(i_rhor,i_Fr)
339 csr1(i_rhor,i_Fr) = cs
340 dsr1(i_rhor,i_Fr) = ds
341 ! case of partial riming (Fr between 0 and 100%)
343 elseif (i_Fr.eq.2.or.i_Fr.eq.3) then
346 dcrits1(i_rhor,i_Fr) = (cs/cgp1(i_rhor,i_Fr))**(1./(dg-ds))
347 dcritr1(i_rhor,i_Fr) = ((1.+Fr/(1.-Fr))*cs/cgp1(i_rhor,i_Fr))**(1./(dg-ds))
348 csr1(i_rhor,i_Fr) = cs*(1.+Fr/(1.-Fr))
349 dsr1(i_rhor,i_Fr) = ds
350 ! get mean density of vapor deposition/aggregation grown ice
351 rhodep = 1./(dcritr1(i_rhor,i_Fr)-dcrits1(i_rhor,i_Fr))*6.*cs/(pi*(ds-2.))* &
352 (dcritr1(i_rhor,i_Fr)**(ds-2.)-dcrits1(i_rhor,i_Fr)**(ds-2.))
353 ! get graupel density as rime mass fraction weighted rime density plus
354 ! density of vapor deposition/aggregation grown ice
355 cgpold = cgp1(i_rhor,i_Fr)
356 cgp1(i_rhor,i_Fr) = crp(i_rhor)*Fr+rhodep*(1.-Fr)*pi*sxth
357 if (abs((cgp1(i_rhor,i_Fr)-cgpold)/cgp1(i_rhor,i_Fr)).lt.0.01) goto 115
362 ! case of complete riming (Fr=100%)
365 ! set threshold size for pure graupel arbitrary large
366 dcrits1(i_rhor,i_Fr) = (cs/cgp1(i_rhor,i_Fr))**(1./(dg-ds))
367 dcritr1(i_rhor,i_Fr) = 1.e6
368 csr1(i_rhor,i_Fr) = cgp1(i_rhor,i_Fr)
369 dsr1(i_rhor,i_Fr) = dg
373 !---------------------------------------------------------------------------------------
374 ! set up particle fallspeed arrays
375 ! fallspeed is a function of mass dimension and projected area dimension relationships
376 ! following mitchell and heymsfield (2005), jas
378 ! set up array of particle fallspeed to make computationally efficient
379 !.........................................................
381 ! note: this part could be incorporated into the longer (every 2 micron) loop
383 jj_loop_1: do jj = 1,num_bins1
385 d1 = real(jj)*dd - 0.5*dd !particle size [m]
387 if (d1.le.dcrit) then
392 else if (d1.gt.dcrit.and.d1.le.dcrits1(i_rhor,i_Fr)) then
397 else if (d1.gt.dcrits1(i_rhor,i_Fr).and.d1.le.dcritr1(i_rhor,i_Fr)) then
398 cs1 = cgp1(i_rhor,i_Fr)
402 else if (d1.gt.dcritr1(i_rhor,i_Fr)) then
403 cs1 = csr1(i_rhor,i_Fr)
404 ds1 = dsr1(i_rhor,i_Fr)
409 ! for area, keep bas1 constant, but modify aas1 according to rimed fraction
413 ! dum3 = (1.-Fr)*dum1+Fr*dum2
416 m3 = cgp1(i_rhor,i_Fr)*d1**dg
417 dum3 = dum1+(m1-m2)*(dum2-dum1)/(m3-m2) !linearly interpolate based on particle mass
418 aas1 = dum3/(d1**bas)
422 ! correction for turbulence
423 ! if (d1.lt.500.e-6) then
433 xx = 2.*cs1*g*rho*d1**(ds1+2.-bas1)/(aas1*(mu*rho)**2)
435 b1 = c1*xx**0.5/(2.*((1.+c1*xx**0.5)**0.5-1.)*(1.+c1*xx**0.5)**0.5)-a0*b0*xx** &
436 b0/(c2*((1.+c1*xx**0.5)**0.5-1.)**2)
437 a1 = (c2*((1.+c1*xx**0.5)**0.5-1.)**2-a0*xx**b0)/xx**b1
438 ! velocity in terms of drag terms
439 fall1(i_rhor,i_Fr,jj) = a1*mu**(1.-2.*b1)*(2.*cs1*g/(rho*aas1))**b1*d1**(b1*(ds1-bas1+2.)-1.)
443 !---------------------------------------------------------------------------------
444 ! main loops around normalized q for lookup table
446 ! q = normalized ice mass mixing ratio = q/N, units are kg^-1
448 i_Qnorm_loop_1: do i = 1,n_Qnorm
452 print*,'i,Fr,i_rhor ',i,i_Fr,i_rhor
455 qerror = 1.e+20 !initialize qerror to arbitrarily large value
457 !.....................................................................................
458 ! find parameters for gamma distribution
460 ! size distribution for ice is assumed to be
461 ! N(D) = n0 * D^pgam * exp(-lam*D)
463 ! for the given q and N, we need to find n0, pgam, and lam
465 ! approach for finding lambda:
466 ! cycle through a range of lambda, find closest lambda to produce correct q
468 ! start with lam, range of lam from 100 to 5 x 10^6 is large enough to
469 ! cover full range over mean size from 2 to 5000 micron
471 ii_loop: do ii = 1,num_bins2
473 lam1(i_rhor,i_Fr,i) = lambdai(ii)
474 mu_i1(i_rhor,i_Fr,i) = diagnostic_mui(lam1(i_rhor,i_Fr,i),q,cgp1(i_rhor,i_Fr),Fr,pi)
476 ! set min,max lam corresponding to Dm_max,Dm_min:
477 lam1(i_rhor,i_Fr,i) = max(lam1(i_rhor,i_Fr,i),(mu_i1(i_rhor,i_Fr,i)+1.)/Dm_max)
478 lam1(i_rhor,i_Fr,i) = min(lam1(i_rhor,i_Fr,i),(mu_i1(i_rhor,i_Fr,i)+1.)/Dm_min)
479 ! get normalized n0 = n0/N
480 n01(i_rhor,i_Fr,i) = lam1(i_rhor,i_Fr,i)**(mu_i1(i_rhor,i_Fr,i)+1.)/(gamma(mu_i1(i_rhor,i_Fr,i)+1.))
482 ! calculate integral for each of the 4 parts of the size distribution
483 ! check difference with respect to q
485 ! set up m-D relationship for solid ice with D < Dcrit
489 call intgrl_section(lam1(i_rhor,i_Fr,i),mu_i1(i_rhor,i_Fr,i), ds1,ds,dg, &
490 dsr1(i_rhor,i_Fr),dcrit,dcrits1(i_rhor,i_Fr), &
491 dcritr1(i_rhor,i_Fr),intgrR1,intgrR2,intgrR3,intgrR4)
493 ! intgrR1 is integral from 0 to dcrit (solid ice)
494 ! intgrR2 is integral from dcrit to dcrits (snow)
495 ! intgrR3 is integral from dcrits to dcritr (graupel)
496 ! intgrR4 is integral from dcritr to inf (rimed snow)
498 ! sum of the integrals from the 4 regions of the size distribution
499 qdum = n01(i_rhor,i_Fr,i)*(cs1*intgrR1 + cs*intgrR2 + cgp1(i_rhor,i_Fr)*intgrR3 + &
500 csr1(i_rhor,i_Fr)*intgrR4)
504 lamf = lam1(i_rhor,i_Fr,i)
507 ! find lam with smallest difference between q and estimate of q, assign to lamf
508 if (abs(q-qdum).lt.qerror) then
509 lamf = lam1(i_rhor,i_Fr,i)
515 ! check and print relative error in q to make sure it is not too large
516 ! note: large error is possible if size bounds are exceeded!!!!!!!!!!
518 print*,'qerror (%)',qerror/q*100.
520 ! find n0 based on final lam value
521 ! set final lamf to 'lam' variable
522 ! this is the value of lam with the smallest qerror
523 lam1(i_rhor,i_Fr,i) = lamf
524 ! recalculate mu_i based on final lam
525 mu_i1(i_rhor,i_Fr,i) = diagnostic_mui(lam1(i_rhor,i_Fr,i),q,cgp1(i_rhor,i_Fr),Fr,pi)
527 ! n0 = N*lam**(pgam+1.)/(gamma(pgam+1.))
529 ! find n0 from lam and q
530 ! this is done instead of finding n0 from lam and N, since N
531 ! may need to be adjusted to constrain mean size within reasonable bounds
533 call intgrl_section(lam1(i_rhor,i_Fr,i),mu_i1(i_rhor,i_Fr,i), ds1,ds,dg, &
534 dsr1(i_rhor,i_Fr),dcrit,dcrits1(i_rhor,i_Fr), &
535 dcritr1(i_rhor,i_Fr),intgrR1,intgrR2,intgrR3,intgrR4)
538 n01(i_rhor,i_Fr,i) = q/(cs1*intgrR1 + cs*intgrR2 + cgp1(i_rhor,i_Fr)*intgrR3 + &
539 csr1(i_rhor,i_Fr)*intgrR4)
540 print*,'lam,N0:',lam1(i_rhor,i_Fr,i),n01(i_rhor,i_Fr,i)
541 print*,'mu_i:',mu_i1(i_rhor,i_Fr,i)
542 print*,'mean size:',(mu_i1(i_rhor,i_Fr,i)+1.)/lam1(i_rhor,i_Fr,i)
544 ! At this point, we have solve for all of the size distribution parameters
546 ! NOTE: In the code it is assumed that mean size and number have already been
547 ! adjusted, so that mean size will fall within allowed bounds. Thus, we do
548 ! not apply a lambda limiter here.
555 !--------------------------------------------------------------------
556 !.....................................................................................
557 ! now calculate parameters for category 2
559 i_rhor_loop_2: do i_rhor = 1,n_rhor
561 i_Fr_loop_2: do i_Fr = 1,n_Fr
565 ! calculate mass-dimension relationship for partially-rimed crystals
567 ! formula from morrison grabowski 2008
569 ! dcritr is critical size separating graupel from partially-rime crystal
570 ! same as "Dcr" in morrison and grabowski 2008
572 ! first guess, set cgp=crp
573 cgp2(i_rhor,i_Fr) = crp(i_rhor)
575 ! case of no riming (Fr = 0%), then we need to set dcrits and dcritr to arbitrary large values
579 dcrits2(i_rhor,i_Fr) = 1.e+6
580 dcritr2(i_rhor,i_Fr) = dcrits2(i_rhor,i_Fr)
581 csr2(i_rhor,i_Fr) = cs
582 dsr2(i_rhor,i_Fr) = ds
584 ! case of partial riming (Fr between 0 and 100%)
585 elseif (i_Fr.eq.2.or.i_Fr.eq.3) then
588 dcrits2(i_rhor,i_Fr) = (cs/cgp2(i_rhor,i_Fr))**(1./(dg-ds))
589 dcritr2(i_rhor,i_Fr) = ((1.+Fr/(1.-Fr))*cs/cgp2(i_rhor,i_Fr))**(1./(dg-ds))
590 csr2(i_rhor,i_Fr) = cs*(1.+Fr/(1.-Fr))
591 dsr2(i_rhor,i_Fr) = ds
592 ! get mean density of vapor deposition/aggregation grown ice
593 rhodep = 1./(dcritr2(i_rhor,i_Fr)-dcrits2(i_rhor,i_Fr))*6.*cs/(pi*(ds-2.))* &
594 (dcritr2(i_rhor,i_Fr)**(ds-2.)-dcrits2(i_rhor,i_Fr)**(ds-2.))
595 ! get graupel density as rime mass fraction weighted rime density plus
596 ! density of vapor deposition/aggregation grown ice
597 cgpold = cgp2(i_rhor,i_Fr)
598 cgp2(i_rhor,i_Fr) = crp(i_rhor)*Fr+rhodep*(1.-Fr)*pi*sxth
599 if (abs((cgp2(i_rhor,i_Fr)-cgpold)/cgp2(i_rhor,i_Fr)).lt.0.01) goto 116
604 ! case of complete riming (Fr=100%)
607 ! set threshold size for pure graupel arbitrary large
608 dcrits2(i_rhor,i_Fr) = (cs/cgp2(i_rhor,i_Fr))**(1./(dg-ds))
609 dcritr2(i_rhor,i_Fr) = 1.e+6
610 csr2(i_rhor,i_Fr) = cgp2(i_rhor,i_Fr)
611 dsr2(i_rhor,i_Fr) = dg
615 !---------------------------------------------------------------------------------------
616 ! set up particle fallspeed arrays
617 ! fallspeed is a function of mass dimension and projected area dimension relationships
618 ! following mitchell and heymsfield (2005), jas
620 ! set up array of particle fallspeed to make computationally efficient
622 jj_loop_2: do jj = 1,num_bins1
624 d1 = real(jj)*dd - 0.5*dd !particle size [m]
626 if (d1.le.dcrit) then
631 else if (d1.gt.dcrit.and.d1.le.dcrits2(i_rhor,i_Fr)) then
636 else if (d1.gt.dcrits2(i_rhor,i_Fr).and.d1.le.dcritr2(i_rhor,i_Fr)) then
637 cs1 = cgp2(i_rhor,i_Fr)
641 else if (d1.gt.dcritr2(i_rhor,i_Fr)) then
642 cs1 = csr2(i_rhor,i_Fr)
643 ds1 = dsr2(i_rhor,i_Fr)
648 ! for area, keep bas1 constant, but modify aas1 according to rimed fraction
652 ! dum3 = (1.-Fr)*dum1+Fr*dum2
655 m3 = cgp2(i_rhor,i_Fr)*d1**dg
656 dum3 = dum1+(m1-m2)*(dum2-dum1)/(m3-m2) !linearly interpolate based on particle mass
657 aas1 = dum3/(d1**bas)
661 ! correction for turbulence
662 ! if (d1.lt.500.e-6) then
672 xx = 2.*cs1*g*rho*d1**(ds1+2.-bas1)/(aas1*(mu*rho)**2)
675 b1 = c1*xx**0.5/(2.*((1.+c1*xx**0.5)**0.5-1.)*(1.+c1*xx**0.5)**0.5)-a0*b0*xx** &
676 b0/(c2*((1.+c1*xx**0.5)**0.5-1.)**2)
678 a1 = (c2*((1.+c1*xx**0.5)**0.5-1.)**2-a0*xx**b0)/xx**b1
680 ! velocity in terms of drag terms
681 fall2(i_rhor,i_Fr,jj) = a1*mu**(1.-2.*b1)*(2.*cs1*g/(rho*aas1))**b1*d1**(b1*(ds1-bas1+2.)-1.)
683 !---------------------------------------------------------------
687 !---------------------------------------------------------------------------------
689 ! q = total ice mixing ratio (vapor dep. plus rime mixing ratios), normalized by N
691 i_Qnorm_loop_2: do i = 1,n_Qnorm
697 print*,'&&&&&&&&&&&i_rhor',i_rhor
698 print*,'***************',i,k
702 qerror = 1.e+20 ! initialize qerror to arbitrarily large value
704 !.....................................................................................
705 ! find parameters for gamma distribution
707 ! size distribution for ice is assumed to be
708 ! N(D) = n0 * D^pgam * exp(-lam*D)
710 ! for the given q and N, we need to find n0, pgam, and lam
712 ! approach for finding lambda:
713 ! cycle through a range of lambda, find closest lambda to produce correct q
715 ! start with lam, range of lam from 100 to 5 x 10^6 is large enough to
716 ! cover full range over mean size from 2 to 5000 micron
718 ii_loop_2: do ii = 1,num_bins2
720 lam2(i_rhor,i_Fr,i) = lambdai(ii)
721 mu_i2(i_rhor,i_Fr,i) = diagnostic_mui(lam2(i_rhor,i_Fr,i),q,cgp2(i_rhor,i_Fr),Fr,pi)
723 !set min,max lam corresponding to Dm_max, Dm_min
724 lam2(i_rhor,i_Fr,i) = max(lam2(i_rhor,i_Fr,i),(mu_i2(i_rhor,i_Fr,i)+1.)/Dm_max)
725 lam2(i_rhor,i_Fr,i) = min(lam2(i_rhor,i_Fr,i),(mu_i2(i_rhor,i_Fr,i)+1.)/Dm_min)
727 !get n0, note this is normalized
728 n02(i_rhor,i_Fr,i) = lam2(i_rhor,i_Fr,i)**(mu_i2(i_rhor,i_Fr,i)+1.)/ &
729 (gamma(mu_i2(i_rhor,i_Fr,i)+1.))
731 ! calculate integral for each of the 4 parts of the size distribution
732 ! check difference with respect to q
734 ! set up m-D relationship for solid ice with D < Dcrit
738 call intgrl_section(lam2(i_rhor,i_Fr,i),mu_i2(i_rhor,i_Fr,i), ds1,ds,dg, &
739 dsr2(i_rhor,i_Fr),dcrit,dcrits2(i_rhor,i_Fr), &
740 dcritr2(i_rhor,i_Fr),intgrR1,intgrR2,intgrR3,intgrR4)
742 ! sum of the integrals from the 4 regions of the size distribution
743 qdum = n02(i_rhor,i_Fr,i)*(cs1*intgrR1 + cs*intgrR2 + cgp2(i_rhor,i_Fr)*intgrR3 + &
744 csr2(i_rhor,i_Fr)*intgrR4)
748 lamf = lam2(i_rhor,i_Fr,i)
751 ! find lam with smallest difference between q and estimate of q, assign to lamf
752 if (abs(q-qdum).lt.qerror) then
753 lamf = lam2(i_rhor,i_Fr,i)
759 ! check and print relative error in q to make sure it is not too large
760 ! note: large error is possible if size bounds are exceeded!!!!!!!!!!
762 print*,'qerror (%)',qerror/q*100.
764 ! find n0 based on final lam value
765 ! set final lamf to 'lam' variable
766 ! this is the value of lam with the smallest qerror
767 lam2(i_rhor,i_Fr,i) = lamf
769 ! recalculate mu based on final lam
770 ! mu_i2(i_rhor,i_Fr,i) = diagnostic_mui(log_diagmu_orig,lam2(i_rhor,i_Fr,i),q,cgp2(i_rhor,i_Fr),Fr,pi)
771 mu_i2(i_rhor,i_Fr,i) = diagnostic_mui(lam2(i_rhor,i_Fr,i),q,cgp2(i_rhor,i_Fr),Fr,pi)
773 ! n0 = N*lam**(pgam+1.)/(gamma(pgam+1.))
775 ! find n0 from lam and q
776 ! this is done instead of finding n0 from lam and N, since N
777 ! may need to be adjusted to constrain mean size within reasonable bounds
779 call intgrl_section(lam2(i_rhor,i_Fr,i),mu_i2(i_rhor,i_Fr,i), ds1,ds,dg, &
780 dsr2(i_rhor,i_Fr),dcrit,dcrits2(i_rhor,i_Fr), &
781 dcritr2(i_rhor,i_Fr),intgrR1,intgrR2,intgrR3,intgrR4)
783 n02(i_rhor,i_Fr,i) = q/(cs1*intgrR1 + cs*intgrR2 + cgp2(i_rhor,i_Fr)*intgrR3 + & ! n0 is normalized
784 csr2(i_rhor,i_Fr)*intgrR4)
785 print*,'lam,N0:',lam2(i_rhor,i_Fr,i),n02(i_rhor,i_Fr,i)
786 print*,'pgam:',mu_i2(i_rhor,i_Fr,i)
788 qsave(i,i_Fr,i_rhor) = q ! q is normalized (Q/N)
790 log_lamIsMax(i_rhor,i_Fr,i) = abs(lam2(i_rhor,i_Fr,i)-lamold) .lt. 1.e-8
791 lamold = lam2(i_rhor,i_Fr,i)
799 ! At this point, we have solve for all of the size distribution parameters
801 ! NOTE: In the code it is assumed that mean size and number have already been
802 ! adjusted, so that mean size will fall within allowed bounds. Thus, we do
803 ! not apply a lambda limiter here.
805 !--------------------------------------------------------------------
807 ! RUNNING IN PARALLEL MODE:
809 !------------------------------------------------------------------------------------
810 ! CODE ABOVE HERE IS FOR THE "TOP" OF THE BROKEN UP CODE (for running in parallel)
812 ! Before running ./go_1-compile.ksh, delete all lines below this point and
813 ! and save as 'create_p3_lookupTable_2-top.f90'
814 !------------------------------------------------------------------------------------
816 ! For testing single values, uncomment the following:
819 !------------------------------------------------------------------------------------
820 ! CODE BELOW HERE IS FOR THE "BOTTOM" OF THE BROKEN UP CODE (for running in parallel)
822 ! Before running ./go_1-compile.ksh, delete all lines below this point and
823 ! and save as 'create_p3_lookupTable_2-bottom.f90'
824 !------------------------------------------------------------------------------------
826 !.....................................................................................
827 ! begin category process interaction calculations for the lookup table
829 !.....................................................................................
830 ! collection of category 1 by category 2
831 !.....................................................................................
833 write (filename, "(A12,I0.2,A4)") "lookupTable_2-",i1,".dat"
834 filename = trim(filename)
835 open(unit=1, file=filename, status='unknown')
839 write(1,*) 'LOOKUP_TABLE_2-version: ',trim(version)
844 ! Note: i1 loop (do/enddo statements) is commented out for parallelization; i1 gets initizatized there
845 ! - to run in serial, uncomment the 'Qnorm_loop_3: do i1' statement and the corresponding 'enddo'
847 !Qnorm_loop_3: do i1 = 1,n_Qnorm ! COMMENTED OUT FOR PARALLELIZATION
849 do i_rhor1 = 1,n_rhor
852 do i_rhor2 = 1,n_rhor
854 lamIsMax: if (.not. log_lamIsMax(i_rhor2,i_Fr2,i2)) then
859 !set up binned distribution of ice from categories 1 and 2 (distributions normalized by N)
861 d1 = real(jj)*dd - 0.5*dd
862 num1(jj) = n01(i_rhor1,i_Fr1,i1)*d1**mu_i1(i_rhor1,i_Fr1,i1)* &
863 exp(-lam1(i_rhor1,i_Fr1,i1)*d1)*dd
864 num2(jj) = n02(i_rhor2,i_Fr2,i2)*d1**mu_i2(i_rhor2,i_Fr2,i2)* &
865 exp(-lam2(i_rhor2,i_Fr2,i2)*d1)*dd
868 ! loop over size distribution
869 ! note: collection of ice within the same bin is neglected
871 ! loop over particle 1
872 jj_loop_3: do jj = num_bins1,1,-1
874 d1 = real(jj)*dd - 0.5*dd !particle size [m]
876 if (d1.le.dcrit) then
881 elseif (d1.gt.dcrit.and.d1.le.dcrits1(i_rhor1,i_Fr1)) then
886 else if (d1.gt.dcrits1(i_rhor1,i_Fr1).and.d1.le.dcritr1(i_rhor1,i_Fr1)) then
887 cs1 = cgp1(i_rhor1,i_Fr1)
891 else if (d1.gt.dcritr1(i_rhor1,i_Fr1)) then
892 cs1 = csr1(i_rhor1,i_Fr1)
893 ds1 = dsr1(i_rhor1,i_Fr1)
898 ! for area, keep bas1 constant, but modify aas1 according to rimed fraction
904 m3 = cgp1(i_rhor1,i_Fr1)*d1**dg
905 ! linearly interpolate based on particle mass
906 dum3 = dum1+(m1-m2)*(dum2-dum1)/(m3-m2)
907 aas1 = dum3/(d1**bas)
911 ! loop over particle 2
912 kk_loop: do kk = num_bins1,1,-1
914 d2 = real(kk)*dd - 0.5*dd !particle size [m]
916 ! parameters for particle 2
917 if (d2.le.dcrit) then
922 elseif (d2.gt.dcrit.and.d2.le.dcrits2(i_rhor2,i_Fr2)) then
927 else if (d2.gt.dcrits2(i_rhor2,i_Fr2).and.d2.le.dcritr2(i_rhor2,i_Fr2)) then
928 ! cs2 = cgp2(i_rhor2,i_Fr2)
932 else if (d2.gt.dcritr2(i_rhor2,i_Fr2)) then
933 cs2 = csr2(i_rhor2,i_Fr2)
934 ds2 = dsr2(i_rhor2,i_Fr2)
939 ! for area, keep bas1 constant, but modify aas1 according to rimed fraction
945 m3 = cgp2(i_rhor2,i_Fr2)*d2**dg
946 ! linearly interpolate based on particle mass
947 dum3 = dum1+(m1-m2)*(dum2-dum1)/(m3-m2)
948 aas2 = dum3/(d2**bas)
952 ! absolute value, differential fallspeed
953 ! delu = abs(fall2(i_rhor2,i_Fr2,kk)-fall1(i_rhor1,i_Fr1,jj))
955 ! calculate collection of category 1 by category 2, which occurs
956 ! in fallspeed of particle in category 2 is greater than category 1
958 if (fall2(i_rhor2,i_Fr2,kk).gt.fall1(i_rhor1,i_Fr1,jj)) then
960 delu = fall2(i_rhor2,i_Fr2,kk)-fall1(i_rhor1,i_Fr1,jj)
962 ! note: in micro code we have to multiply by air density
963 ! correction factor for fallspeed, and collection efficiency
967 ! sum1 = # of collision pairs
968 ! the assumption is that each collision pair reduces crystal
969 ! number mixing ratio by 1 kg^-1 s^-1 per kg/m^3 of air (this is
970 ! why we need to multiply by air density, to get units of
972 ! NOTE: For consideration of particle depletion, air density is assumed to be 1 kg m-3
973 ! This problem could be avoided by using number concentration instead of number mixing ratio
974 ! for the lookup table calculations, and then not multipling process rate by air density
975 ! in the P3 code... TO BE FIXED IN THE FUTURE
977 ! sum1 = sum1+min((aas1*d1**bas1+aas2*d2**bas2)*delu*num(jj)*num(kk), &
980 ! set collection efficiency
983 ! accretion of number
984 sum1 = sum1 + (aas1*d1**bas1 + aas2*d2**bas2)*delu*num1(jj)*num2(kk)
986 sum2 = sum2 + cs1*d1**ds1*(aas1*d1**bas1 + aas2*d2**bas2)*delu*num1(jj)*num2(kk)
988 ! remove collected particles from distribution over time period dt, update num1
989 ! note -- dt is time scale for removal, not necessarily the model time step
991 endif ! fall2(i_rhor2,i_Fr2,kk) > fall1(i_rhor1,i_Fr1,jj)
1002 print*,'&&&&&&, skip'
1008 n_agg = dim(n_agg, 1.e-50)
1009 q_agg = dim(q_agg, 1.e-50)
1011 !write(6,'(a5,6i5,2e15.5)') 'index:',i1,i_Fr1,i_rhor1,i2,i_Fr2,i_rhor2,n_agg,q_agg
1012 write(1,'(6i5,2e15.6)') i1,i_Fr1,i_rhor1,i2,i_Fr2,i_rhor2,n_agg,q_agg
1015 enddo ! i_rhor2 loop
1018 enddo ! i_rhor1 loop
1020 !enddo Qnorm_loop_3 ! i1 loop (Qnorm) ! COMMENTED OUT FOR PARALLELIZATION
1024 END PROGRAM create_p3_lookuptable_2
1025 !______________________________________________________________________________________
1027 ! Incomplete gamma function
1028 ! from Numerical Recipes in Fortran 77: The Art of Scientific Computing
1035 ! Returns the incomplete gamma function Q(a,x) = 1-P(a,x)
1037 real gammcf,gammser,gln
1038 if (x.lt.0..or.a.le.0) print*, 'bad argument in gammq'
1040 call gser(gamser,a,x,gln)
1043 call gcf(gammcf,a,x,gln)
1049 !-------------------------------------
1051 subroutine gser(gamser,a,x,gln)
1053 real a,gamser,gln,x,eps
1054 parameter(itmax=100,eps=3.e-7)
1056 real ap,del,sum,gamma
1059 if (x.lt.0.) print*, 'x < 0 in gser'
1070 if (abs(del).lt.abs(sum)*eps) goto 1
1072 print*, 'a too large, itmax too small in gser'
1073 1 gamser=sum*exp(-x+a*log(x)-gln)
1077 !-------------------------------------
1079 subroutine gcf(gammcf,a,x,gln)
1081 real a,gammcf,gln,x,eps,fpmin
1082 parameter(itmax=100,eps=3.e-7,fpmin=1.e-30)
1084 real an,b,c,d,del,h,gamma
1094 if(abs(d).lt.fpmin) d=fpmin
1096 if(abs(c).lt.fpmin) c=fpmin
1100 if(abs(del-1.).lt.eps)goto 1
1102 ! pause 'a too large, itmax too small in gcf'
1103 print*, 'a too large, itmax too small in gcf'
1104 1 gammcf=exp(-x+a*log(x)-gln)*h
1108 !______________________________________________________________________________________
1110 real function diagnostic_mui(lam,q,cgp,Fr,pi)
1112 !----------------------------------------------------------!
1113 ! Compute mu_i diagnostically.
1114 !----------------------------------------------------------!
1119 real :: lam,q,cgp,Fr,pi
1122 real :: mu_i,dum1,dum2,dum3
1123 real, parameter :: mu_i_min = 0.
1124 real, parameter :: mu_i_max = 20. !note: for orig 2-mom, mu_i_max=6.
1125 real, parameter :: Di_thres = 0.2 !diameter threshold [mm]
1127 !-- original formulation: (from Heymsfield, 2003)
1128 ! mu_i = 0.076*(lam/100.)**0.8-2. ! /100 is to convert m-1 to cm-1
1129 ! mu_i = max(mu_i,0.) ! make sure mu_i >= 0, otherwise size dist is infinity at D = 0
1130 ! mu_i = min(mu_i,6.)
1133 !-- formulation based on 3-moment results (see 2021 JAS article)
1134 dum1 = (q/cgp)**(1./3)*1000. ! estimated Dmvd [mm], assuming spherical
1135 if (dum1<=Di_thres) then
1136 !diagnostic mu_i, original formulation: (from Heymsfield, 2003)
1137 mu_i = 0.076*(lam*0.01)**0.8-2. ! /100 is to convert m-1 to cm-1
1140 dum2 = (6./pi)*cgp ! mean density (total)
1141 dum3 = max(1., 1.+0.00842*(dum2-400.)) ! adjustment factor for density
1142 mu_i = 0.25*(dum1-Di_thres)*dum3*Fr
1144 mu_i = max(mu_i, mu_i_min)
1145 mu_i = min(mu_i, mu_i_max)
1147 diagnostic_mui = mu_i
1149 end function diagnostic_mui
1151 !______________________________________________________________________________________
1153 subroutine intgrl_section(lam,mu, d1,d2,d3,d4, Dcrit1,Dcrit2,Dcrit3, &
1154 intsec_1,intsec_2,intsec_3,intsec_4)
1156 ! Computes and returns partial integrals (partial moments) of ice PSD.
1162 real, intent(in) :: lam,mu, d1,d2,d3,d4, Dcrit1,Dcrit2,Dcrit3
1163 real, intent(out) :: intsec_1,intsec_2,intsec_3,intsec_4
1169 !Region I -- integral from 0 to Dcrit1 (small spherical ice)
1170 intsec_1 = lam**(-d1-mu-1.)*gamma(mu+d1+1.)*(1.-gammq(mu+d1+1.,Dcrit1*lam))
1172 !Region II -- integral from Dcrit1 to Dcrit2 (non-spherical unrimed ice)
1173 intsec_2 = lam**(-d2-mu-1.)*gamma(mu+d2+1.)*(gammq(mu+d2+1.,Dcrit1*lam))
1174 dum = lam**(-d2-mu-1.)*gamma(mu+d2+1.)*(gammq(mu+d2+1.,Dcrit2*lam))
1175 intsec_2 = intsec_2-dum
1177 !Region III -- integral from Dcrit2 to Dcrit3 (fully rimed spherical ice)
1178 intsec_3 = lam**(-d3-mu-1.)*gamma(mu+d3+1.)*(gammq(mu+d3+1.,Dcrit2*lam))
1179 dum = lam**(-d3-mu-1.)*gamma(mu+d3+1.)*(gammq(mu+d3+1.,Dcrit3*lam))
1180 intsec_3 = intsec_3-dum
1182 !Region IV -- integral from Dcrit3 to infinity (partially rimed ice)
1183 intsec_4 = lam**(-d4-mu-1.)*gamma(mu+d4+1.)*(gammq(mu+d4+1.,Dcrit3*lam))
1187 end subroutine intgrl_section
1190 !______________________________________________________________________________________
1192 real function Q_normalized(i_qnorm)
1195 ! Computes the normalized Q (qitot/nitot) based on a given index (used in the Qnorm loops)
1203 !Q_normalized = 261.7**((i_qnorm+5)*0.2)*1.e-18 ! v4, v5.0 (range of mean mass diameter from ~ 1 micron to 1 cm) (for n_Qnorm = 25)
1204 !Q_normalized = 800.**((i_qnorm+10)*0.1)*1.e-18 ! based on LT1-v5.2 (range for Dm_max = 400000.) (for n_Qnorm = 50)
1205 Q_normalized = 800.**(0.2*(i_qnorm+5))*1.e-18 ! (range for Dm_max = 400000.) (for n_Qnorm = 25)
1207 !--- from LT1: [TO BE DELTED]
1208 ! !q = 261.7**((i_Qnorm+10)*0.1)*1.e-18 ! old (strict) lambda limiter
1209 ! q = 800.**((i_Qnorm+10)*0.1)*1.e-18 ! new lambda limiter (for n_Qnorm = 50)
1214 end function Q_normalized
1217 !______________________________________________________________________________________
1219 real function lambdai(i)
1222 ! Computes the lambda_i for a given index (used in loops to solve for PSD parameters)
1228 integer, intent(in) :: i
1230 !lambdai = 1.0013**i*100. !used with Dm_max = 2000.
1231 lambdai = 1.0013**i*10. !used with Dm_max = 400000.
1235 end function lambdai