1 module module_mosaic_coag1d
5 use module_data_mosaic_kind
18 !-----------------------------------------------------------------------
19 subroutine mosaic_coag_1box( istat_coag, &
22 temp_box, patm_box, rhoair_box, rbox, &
23 fact_apmassmr, fact_apnumbmr, fact_apdens, fact_apdiam, &
24 adrydens_box, awetdens_box, &
25 adrydpav_box, awetdpav_box, adryqmas_box, mcoag_flag1 )
27 ! calculates aerosol particle coagulation for a single grid box
28 ! over timestep dtcoag_in
30 ! uses the single-type coagulation algorithm of jacobson (2002)
32 use module_data_mosaic_main, only: ntot_used, pi, piover6, third
33 use module_data_mosaic_aero, only: ifreq_coag
34 use module_data_mosaic_asecthp, only: &
35 ai_phase, hyswptr_aer, &
37 maxd_acomp, maxd_asize, maxd_atype, &
38 ncomp_aer, ncomp_plustracer_aer, nsize_aer, ntype_aer, &
39 massptr_aer, numptr_aer, waterptr_aer, &
40 dens_aer, dens_water_aer, &
41 dcen_sect, dhi_sect, dlo_sect, &
43 volumcen_sect, volumcut_sect, volumhi_sect, volumlo_sect
47 integer, intent(inout) :: istat_coag ! =0 if no problems
48 integer, intent(in) :: idiagbb_in ! controls diagnostics
49 integer, intent(in) :: itstep ! host code time step index
50 integer, intent(in) :: mcoag_flag1
51 real(r8), intent(in) :: dtcoag_in ! time step (s)
52 real(r8), intent(in) :: temp_box ! air temp (K)
53 real(r8), intent(in) :: patm_box ! air pressure (atm)
54 real(r8), intent(in) :: rhoair_box ! air density (g/cm^3)
56 real(r8), intent(inout) :: rbox(ntot_used)
57 ! rbox - holds aerosol number and component mass mixing ratios
58 ! units for number mr are such that (rbox*fact_apnumbmr) gives (#/g-air)
59 ! units for mass mr are such that (rbox*fact_apmassmr) gives (g/g-air)
61 real(r8), intent(in) :: fact_apmassmr, fact_apnumbmr, fact_apdens, fact_apdiam
62 ! these are units conversion factors
63 ! the module_data_mosaic_asecthp densities are such that
64 ! dens_aer*fact_apdens gives (g/cm^3)
65 ! the module_data_mosaic_asecthp diameters are such that
66 ! d[lo,cen,hi]_sect*fact_apdiam gives (cm)
67 ! the module_data_mosaic_asecthp volumes are such that
68 ! volum[lo,cen,hi]_sect**fact_apdiam**3) gives (cm^3)
70 real(r8), intent(inout) :: adrydens_box(maxd_asize,maxd_atype)
71 real(r8), intent(inout) :: awetdens_box(maxd_asize,maxd_atype)
72 real(r8), intent(inout) :: adrydpav_box(maxd_asize,maxd_atype)
73 real(r8), intent(inout) :: awetdpav_box(maxd_asize,maxd_atype)
74 real(r8), intent(inout) :: adryqmas_box(maxd_asize,maxd_atype)
75 ! adryqmas_box = aerosol total-dry-mass mixing ratio (g/mol-air)
76 ! adrydens_box = aerosol dry density (units same as dens_aer)
77 ! awetdens_box = aerosol wet density (units same as dens_aer)
78 ! adrydpav_box = aerosol mean dry diameter (units same as dlo_sect)
79 ! awetdpav_box = aerosol mean wet diameter (units same as dlo_sect)
80 ! adryqmas_box = aerosol total-dry-mass mixing ratio (units same as rbox)
84 integer, parameter :: coag_method = 1
85 integer, parameter :: ncomp_coag_maxd = maxd_acomp + 3
87 integer :: l, ll, lla, llb, llx
88 integer :: isize, itype, iphase
89 integer :: iconform_numb
90 integer :: idiagbb, idiag_coag_onoff
91 integer :: lunout_coag
92 integer :: ncomp_coag, nsize, nsubstep_coag, ntau_coag
93 integer,save :: ncountaa(10), ncountbb(0:ncomp_coag_maxd)
95 real(r8), parameter :: factsafe = 1.00001
97 ! units for local working varibles:
100 ! number mixing ratio = #/g-air
101 ! EXCEPT num_distrib = #/cm^3-air
102 ! mass mixing ratio = g/g-air
103 ! volume mixing ratio = cm^3/g-air
105 real(r8) :: densdefault, dtcoag
106 real(r8) :: fact_apdia3, fact_apvolumr
107 real(r8) :: press_cgs ! air pressure (dyne/cm2)
108 real(r8) :: tmpa, tmpb
109 real(r8) :: wwdens, wwdpav, wwmass, wwvolu
110 real(r8) :: xxdens, xxdpav, xxmass, xxnumb, xxvolu
111 real(r8) :: xxmasswet, xxvoluwet
113 real(r8) :: adryqvol_box(maxd_asize,maxd_atype) ! aerosol total-dry-volume mixing ratio (cm3/mol-air)
114 real(r8) :: dpdry(maxd_asize), dpwet(maxd_asize), denswet(maxd_asize)
115 real(r8) :: num_distrib(maxd_asize)
116 real(r8) :: sumnew(ncomp_coag_maxd), sumold(ncomp_coag_maxd)
117 real(r8) :: sum_num(2), sum_vol(0:ncomp_coag_maxd,2)
118 real(r8) :: vol_distrib(maxd_asize,ncomp_coag_maxd)
119 real(r8) :: vpcut(0:maxd_asize), vpdry(maxd_asize)
120 real(r8) :: xxvolubb(maxd_asize)
122 character(len=120) :: msg
126 if (mcoag_flag1 <= 0) return
127 if (ifreq_coag > 1) then
128 if (mod(itstep,ifreq_coag) /= 0) return
129 dtcoag = dtcoag_in*ifreq_coag
137 if (lunout .gt. 0) lunout_coag = lunout
140 ! set variables that do not change
142 idiag_coag_onoff = +1
143 if (idiagbb <= 0) idiag_coag_onoff = 0
146 fact_apdia3 = fact_apdiam*fact_apdiam*fact_apdiam
147 fact_apvolumr = fact_apmassmr/fact_apdens
150 press_cgs = patm_box*1.01325e6_r8
153 ! temporary diagnostics
158 do 2800 itype = 1, ntype_aer
159 ! do 2800 itype = 1, 1
161 nsize = nsize_aer(itype)
162 ncomp_coag = ncomp_plustracer_aer(itype) + 3
164 densdefault = dens_aer(1,itype)*fact_apdens
165 vpcut(0:nsize) = volumcut_sect(0:nsize,itype)*fact_apdia3
167 ncountaa(1) = ncountaa(1) + 1
169 ! do initial calculation of dry mass, volume, and density,
170 ! and initial number conforming (as needed)
171 vol_distrib(:,:) = 0.0
176 l = numptr_aer(isize,itype,iphase)
177 xxnumb = rbox(l)*fact_apnumbmr
178 xxmass = adryqmas_box(isize,itype)*fact_apmassmr
179 xxdens = adrydens_box(isize,itype)*fact_apdens
182 if ( (xxdens .lt. 0.1) .or. (xxdens .gt. 20.0) ) then
183 ! (exception) case of drydensity not valid
184 ! so compute dry mass and volume from rbox
185 ncountaa(2) = ncountaa(2) + 1
187 if (idiagbb .ge. 200) &
188 write(lunout_coag,'(a,i10,2i4,1p,4e10.2)') 'coagaa-2a', &
189 itstep, itype, isize, xxmass, xxvolu, xxdens
191 do ll = 1, ncomp_aer(itype)
192 l = massptr_aer(ll,isize,itype,iphase)
194 tmpa = max( 0.0_r8, rbox(l) )
195 xxmass = xxmass + tmpa
196 xxvolu = xxvolu + tmpa/dens_aer(ll,itype)
199 xxmass = xxmass*fact_apmassmr
200 xxvolu = xxvolu*fact_apvolumr
201 if (xxmass .gt. 0.99*smallmassbb) then
202 xxdens = xxmass/xxvolu
203 xxvolu = xxmass/xxdens
204 if (idiagbb .ge. 200) &
205 write(lunout_coag,'(a,i10,2i4,1p,4e10.2)') 'coagaa-2b', &
206 itstep, itype, isize, xxmass, xxvolu, xxdens
209 xxvolu = xxmass/xxdens
212 if (xxmass .le. 1.01*smallmassbb) then
213 ! when drymass extremely small, use default density and bin center size,
215 ncountaa(3) = ncountaa(3) + 1
217 xxvolu = xxmass/xxdens
218 xxnumb = xxmass/(volumcen_sect(isize,itype)*fact_apdia3*xxdens)
219 l = hyswptr_aer(isize,itype)
220 if (l .ge. 1) rbox(l) = 0.0
221 l = waterptr_aer(isize,itype)
222 if (l .ge. 1) rbox(l) = 0.0
224 if (idiagbb .ge. 200) &
225 write(lunout_coag,'(a,i10,2i4,1p,4e10.2)') 'coagaa-2c', &
226 itstep, itype, isize, xxmass, xxvolu, xxdens
228 xxvolu = xxmass/xxdens
231 ! check for mean dry-size 'safely' within bounds, and conform number if not
232 if (iconform_numb .gt. 0) then
234 xxvolu/(factsafe*volumlo_sect(isize,itype)*fact_apdia3)) then
235 ncountaa(4) = ncountaa(4) + 1
237 xxnumb = xxvolu/(factsafe*volumlo_sect(isize,itype)*fact_apdia3)
238 if (idiagbb .ge. 200) &
239 write(lunout_coag,'(a,i10,2i4,1p,3e12.4)') 'coagcc-4 ', &
240 itstep, itype, isize, xxvolu, tmpa, xxnumb
241 else if (xxnumb .lt. &
242 xxvolu*factsafe/(volumhi_sect(isize,itype)*fact_apdia3)) then
243 ncountaa(5) = ncountaa(5) + 1
245 xxnumb = xxvolu*factsafe/(volumhi_sect(isize,itype)*fact_apdia3)
246 if (idiagbb .ge. 200) &
247 write(lunout_coag,'(a,i10,2i4,1p,3e12.4)') 'coagcc-5 ', &
248 itstep, itype, isize, xxvolu, tmpa, xxnumb
249 if (idiagbb .ge. 200) &
250 write(lunout_coag,'(a,10x, 8x,1p,4e12.4)') 'coagcc-5 ', &
251 dlo_sect(isize,itype), dlo_sect(isize,itype)*fact_apdiam, &
252 volumlo_sect(isize,itype), volumlo_sect(isize,itype)*fact_apdia3
253 if (idiagbb .ge. 200) &
254 write(lunout_coag,'(a,10x, 8x,1p,4e12.4)') 'coagcc-5 ', &
255 dhi_sect(isize,itype), dhi_sect(isize,itype)*fact_apdiam, &
256 volumhi_sect(isize,itype), volumhi_sect(isize,itype)*fact_apdia3
260 ! load numb, mass, volu, dens back into mosaic_asecthp arrays
261 l = numptr_aer(isize,itype,iphase)
262 rbox(l) = xxnumb/fact_apnumbmr
263 adrydens_box(isize,itype) = xxdens/fact_apdens
264 adryqmas_box(isize,itype) = xxmass/fact_apmassmr
265 adryqvol_box(isize,itype) = xxvolu/fact_apvolumr
268 ! load number/mass/volume into working arrays
271 ! num_distrib must be (#/cm3)
272 ! vol_distrib units do not matter - they can be either masses or volumes,
273 ! and either mixing ratios or concentrations
274 ! (e.g., g/cm3-air, ug/kg-air, cm3/cm3-air, cm3/kg-air, ...)
276 num_distrib(isize) = xxnumb*rhoair_box
278 do ll = 1, ncomp_plustracer_aer(itype)
279 l = massptr_aer(ll,isize,itype,iphase)
281 if (l .ge. 1) tmpa = max( 0.0_r8, rbox(l) )*fact_apmassmr
282 vol_distrib(isize,ll) = tmpa
283 sumold(ll) = sumold(ll) + tmpa
287 ll = (ncomp_coag-3) + llx
290 l = hyswptr_aer(isize,itype)
291 if (l .ge. 1) tmpa = max( 0.0_r8, rbox(l) )*fact_apmassmr
292 else if (llx .eq. 2) then
293 l = waterptr_aer(isize,itype)
294 if (l .ge. 1) tmpa = max( 0.0_r8, rbox(l) )*fact_apmassmr
296 tmpa = max( 0.0_r8, adryqvol_box(isize,itype) )*fact_apvolumr
298 vol_distrib(isize,ll) = tmpa
299 sumold(ll) = sumold(ll) + tmpa
302 ! calculate dry and wet diameters and wet density
303 if (xxmass .le. 1.01*smallmassbb) then
304 dpdry(isize) = dcen_sect(isize,itype)*fact_apdiam
305 dpwet(isize) = dpdry(isize)
306 denswet(isize) = xxdens
308 dpdry(isize) = (xxvolu/(xxnumb*piover6))**third
309 dpdry(isize) = max( dpdry(isize), dlo_sect(isize,itype)*fact_apdiam )
310 l = waterptr_aer(isize,itype)
312 tmpa = max( 0.0_r8, rbox(l) )*fact_apmassmr
313 xxmasswet = xxmass + tmpa
314 xxvoluwet = xxvolu + tmpa/(dens_water_aer*fact_apdens)
315 dpwet(isize) = (xxvoluwet/(xxnumb*piover6))**third
316 dpwet(isize) = min( dpwet(isize), 30.0_r8*dhi_sect(isize,itype)*fact_apdiam )
317 denswet(isize) = (xxmasswet/xxvoluwet)
319 dpwet(isize) = dpdry(isize)
320 denswet(isize) = xxdens
324 vpdry(isize) = piover6 * (dpdry(isize)**3)
330 ! make call to coagulation routine
332 if (idiag_coag_onoff > 0) call coag_conserve_check( &
333 nsize, maxd_asize, ncomp_coag, ncomp_coag_maxd, 1, lunout, &
334 dpdry, num_distrib, vol_distrib, sum_num, sum_vol )
336 if ((mcoag_flag1 >= 10) .and. (mcoag_flag1 <= 19)) then
337 call movingcenter_singletype_coag( &
338 idiagbb, lunout_coag, nsize, maxd_asize, ncomp_coag, ncomp_coag_maxd, &
339 temp_box, press_cgs, dtcoag, nsubstep_coag, &
340 vpcut, vpdry, dpdry, dpwet, denswet, num_distrib, vol_distrib )
343 call jacobson2002_singletype_coag( &
344 nsize, maxd_asize, ncomp_coag, ncomp_coag_maxd, &
345 temp_box, press_cgs, dtcoag, nsubstep_coag, &
346 dpdry, dpwet, denswet, num_distrib, vol_distrib )
350 if (idiag_coag_onoff > 0) call coag_conserve_check( &
351 nsize, maxd_asize, ncomp_coag, ncomp_coag_maxd, 2, lunout, &
352 dpdry, num_distrib, vol_distrib, sum_num, sum_vol )
356 ! unload number/mass/volume from working arrays
361 do ll = 1, ncomp_coag
362 sumnew(ll) = sumnew(ll) + max( 0.0_r8, vol_distrib(isize,ll) )
365 l = numptr_aer(isize,itype,iphase)
366 rbox(l) = max( 0.0_r8, num_distrib(isize)/(rhoair_box*fact_apnumbmr) )
368 ! unload mass mixing ratios into rbox; also calculate dry mass and volume
371 do ll = 1, ncomp_aer(itype)
372 l = massptr_aer(ll,isize,itype,iphase)
374 tmpa = max( 0.0_r8, vol_distrib(isize,ll) )
375 rbox(l) = tmpa/fact_apmassmr
376 xxmass = xxmass + tmpa
377 xxvolu = xxvolu + tmpa/(dens_aer(ll,itype)*fact_apdens)
380 adryqmas_box(isize,itype) = xxmass/fact_apmassmr
381 xxvolubb(isize) = xxvolu
383 ll = (ncomp_coag-3) + 1
384 l = hyswptr_aer(isize,itype)
385 if (l .ge. 1) rbox(l) = max( 0.0_r8, vol_distrib(isize,ll) )/fact_apmassmr
387 ll = (ncomp_coag-3) + 2
388 l = waterptr_aer(isize,itype)
389 if (l .ge. 1) rbox(l) = max( 0.0_r8, vol_distrib(isize,ll) )/fact_apmassmr
391 ll = (ncomp_coag-3) + 3
392 adryqvol_box(isize,itype) = max( 0.0_r8, vol_distrib(isize,ll) )/fact_apvolumr
396 ! check for mass and volume conservation
397 do ll = 1, ncomp_coag
398 tmpa = max( sumold(ll), sumnew(ll), 1.0e-35_r8 )
399 if (abs(sumold(ll)-sumnew(ll)) .gt. 1.0e-6*tmpa) then
400 ncountbb(ll) = ncountbb(ll) + 1
401 ncountbb(0) = ncountbb(0) + 1
407 ! calculate new dry density,
408 ! and check for new mean dry-size within bounds
413 xxmass = adryqmas_box(isize,itype)*fact_apmassmr
414 xxvolu = adryqvol_box(isize,itype)*fact_apvolumr
415 l = numptr_aer(isize,itype,iphase)
416 xxnumb = rbox(l)*fact_apnumbmr
419 ! do a cautious calculation of density, using volume from coag routine
420 if (xxmass .le. smallmassbb) then
421 ncountaa(8) = ncountaa(8) + 1
423 xxvolu = xxmass/xxdens
424 xxnumb = xxmass/(volumcen_sect(isize,itype)*fact_apdia3*xxdens)
425 xxdpav = dcen_sect(isize,itype)*fact_apdiam
428 l = hyswptr_aer(isize,itype)
429 if (l .ge. 1) rbox(l) = 0.0
430 l = waterptr_aer(isize,itype)
431 if (l .ge. 1) rbox(l) = 0.0
433 if (idiagbb .ge. 200) &
434 write(lunout_coag,'(a,i10,2i4,1p,4e10.2)') 'coagaa-7a', &
435 itstep, itype, isize, xxmass, xxvolu, xxdens
436 else if (xxmass .gt. 1000.0*xxvolu) then
437 ! in this case, density is too large. setting density=1000 forces
438 ! next IF block while avoiding potential divide by zero or overflow
441 xxdens = xxmass/xxvolu
444 if ((xxdens .lt. 0.1) .or. (xxdens .gt. 20.0)) then
445 ! (exception) case -- new dry density is unrealistic,
446 ! so use dry volume from rbox instead of that from coag routine
447 ncountaa(7) = ncountaa(7) + 1
448 if (idiagbb .ge. 200) &
449 write(lunout_coag,'(a,i10,2i4,1p,4e10.2)') 'coagaa-7b', &
450 itstep, itype, isize, xxmass, xxvolu, xxdens
451 xxvolu = xxvolubb(isize)
452 xxdens = xxmass/xxvolu
453 if (idiagbb .ge. 200) &
454 write(lunout_coag,'(a,18x,1p,4e10.2)') 'coagaa-7c', &
455 xxmass, xxvolu, xxdens
458 ! check for mean size ok, and conform number if not
459 if (iconform_numb .gt. 0) then
460 if (xxnumb .gt. xxvolu/(volumlo_sect(isize,itype)*fact_apdia3)) then
461 ncountaa(9) = ncountaa(9) + 1
463 xxnumb = xxvolu/(volumlo_sect(isize,itype)*fact_apdia3)
464 xxdpav = dlo_sect(isize,itype)*fact_apdiam
465 if (idiagbb .ge. 200) &
466 write(lunout_coag,'(a,i10,2i4,1p,3e12.4)') 'coagcc-9 ', &
467 itstep, itype, isize, xxvolu, tmpa, xxnumb
468 else if (xxnumb .lt. xxvolu/(volumhi_sect(isize,itype)*fact_apdia3)) then
469 ncountaa(10) = ncountaa(10) + 1
471 xxnumb = xxvolu/(volumhi_sect(isize,itype)*fact_apdia3)
472 xxdpav = dhi_sect(isize,itype)*fact_apdiam
473 if (idiagbb .ge. 200) &
474 write(lunout_coag,'(a,i10,2i4,1p,3e12.4)') 'coagcc-10', &
475 itstep, itype, isize, xxvolu, tmpa, xxnumb
477 xxdpav = (xxvolu/(xxnumb*piover6))**third
481 l = waterptr_aer(isize,itype)
482 if (l .ge. 1) tmpb = max(0.0_r8,rbox(l))*fact_apmassmr
483 wwmass = xxmass + tmpb
484 wwvolu = xxvolu + tmpb/(dens_water_aer*fact_apdens)
485 wwdens = wwmass/wwvolu
486 wwdpav = xxdpav*((wwvolu/xxvolu)**third)
489 ! load number, mass, volume, dry-density back into arrays
490 l = numptr_aer(isize,itype,iphase)
491 rbox(l) = xxnumb/fact_apnumbmr
492 adrydens_box(isize,itype) = xxdens/fact_apdens
493 adrydpav_box(isize,itype) = xxdpav/fact_apdiam
494 adryqmas_box(isize,itype) = xxmass/fact_apmassmr
495 adryqvol_box(isize,itype) = xxvolu/fact_apvolumr
496 awetdens_box(isize,itype) = wwdens/fact_apdens
497 awetdpav_box(isize,itype) = wwdpav/fact_apdiam
502 ! temporary diagnostics
503 if (idiagbb .ge. 100) then
504 write(msg,93030) 'coagbb ncntaa ', ncountaa(1:10)
505 call peg_message( lunerr, msg )
506 if (ncountbb(0) .gt. 0) then
507 do llx = 1, (ncomp_coag+9)/10
510 llb = min( llb, ncomp_coag)
511 write(msg,93032) 'coagbb ncntbb', &
512 mod(llx,10), ncountbb(lla:llb)
513 call peg_message( lunerr, msg )
517 93020 format( a, 1p, 10e10.2 )
518 93030 format( a, 1p, 10i10 )
519 93032 format( a, 1p, i1, 10i10 )
522 2800 continue ! itype = 1, ntype_aer
526 end subroutine mosaic_coag_1box
529 !----------------------------------------------------------------------
530 subroutine coag_conserve_check( &
531 nbin, nbin_maxd, ncomp, ncomp_maxd, iflagaa, lunout, &
532 dpdry, num_distrib, vol_distrib, sum_num, sum_vol )
536 ! checks on conservation of volume before/after coag routine
538 integer, intent(in) :: nbin ! actual number of size bins
539 integer, intent(in) :: nbin_maxd ! size-bin dimension for input (argument) arrays
540 integer, intent(in) :: ncomp ! actual number of aerosol volume components
541 integer, intent(in) :: ncomp_maxd ! volume-component dimension for input (argument) arrays
542 integer, intent(in) :: lunout ! logical unit for warning & diagnostic output
543 integer, intent(in) :: iflagaa !
545 real(r8), intent(in) :: dpdry(nbin_maxd)
546 real(r8), intent(in) :: num_distrib(nbin_maxd)
547 real(r8), intent(in) :: vol_distrib(nbin_maxd,ncomp_maxd)
548 real(r8), intent(inout) :: sum_num(2)
549 real(r8), intent(inout) :: sum_vol(0:ncomp_maxd,2)
553 real(r8), parameter :: pi = 3.14159265358979323846_r8
554 real(r8) :: voldry(nbin)
557 if (iflagaa == 1) then
559 else if (iflagaa == 2) then
566 ! sum initial or number and volumes
567 voldry(1:nbin) = (pi/6.0)*(dpdry(1:nbin)**3)
568 sum_num(i) = sum( num_distrib(1:nbin) )
569 sum_vol(0,i) = sum( num_distrib(1:nbin)*voldry(1:nbin) )
571 sum_vol(l,i) = sum( vol_distrib(1:nbin,l) )
575 if (iflagaa /= 2) return
577 write(lunout,'(2a,1p,2e14.6)') &
578 'number total ', ' initial, final =', &
580 write(lunout,'(2a,1p,2e14.6)') &
581 'number total ', ' (final/initial) =', &
582 sum_num(2)/sum_num(1)
583 write(lunout,'(2a,1p,2e14.6)') &
584 'volume total ', ' initial, final =', &
586 write(lunout,'(2a,1p,2e14.6)') &
587 'volume total ', ' (initial/final)-1 =', &
588 (sum_vol(0,1)/sum_vol(0,2))-1.0
591 if (abs(sum_vol(l,2)) > 0.0) then
592 write(lunout,'(a,i4,a,1p,2e14.6)') &
593 'volume l=', l, ' (initial/final)-1 =', &
594 (sum_vol(l,1)/sum_vol(l,2))-1.0
596 write(lunout,'(a,i4,a,1p,2e14.6)') &
597 'volume l=', l, ' initial, final =', &
598 sum_vol(l,1), sum_vol(l,2)
603 end subroutine coag_conserve_check
607 !----------------------------------------------------------------------
608 subroutine movingcenter_singletype_coag( &
609 idiagbb, lunout, nbin, nbin_maxd, ncomp, ncomp_maxd, &
610 tempk, press, deltat, nsubstep_in, &
611 vpcut, vpdry_in, dpdry, dpwet, denswet, &
612 num_distrib, vol_distrib )
616 ! calculates aerosol coagulation for sectional representation
617 ! and a single "particle type" using the single-type algorithm of
621 ! jacobson, m. z., 2002, analysis of aerosol interactions with numerical
622 ! techniques for solving coagulation, nucleation, condensation,
623 ! dissolution, and reversible chemistry among multiple size
624 ! distributions, j. geophys. res., 107, d19, 4366
628 integer, intent(in) :: idiagbb
629 integer, intent(in) :: lunout
630 integer, intent(in) :: nbin ! actual number of size bins
631 integer, intent(in) :: nbin_maxd ! size-bin dimension for input (argument) arrays
632 integer, intent(in) :: ncomp ! actual number of aerosol volume components
633 integer, intent(in) :: ncomp_maxd ! volume-component dimension for input (argument) arrays
634 integer, intent(in) :: nsubstep_in ! number of time sub-steps for the integration
636 real(r8), intent(in) :: tempk ! air temperature (K)
637 real(r8), intent(in) :: press ! air pressure (dyne/cm2)
638 real(r8), intent(in) :: deltat ! integration time (s)
639 real(r8), intent(in) :: dpdry(nbin_maxd) ! bin current volume-mean dry diameter (cm)
640 real(r8), intent(in) :: dpwet(nbin_maxd) ! bin wet (ambient) diameter (cm)
641 real(r8), intent(in) :: denswet(nbin_maxd) ! bin wet (ambient) density (g/cm3)
642 real(r8), intent(in) :: vpdry_in(nbin_maxd) ! bin current mean 1-particle dry volume (cm^3)
643 real(r8), intent(in) :: vpcut(0:nbin_maxd) ! 1-particle dry volumes at bin boundaries (cm^3)
646 real(r8), intent(inout) :: num_distrib(nbin_maxd)
647 ! num_distrib(i) = number concentration for bin i (#/cm3)
648 real(r8), intent(inout) :: vol_distrib(nbin_maxd,ncomp_maxd)
649 ! vol_distrib(i,l) = volume concentration for bin i, component l (cm3/cm3)
652 ! NOTE -- The sectional representation is based on dry particle size.
653 ! Dry sizes are used to calculate the receiving bin indices and product fractions
654 ! for each (bin-i1, bin-i2) coagulation combination.
655 ! Wet sizes and densities are used to calculate the brownian coagulation kernel.
657 ! NOTE -- Units for num_distrib and vol_distrib
658 ! num_distrib units MUST BE (#/cm3)
660 ! (cm3/cm3) units are most consistent with the num_distrib units
661 ! However, the algorithm works with almost any units! So vol_distrib can
662 ! be either masses or volumes, and either mixing ratios or concentrations
663 ! (e.g., g/cm3-air, ug/kg-air, um3/cm3-air, cm3/kg-air, ...).
664 ! Use whatever is convenient.
670 integer :: i, isubstep, j, k, l, nsubstep
672 real(r8), parameter :: frac_loss_limit = 0.5_r8
673 real(r8), parameter :: pi = 3.14159265358979323846_r8
676 del_num, del_voli, del_volj, deltat_sub, &
677 tmpa, tmpb, tmp_beta, &
682 cnum(nbin), cnum_old(nbin), &
683 cvol(nbin,ncomp), cvol_old(nbin,ncomp), &
688 ! calculate brownian coagulation kernel
690 call brownian_kernel( &
691 nbin, tempk, press, dpwet, denswet, betaxh )
694 ! check that fractional number loss from any bin over deltat_sub does
695 ! not exceed frac_loss_limit
697 nsubstep = nsubstep_in
698 deltat_sub = deltat/nsubstep ! time substep (s)
703 tmpb = tmpb + betaxh(i,j)*num_distrib(i)
705 tmpb = tmpb - 0.5*betaxh(j,j)*num_distrib(j)
706 tmpa = max( tmpa, tmpb )
708 if (idiagbb > 0) write(lunout,'(/a,i10,1p,4e12.4)') 'coag aa nsub, dtsub, fracloss', &
709 nsubstep, deltat_sub, tmpa*deltat_sub, frac_loss_limit
710 if (tmpa*deltat_sub > frac_loss_limit) then
711 tmpb = tmpa*deltat/frac_loss_limit
712 isubstep = int(tmpb) + 1
713 tmpb = deltat/isubstep
714 if (idiagbb > 0) write(lunout,'( a,i10,1p,4e12.4)') 'coag bb nsub, dtsub, fracloss', &
715 isubstep, tmpb, tmpa*tmpb
717 deltat_sub = deltat/nsubstep
720 ! multiply coag kernel by deltat_sub
722 ! ' dpdry_i (um) dpdry_j (um) coag coef (cm3/#/s)'
725 ! write(92,'(1p,3e16.7)') &
726 ! (1.0e4*dpwet(i)), (1.0e4*dpwet(j)), betaxh(i,j)
727 betaxh(i,j) = betaxh(i,j) * deltat_sub
732 ! initialize working arrays
733 ! cnum(i) = current number conc (#/cm3) for bin i
734 ! cvol(i,l) = current volume conc (cm3/cm3) for bin i, species l
735 cnum(1:nbin) = num_distrib(1:nbin)
736 cvol(1:nbin,1:ncomp) = vol_distrib(1:nbin,1:ncomp)
739 ! solve coagulation over multiple time substeps
741 solve_isubstep_loop: &
742 do isubstep = 1, nsubstep
743 ! write(91,*) 'maa =', 2
745 cnum_old(1:nbin) = cnum(1:nbin)
746 cvol_old(1:nbin,1:ncomp) = cvol(1:nbin,1:ncomp)
747 if (isubstep == 1) then
748 vpdry(1:nbin) = vpdry_in(1:nbin)
751 ! *** should be updating vpdry(:) after first substep
758 ! determine the bin that receives the "i+j" particles
759 vpdry_ipj = vpdry(i) + vpdry(j)
762 if (vpdry_ipj > vpcut(k)) then
764 if (k >= nbin) exit ! do while ...
765 else if (vpdry_ipj < vpcut(k-1)) then
767 if (k <= 1) exit ! do while ...
772 k = max( 1, min( nbin, k ) )
773 ! write(183,'(3i4,1p,5e10.2)') i, j, k, vpdry(i), vpdry(j), vpdry_ipj
774 ! write(183,'(12x,1p,5e10.2)') vpcut(i), vpcut(j), vpcut(k-1:k)
776 tmp_beta = betaxh(i,j)
778 ! now calc the coagulation changes to number and volume
780 del_num = 0.5*tmp_beta*cnum_old(i)*cnum_old(i)
783 ! each coag event consumes 2 and produces 1 "i" particle
784 cnum(i) = cnum(i) - del_num
787 ! here i + i --> k /= i
788 ! each coag event consumes 2 "i" and produces 1 "k" particle
789 ! and there is transfer of mass" and produces 1 "k" particle
790 cnum(i) = cnum(i) - del_num*2.0
791 cnum(k) = cnum(k) + del_num
793 del_voli = tmp_beta*cnum_old(i)*cvol_old(i,l)
794 cvol(i,l) = cvol(i,l) - del_voli
795 cvol(k,l) = cvol(k,l) + del_voli
800 del_num = tmp_beta*cnum_old(i)*cnum_old(j)
801 cnum(i) = cnum(i) - del_num
803 ! here i and j are different but j = k
804 ! i loses number and transfers mass to k=j
805 ! j=k does not lose any of its own number or mass
807 del_voli = tmp_beta*cnum_old(j)*cvol_old(i,l)
808 cvol(i,l) = cvol(i,l) - del_voli
809 cvol(k,l) = cvol(k,l) + del_voli
812 ! here i, j, and k are all different
813 ! both i and j lose number and transfer mass to k
814 cnum(j) = cnum(j) - del_num
815 cnum(k) = cnum(k) + del_num
817 del_voli = tmp_beta*cnum_old(j)*cvol_old(i,l)
818 del_volj = tmp_beta*cnum_old(i)*cvol_old(j,l)
819 cvol(i,l) = cvol(i,l) - del_voli
820 cvol(j,l) = cvol(j,l) - del_volj
821 cvol(k,l) = cvol(k,l) + del_voli + del_volj
830 end do solve_isubstep_loop
833 ! transfer new concentrations from working arrays
834 num_distrib(1:nbin) = cnum(1:nbin)
836 vol_distrib(1:nbin,l) = cvol(1:nbin,l)
842 end subroutine movingcenter_singletype_coag
846 !----------------------------------------------------------------------
847 subroutine jacobson2002_singletype_coag( &
848 nbin, nbin_maxd, ncomp, ncomp_maxd, &
849 tempk, press, deltat, nsubstep, &
850 dpdry, dpwet, denswet, &
851 num_distrib, vol_distrib )
855 ! calculates aerosol coagulation for sectional representation
856 ! and a single "particle type" using the single-type algorithm of
860 ! jacobson, m. z., 2002, analysis of aerosol interactions with numerical
861 ! techniques for solving coagulation, nucleation, condensation,
862 ! dissolution, and reversible chemistry among multiple size
863 ! distributions, j. geophys. res., 107, d19, 4366
867 integer, intent(in) :: nbin ! actual number of size bins
868 integer, intent(in) :: nbin_maxd ! size-bin dimension for input (argument) arrays
869 integer, intent(in) :: ncomp ! actual number of aerosol volume components
870 integer, intent(in) :: ncomp_maxd ! volume-component dimension for input (argument) arrays
871 integer, intent(in) :: nsubstep ! number of time sub-steps for the integration
873 real(r8), intent(in) :: tempk ! air temperature (K)
874 real(r8), intent(in) :: press ! air pressure (dyne/cm2)
875 real(r8), intent(in) :: deltat ! integration time (s)
876 real(r8), intent(in) :: dpdry(nbin_maxd) ! bin dry diameter (cm)
877 real(r8), intent(in) :: dpwet(nbin_maxd) ! bin wet (ambient) diameter (cm)
878 real(r8), intent(in) :: denswet(nbin_maxd) ! bin wet (ambient) density (g/cm3)
881 real(r8), intent(inout) :: num_distrib(nbin_maxd)
882 ! num_distrib(i) = number concentration for bin i (#/cm3)
883 real(r8), intent(inout) :: vol_distrib(nbin_maxd,ncomp_maxd)
884 ! vol_distrib(i,l) = volume concentration for bin i, component l (cm3/cm3)
887 ! NOTE -- The sectional representation is based on dry particle size.
888 ! Dry sizes are used to calculate the receiving bin indices and product fractions
889 ! for each (bin-i1, bin-i2) coagulation combination.
890 ! Wet sizes and densities are used to calculate the brownian coagulation kernel.
892 ! NOTE -- Units for num_distrib and vol_distrib
893 ! num_distrib units MUST BE (#/cm3)
895 ! (cm3/cm3) units are most consistent with the num_distrib units
896 ! However, the algorithm works with almost any units! So vol_distrib can
897 ! be either masses or volumes, and either mixing ratios or concentrations
898 ! (e.g., g/cm3-air, ug/kg-air, um3/cm3-air, cm3/kg-air, ...).
899 ! Use whatever is convenient.
905 integer :: i, isubstep, j, k, kp1, l
908 ilo_of_jk(nbin,nbin), &
909 ihi_of_jk(nbin,nbin), &
910 klo_of_ij(nbin,nbin), &
913 real(r8), parameter :: pi = 3.14159265358979323846_r8
918 t1xh_num, t3xh_num, &
925 cnum(nbin), cnum_old(nbin), &
926 fijk_ieqk(nbin,nbin), &
927 fijk_keqklo(nbin,nbin), &
928 fijk_keqkhi(nbin,nbin), &
929 t1xh_vol(ncomp), t3xh_vol(ncomp), &
934 ! calculate brownian coagulation kernel
936 call brownian_kernel( &
937 nbin, tempk, press, dpwet, denswet, betaxh )
940 ! ' dpdry_i (um) dpdry_j (um) coag coef (cm3/#/s)'
941 deltat_sub = deltat/nsubstep ! time substep (s)
944 ! write(92,'(1p,3e16.7)') &
945 ! (1.0e4*dpwet(i)), (1.0e4*dpwet(j)), betaxh(i,j)
946 betaxh(i,j) = betaxh(i,j) * deltat_sub
952 ! calculate the fijk of eqn 8
954 ! for each (i,j) pair, there are at most two k values for which fijk > 0
955 ! rather than calculating (and using) the full (3d) fijk array,
956 ! only calculate the non-zero portions
958 ! klo_of_ij(i,j) is the lowest k for which i + j --> k
959 ! khi_of_ij(i,j) is the highest k for which i + j --> k
960 ! for most (i,j), khi_of_ij = klo_of_ij + 1
961 ! fijk_keqklo(i,j) = fijk with k=klo_of_ij(i,j)
962 ! fijk_keqkhi(i,j) = fijk with k=khi_of_ij(i,j)
963 ! fijk_ieqk(i,j) = fijk with k=i
966 fijk_keqklo(:,:) = 0.0
967 fijk_keqkhi(:,:) = 0.0
972 voldry(i) = (pi/6.0)*(dpdry(i)**3)
977 voldry_ipj = voldry(i) + voldry(j)
978 if (voldry_ipj >= voldry(nbin)) then
979 if (i == nbin) fijk_ieqk(i,j) = 1.0
980 klo_of_ij(i,j) = nbin
981 fijk_keqklo(i,j) = 1.0
985 if ((voldry_ipj >= voldry(k)) .and. (voldry_ipj < voldry(kp1))) then
987 fijk_tmp = ((voldry(kp1) - voldry_ipj)/(voldry(kp1) - voldry(k))) &
988 * voldry(k) / voldry_ipj
989 if (i == k) fijk_ieqk(i,j) = fijk_tmp
991 fijk_keqklo(i,j) = fijk_tmp
993 fijk_keqkhi(i,j) = 1.0 - fijk_tmp
1000 ! for each j and k, determine the lowest and highest i
1001 ! for which i + j --> k is possible
1004 ilo_of_jk(j,k) = nbin+1
1007 if ((klo_of_ij(i,j) == k) .or. (khi_of_ij(i,j) == k)) then
1008 ilo_of_jk(j,k) = min( ilo_of_jk(j,k), i )
1009 ihi_of_jk(j,k) = max( ihi_of_jk(j,k), i )
1016 ! initialize working arrays
1017 ! cnum(i) = current number conc (#/cm3) for bin i
1018 ! cvol(i,l) = current volume conc (cm3/cm3) for bin i, species l
1019 cnum(1:nbin) = num_distrib(1:nbin)
1021 cvol(1:nbin,l) = vol_distrib(1:nbin,l)
1025 ! solve coagulation over multiple time substeps
1027 solve_isubstep_loop: &
1028 do isubstep = 1, nsubstep
1029 ! write(91,*) 'maa =', 2
1031 cnum_old(1:nbin) = cnum(1:nbin)
1035 ! T3*h of eqns 7 and 9
1039 t3xh_num = t3xh_num + (1.0-fijk_ieqk(k,j))*betaxh(k,j)*cnum_old(j)
1042 t3xh_vol(:) = t3xh_num
1044 ! T1*h of eqn 7 (for number) and eqn 9 (for volume)
1049 do i = ilo_of_jk(j,k), ihi_of_jk(j,k)
1050 if (klo_of_ij(i,j) == k) then
1051 fijk_tmp = fijk_keqklo(i,j)
1052 else if (khi_of_ij(i,j) == k) then
1053 fijk_tmp = fijk_keqkhi(i,j)
1057 tmpa = fijk_tmp*betaxh(j,i)*cnum_old(j)
1058 t1xh_num = t1xh_num + tmpa*cnum(i)*voldry(i)
1059 ! write(91,'(a,3i4,1p,2e12.4)') &
1060 ! 'num source', i, j, k, fijk_tmp, t1xh_num
1062 t1xh_vol(l) = t1xh_vol(l) + tmpa*cvol(i,l)
1063 ! write(91,'(a,4i4,1p,2e12.4)') &
1064 ! 'vol so', l, i, j, k, fijk_tmp, t1xh_vol(l)
1068 t1xh_num = t1xh_num/voldry(k)
1071 ! new concentrations
1072 cnum(k) = (cnum(k) + t1xh_num) / (1.0 + t3xh_num)
1073 cvol(k,:) = (cvol(k,:) + t1xh_vol(:))/(1.0 + t3xh_vol(:))
1077 end do solve_isubstep_loop
1080 ! transfer new concentrations from working arrays
1081 num_distrib(1:nbin) = cnum(1:nbin)
1083 vol_distrib(1:nbin,l) = cvol(1:nbin,l)
1089 end subroutine jacobson2002_singletype_coag
1093 !-----------------------------------------------------------------------
1094 subroutine brownian_kernel( &
1095 nbin, tempk, press, dpwet, denswet, bckernel )
1097 ! this routine calculates brownian coagulation kernel
1098 ! using on eqn 16.28 of
1099 ! jacobson, m. z. (1999) fundamentals of atmospheric modeling.
1100 ! cambridge university press, new york, 656 pp.
1105 integer, intent(in) :: nbin ! actual number of size bins
1107 real(r8), intent(in) :: tempk ! air temperature (K)
1108 real(r8), intent(in) :: press ! air pressure (dyne/cm2)
1109 real(r8), intent(in) :: dpwet(nbin) ! bin wet (ambient) diameter (cm)
1110 real(r8), intent(in) :: denswet(nbin) ! bin wet (ambient) density (g/cm3)
1111 real(r8), intent(out) :: bckernel(nbin,nbin) ! brownian coag kernel (cm3/#/s)
1116 real(r8), parameter :: pi = 3.14159265358979323846_r8
1119 apfreepath, avogad, &
1122 deltasq_i, deltasq_j, &
1123 diffus_i, diffus_j, diffus_ipj, &
1124 dpwet_i, dpwet_j, dpwet_ipj, &
1125 gasfreepathx2, gasspeed, &
1127 mass_i, mass_j, mwair, &
1130 speedsq_i, speedsq_j, &
1134 ! boltz = boltzmann's constant (erg/K = g*cm2/s/K)
1135 ! avogad = avogadro's number (molecules/mol)
1136 ! mwair = molecular weight of air (g/mol)
1137 ! rgas = gas constant ((dyne/cm2)/(mol/cm3)/K)
1138 ! rhoair = air density (g/cm3)
1139 ! viscosd = air dynamic viscosity (g/cm/s)
1140 ! viscosk = air kinematic viscosity (cm2/s)
1141 ! gasspeed = air molecule mean thermal velocity (cm/s)
1142 ! gasfreepathx2 = air molecule mean free path (cm) x 2
1146 avogad = 6.02214e+23
1150 rhoair = press*mwair/(rgas*tempk)
1152 viscosd = 1.8325e-04*(416.16/(tempk+120.0)) * (tempk/296.16)**1.5
1153 viscosk = viscosd/rhoair
1154 gasspeed = sqrt(8.0*boltz*tempk*avogad/(pi*mwair))
1155 gasfreepathx2 = 4.0*viscosk/gasspeed
1157 ! coagulation kernel from eqn 16.28 of jacobson (1999) text
1159 ! diffus_i/j = particle brownian diffusion coefficient (cm2/s)
1160 ! speedsq_i/j = square of particle mean thermal velocity (cm/s)
1161 ! apfreepath = particle mean free path (cm)
1162 ! cunning = cunningham slip-flow correction factor
1163 ! deltasq_i/j = square of "delta_i" in eqn 16.29
1166 dpwet_i = dpwet(i) ! particle wet diameter (cm)
1167 mass_i = pi6*denswet(i)*(dpwet_i**3) ! particle wet mass (g)
1168 knud = gasfreepathx2/dpwet_i
1169 cunning = 1.0 + knud*(1.249 + 0.42*exp(-0.87/knud))
1170 diffus_i = boltz*tempk*cunning/(3.0*pi*dpwet_i*viscosd)
1171 speedsq_i = 8.0*boltz*tempk/(pi*mass_i)
1172 apfreepath = 8.0*diffus_i/(pi*sqrt(speedsq_i))
1173 tmp1 = (dpwet_i + apfreepath)**3 &
1174 - (dpwet_i*dpwet_i + apfreepath*apfreepath)**1.5
1175 deltasq_i = ( tmp1/(3.0*dpwet_i*apfreepath) - dpwet_i )**2
1181 dpwet_j = dpwet(j) ! particle wet diameter (cm)
1182 mass_j = pi6*denswet(j)*(dpwet_j**3) ! particle wet mass (g)
1183 knud = gasfreepathx2/dpwet_j
1184 cunning = 1.0 + knud*(1.249 + 0.42*exp(-0.87/knud))
1185 diffus_j = boltz*tempk*cunning/(3.0*pi*dpwet_j*viscosd)
1186 speedsq_j = 8.0*boltz*tempk/(pi*mass_j)
1187 apfreepath = 8.0*diffus_j/(pi*sqrt(speedsq_j))
1188 tmp1 = (dpwet_j + apfreepath)**3 &
1189 - (dpwet_j*dpwet_j + apfreepath*apfreepath)**1.5
1190 deltasq_j = ( tmp1/(3.0*dpwet_j*apfreepath) - dpwet_j )**2
1192 dpwet_ipj = dpwet_i + dpwet_j
1193 diffus_ipj = diffus_i + diffus_j
1194 tmp1 = (dpwet_ipj/(dpwet_ipj + 2.0*sqrt(deltasq_i + deltasq_j))) &
1195 + (8.0*diffus_ipj/(dpwet_ipj*sqrt(speedsq_i + speedsq_j)))
1196 bckernel(i,j) = 2.0*pi*dpwet_ipj*diffus_ipj/tmp1
1199 bckernel(i,j) = bckernel(j,i)
1206 end subroutine brownian_kernel
1210 !-----------------------------------------------------------------------
1214 end module module_mosaic_coag1d