Line data Source code
1 : module microp_aero
2 :
3 : !---------------------------------------------------------------------------------
4 : ! Purpose:
5 : ! CAM driver layer for aerosol activation processes.
6 : !
7 : ! ***N.B.*** This module is currently hardcoded to recognize only the aerosols/modes that
8 : ! affect the climate calculation. This is implemented by using list
9 : ! index 0 in all the calls to rad_constituent interfaces.
10 : !
11 : ! Author: Andrew Gettelman
12 : ! Based on code from: Hugh Morrison, Xiaohong Liu and Steve Ghan
13 : ! May 2010
14 : ! Description in: Morrison and Gettelman, 2008. J. Climate (MG2008)
15 : ! Gettelman et al., 2010 J. Geophys. Res. - Atmospheres (G2010)
16 : ! for questions contact Andrew Gettelman (andrew@ucar.edu)
17 : ! Modifications: A. Gettelman Nov 2010 - changed to support separation of
18 : ! microphysics and macrophysics and concentrate aerosol information here
19 : ! B. Eaton, Sep 2014 - Refactored to move CAM interface code into the CAM
20 : ! interface modules and preserve just the driver layer functionality here.
21 : !
22 : !---------------------------------------------------------------------------------
23 :
24 : use shr_kind_mod, only: r8=>shr_kind_r8
25 : use spmd_utils, only: masterproc
26 : use ppgrid, only: pcols, pver, pverp, begchunk, endchunk
27 : use ref_pres, only: top_lev => trop_cloud_top_lev
28 : use physconst, only: rair
29 : use constituents, only: cnst_get_ind
30 : use physics_types, only: physics_state, physics_ptend, physics_ptend_init, physics_ptend_sum, &
31 : physics_state_copy, physics_update
32 : use physics_buffer, only: physics_buffer_desc, pbuf_get_index, pbuf_old_tim_idx, pbuf_get_field, &
33 : pbuf_get_chunk
34 : use phys_control, only: phys_getopts, use_hetfrz_classnuc
35 : use rad_constituents, only: rad_cnst_get_info, rad_cnst_get_aer_mmr, rad_cnst_get_aer_props, &
36 : rad_cnst_get_mode_num
37 :
38 : use nucleate_ice_cam, only: use_preexisting_ice, nucleate_ice_cam_readnl, nucleate_ice_cam_register, &
39 : nucleate_ice_cam_init, nucleate_ice_cam_calc
40 :
41 : use ndrop, only: ndrop_init, dropmixnuc
42 : use ndrop_bam, only: ndrop_bam_init, ndrop_bam_run, ndrop_bam_ccn
43 :
44 : use hetfrz_classnuc_cam, only: hetfrz_classnuc_cam_readnl, hetfrz_classnuc_cam_register, hetfrz_classnuc_cam_init, &
45 : hetfrz_classnuc_cam_calc
46 :
47 : use cam_history, only: addfld, add_default, outfld
48 : use cam_logfile, only: iulog
49 : use cam_abortutils, only: endrun
50 :
51 : use aerosol_properties_mod, only: aerosol_properties
52 : use modal_aerosol_properties_mod, only: modal_aerosol_properties
53 : use carma_aerosol_properties_mod, only: carma_aerosol_properties
54 :
55 : use aerosol_state_mod, only: aerosol_state
56 : use modal_aerosol_state_mod, only: modal_aerosol_state
57 : use carma_aerosol_state_mod, only: carma_aerosol_state
58 :
59 : implicit none
60 : private
61 : save
62 :
63 : public :: microp_aero_init, microp_aero_run, microp_aero_readnl, microp_aero_register
64 : public :: microp_aero_final
65 : public :: aerosol_state_object
66 : public :: aerosol_properties_object
67 :
68 : ! Private module data
69 : character(len=16) :: eddy_scheme
70 : real(r8), parameter :: unset_r8 = huge(1.0_r8)
71 :
72 : ! contact freezing due to dust
73 : ! dust number mean radius (m), Zender et al JGR 2003 assuming number mode radius of 0.6 micron, sigma=2
74 : real(r8), parameter :: rn_dst1 = 0.258e-6_r8
75 : real(r8), parameter :: rn_dst2 = 0.717e-6_r8
76 : real(r8), parameter :: rn_dst3 = 1.576e-6_r8
77 : real(r8), parameter :: rn_dst4 = 3.026e-6_r8
78 :
79 : ! Namelist parameters
80 : real(r8) :: bulk_scale ! prescribed aerosol bulk sulfur scale factor
81 : real(r8) :: npccn_scale ! scaling for activated number
82 : real(r8) :: wsub_scale ! scaling for sub-grid vertical velocity (liquid)
83 : real(r8) :: wsubi_scale ! scaling for sub-grid vertical velocity (ice)
84 : real(r8) :: wsub_min ! minimum sub-grid vertical velocity (liquid) before scale factor
85 : real(r8) :: wsub_min_asf ! minimum sub-grid vertical velocity (liquid) after scale factor
86 : real(r8) :: wsubi_min ! minimum sub-grid vertical velocity (ice)
87 :
88 : ! smallest mixing ratio considered in microphysics
89 : real(r8), parameter :: qsmall = 1.e-18_r8
90 :
91 : ! minimum allowed cloud fraction
92 : real(r8), parameter :: mincld = 0.0001_r8
93 :
94 : ! indices in state%q and pbuf structures
95 : integer :: cldliq_idx = -1
96 : integer :: cldice_idx = -1
97 : integer :: numliq_idx = -1
98 : integer :: numice_idx = -1
99 : integer :: kvh_idx = -1
100 : integer :: tke_idx = -1
101 : integer :: wp2_idx = -1
102 : integer :: ast_idx = -1
103 : integer :: cldo_idx = -1
104 : integer :: dgnumwet_idx = -1
105 :
106 : ! Bulk aerosols
107 : character(len=20), allocatable :: aername(:)
108 : real(r8), allocatable :: num_to_mass_aer(:)
109 :
110 : integer :: naer_all ! number of aerosols affecting climate
111 : integer :: idxsul = -1 ! index in aerosol list for sulfate
112 : integer :: idxdst2 = -1 ! index in aerosol list for dust2
113 : integer :: idxdst3 = -1 ! index in aerosol list for dust3
114 : integer :: idxdst4 = -1 ! index in aerosol list for dust4
115 :
116 : ! carma aerosols
117 : logical :: clim_carma_aero
118 :
119 : ! modal aerosols
120 : logical :: clim_modal_aero
121 :
122 : integer :: mode_accum_idx = -1 ! index of accumulation mode
123 : integer :: mode_aitken_idx = -1 ! index of aitken mode
124 : integer :: mode_coarse_idx = -1 ! index of coarse mode
125 : integer :: mode_coarse_dst_idx = -1 ! index of coarse dust mode
126 : integer :: mode_coarse_slt_idx = -1 ! index of coarse sea salt mode
127 : integer :: coarse_dust_idx = -1 ! index of dust in coarse mode
128 : integer :: coarse_nacl_idx = -1 ! index of nacl in coarse mode
129 : integer :: coarse_so4_idx = -1 ! index of sulfate in coarse mode
130 :
131 : integer :: npccn_idx, rndst_idx, nacon_idx
132 :
133 : logical :: separate_dust = .false.
134 :
135 : type aero_state_t
136 : class(aerosol_state), pointer :: obj=>null()
137 : end type aero_state_t
138 :
139 : class(aerosol_properties), pointer :: aero_props_obj=>null()
140 : type(aero_state_t), pointer :: aero_state(:) => null()
141 :
142 : !=========================================================================================
143 : contains
144 : !=========================================================================================
145 :
146 1536 : subroutine microp_aero_register
147 : !-----------------------------------------------------------------------
148 : !
149 : ! Purpose:
150 : ! Register pbuf fields for aerosols needed by microphysics
151 : !
152 : ! Author: Cheryl Craig October 2012
153 : !
154 : !-----------------------------------------------------------------------
155 : use ppgrid, only: pcols
156 : use physics_buffer, only: pbuf_add_field, dtype_r8
157 :
158 1536 : call pbuf_add_field('NPCCN', 'physpkg',dtype_r8,(/pcols,pver/), npccn_idx)
159 :
160 1536 : call pbuf_add_field('RNDST', 'physpkg',dtype_r8,(/pcols,pver,4/), rndst_idx)
161 1536 : call pbuf_add_field('NACON', 'physpkg',dtype_r8,(/pcols,pver,4/), nacon_idx)
162 :
163 1536 : call nucleate_ice_cam_register()
164 1536 : call hetfrz_classnuc_cam_register()
165 :
166 1536 : end subroutine microp_aero_register
167 :
168 : !=========================================================================================
169 :
170 1536 : subroutine microp_aero_init(phys_state,pbuf2d)
171 :
172 : !-----------------------------------------------------------------------
173 : !
174 : ! Purpose:
175 : ! Initialize constants for aerosols needed by microphysics
176 : !
177 : ! Author: Andrew Gettelman May 2010
178 : !
179 : !-----------------------------------------------------------------------
180 :
181 : type(physics_state), pointer :: phys_state(:)
182 : type(physics_buffer_desc), pointer :: pbuf2d(:,:)
183 :
184 : ! local variables
185 : integer :: iaer, ierr
186 : integer :: m, n, nmodes, nspec
187 : integer :: nbins
188 :
189 : character(len=32) :: str32
190 : character(len=*), parameter :: routine = 'microp_aero_init'
191 : logical :: history_amwg
192 1536 : type(physics_buffer_desc), pointer :: pbuf(:)
193 : integer :: c
194 :
195 : !-----------------------------------------------------------------------
196 :
197 : ! Query the PBL eddy scheme
198 : call phys_getopts(eddy_scheme_out = eddy_scheme, &
199 1536 : history_amwg_out = history_amwg )
200 :
201 : ! Access the physical properties of the aerosols that are affecting the climate
202 : ! by using routines from the rad_constituents module.
203 :
204 : ! get indices into state and pbuf structures
205 1536 : call cnst_get_ind('CLDLIQ', cldliq_idx)
206 1536 : call cnst_get_ind('CLDICE', cldice_idx)
207 1536 : call cnst_get_ind('NUMLIQ', numliq_idx)
208 1536 : call cnst_get_ind('NUMICE', numice_idx)
209 :
210 3072 : select case(trim(eddy_scheme))
211 : case ('diag_TKE')
212 0 : tke_idx = pbuf_get_index('tke')
213 : case ('CLUBB_SGS')
214 1536 : wp2_idx = pbuf_get_index('WP2_nadv')
215 : case default
216 3072 : kvh_idx = pbuf_get_index('kvh')
217 : end select
218 :
219 : ! clim_modal_aero determines whether modal aerosols are used in the climate calculation.
220 : ! The modal aerosols can be either prognostic or prescribed.
221 1536 : call rad_cnst_get_info(0, nmodes=nmodes, nbins=nbins)
222 1536 : clim_modal_aero = (nmodes > 0)
223 1536 : clim_carma_aero = (nbins> 0)
224 :
225 1536 : ast_idx = pbuf_get_index('AST')
226 :
227 1536 : if (clim_modal_aero .or. clim_carma_aero) then
228 1536 : cldo_idx = pbuf_get_index('CLDO')
229 1536 : if (clim_modal_aero) then
230 1536 : aero_props_obj => modal_aerosol_properties()
231 0 : else if (clim_carma_aero) then
232 0 : aero_props_obj => carma_aerosol_properties()
233 : end if
234 1536 : call ndrop_init(aero_props_obj)
235 : end if
236 :
237 1536 : if (clim_modal_aero) then
238 :
239 1536 : dgnumwet_idx = pbuf_get_index('DGNUMWET')
240 :
241 12288 : allocate(aero_state(begchunk:endchunk))
242 9216 : do c = begchunk,endchunk
243 7680 : pbuf => pbuf_get_chunk(pbuf2d, c)
244 7680 : aero_state(c)%obj => modal_aerosol_state( phys_state(c), pbuf )
245 9216 : if (.not.associated(aero_state(c)%obj)) then
246 0 : call endrun('microp_aero_init: construction of modal_aerosol_state object failed')
247 : end if
248 : end do
249 :
250 : ! Init indices for specific modes/species
251 :
252 : ! mode index for specified mode types
253 9216 : do m = 1, nmodes
254 7680 : call rad_cnst_get_info(0, m, mode_type=str32)
255 16896 : select case (trim(str32))
256 : case ('accum')
257 1536 : mode_accum_idx = m
258 : case ('aitken')
259 1536 : mode_aitken_idx = m
260 : case ('coarse')
261 1536 : mode_coarse_idx = m
262 : case ('coarse_dust')
263 0 : mode_coarse_dst_idx = m
264 : case ('coarse_seasalt')
265 15360 : mode_coarse_slt_idx = m
266 : end select
267 : end do
268 :
269 : ! check if coarse dust is in separate mode
270 1536 : separate_dust = mode_coarse_dst_idx > 0
271 :
272 : ! for 3-mode
273 1536 : if ( mode_coarse_dst_idx<0 ) mode_coarse_dst_idx = mode_coarse_idx
274 1536 : if ( mode_coarse_slt_idx<0 ) mode_coarse_slt_idx = mode_coarse_idx
275 :
276 : ! Check that required mode types were found
277 : if (mode_accum_idx == -1 .or. mode_aitken_idx == -1 .or. &
278 1536 : mode_coarse_dst_idx == -1.or. mode_coarse_slt_idx == -1) then
279 0 : write(iulog,*) routine//': ERROR required mode type not found - mode idx:', &
280 0 : mode_accum_idx, mode_aitken_idx, mode_coarse_dst_idx, mode_coarse_slt_idx
281 0 : call endrun(routine//': ERROR required mode type not found')
282 : end if
283 :
284 : ! species indices for specified types
285 : ! find indices for the dust and seasalt species in the coarse mode
286 1536 : call rad_cnst_get_info(0, mode_coarse_dst_idx, nspec=nspec)
287 6144 : do n = 1, nspec
288 4608 : call rad_cnst_get_info(0, mode_coarse_dst_idx, n, spec_type=str32)
289 10752 : select case (trim(str32))
290 : case ('dust')
291 9216 : coarse_dust_idx = n
292 : end select
293 : end do
294 1536 : call rad_cnst_get_info(0, mode_coarse_slt_idx, nspec=nspec)
295 6144 : do n = 1, nspec
296 4608 : call rad_cnst_get_info(0, mode_coarse_slt_idx, n, spec_type=str32)
297 10752 : select case (trim(str32))
298 : case ('seasalt')
299 9216 : coarse_nacl_idx = n
300 : end select
301 : end do
302 1536 : if (mode_coarse_idx>0) then
303 1536 : call rad_cnst_get_info(0, mode_coarse_idx, nspec=nspec)
304 6144 : do n = 1, nspec
305 4608 : call rad_cnst_get_info(0, mode_coarse_idx, n, spec_type=str32)
306 10752 : select case (trim(str32))
307 : case ('sulfate')
308 9216 : coarse_so4_idx = n
309 : end select
310 : end do
311 : endif
312 :
313 : ! Check that required mode specie types were found
314 1536 : if ( coarse_dust_idx == -1 .or. coarse_nacl_idx == -1 ) then
315 0 : write(iulog,*) routine//': ERROR required mode-species type not found - indicies:', &
316 0 : coarse_dust_idx, coarse_nacl_idx
317 0 : call endrun(routine//': ERROR required mode-species type not found')
318 : end if
319 :
320 0 : else if (.not.clim_carma_aero) then
321 :
322 : ! Props needed for BAM number concentration calcs.
323 :
324 0 : call rad_cnst_get_info(0, naero=naer_all)
325 : allocate( &
326 0 : aername(naer_all), &
327 0 : num_to_mass_aer(naer_all) )
328 :
329 0 : do iaer = 1, naer_all
330 : call rad_cnst_get_aer_props(0, iaer, &
331 0 : aername = aername(iaer), &
332 0 : num_to_mass_aer = num_to_mass_aer(iaer) )
333 :
334 : ! Look for sulfate, dust, and soot in this list (Bulk aerosol only)
335 0 : if (trim(aername(iaer)) == 'SULFATE') idxsul = iaer
336 0 : if (trim(aername(iaer)) == 'DUST2') idxdst2 = iaer
337 0 : if (trim(aername(iaer)) == 'DUST3') idxdst3 = iaer
338 0 : if (trim(aername(iaer)) == 'DUST4') idxdst4 = iaer
339 : end do
340 :
341 0 : call ndrop_bam_init()
342 :
343 : end if
344 :
345 3072 : call addfld('LCLOUD', (/ 'lev' /), 'A', ' ', 'Liquid cloud fraction used in stratus activation', sampled_on_subcycle=.true.)
346 :
347 3072 : call addfld('WSUB', (/ 'lev' /), 'A', 'm/s', 'Diagnostic sub-grid vertical velocity', sampled_on_subcycle=.true.)
348 3072 : call addfld('WSUBI', (/ 'lev' /), 'A', 'm/s', 'Diagnostic sub-grid vertical velocity for ice', sampled_on_subcycle=.true.)
349 :
350 1536 : if (history_amwg) then
351 1536 : call add_default ('WSUB ', 1, ' ')
352 : end if
353 :
354 1536 : if (associated(aero_props_obj)) then
355 1536 : call nucleate_ice_cam_init(mincld, bulk_scale, pbuf2d, aero_props=aero_props_obj)
356 : else
357 0 : call nucleate_ice_cam_init(mincld, bulk_scale, pbuf2d)
358 : end if
359 1536 : if (use_hetfrz_classnuc) then
360 1536 : if (associated(aero_props_obj)) then
361 1536 : call hetfrz_classnuc_cam_init(mincld, aero_props_obj)
362 : else
363 0 : call endrun(routine//': cannot use hetfrz_classnuc without prognostic aerosols')
364 : endif
365 : endif
366 :
367 3072 : end subroutine microp_aero_init
368 :
369 : !=========================================================================================
370 : ! returns a pointer to an aerosol state object for a given chunk index
371 0 : function aerosol_state_object(lchnk) result(obj)
372 :
373 : integer,intent(in) :: lchnk ! local chunk index
374 : class(aerosol_state), pointer :: obj ! aerosol state object pointer for local chunk
375 :
376 0 : obj => aero_state(lchnk)%obj
377 :
378 0 : end function aerosol_state_object
379 :
380 : !=========================================================================================
381 : ! returns a pointer to an aerosol properties object
382 1536 : function aerosol_properties_object() result(obj)
383 :
384 : class(aerosol_properties), pointer :: obj ! aerosol properties object pointer
385 :
386 1536 : obj => aero_props_obj
387 :
388 1536 : end function aerosol_properties_object
389 :
390 : !=========================================================================================
391 :
392 1536 : subroutine microp_aero_final
393 :
394 : integer :: c
395 :
396 1536 : if (associated(aero_props_obj)) then
397 3072 : deallocate(aero_props_obj)
398 : end if
399 1536 : nullify(aero_props_obj)
400 :
401 1536 : if (associated(aero_state)) then
402 9216 : do c = begchunk,endchunk
403 16896 : deallocate(aero_state(c)%obj)
404 : end do
405 1536 : deallocate(aero_state)
406 : nullify(aero_state)
407 : end if
408 :
409 1536 : end subroutine microp_aero_final
410 :
411 : !=========================================================================================
412 :
413 1536 : subroutine microp_aero_readnl(nlfile)
414 :
415 : use namelist_utils, only: find_group_name
416 : use units, only: getunit, freeunit
417 : use spmd_utils, only: mpicom, mstrid=>masterprocid, mpi_real8
418 :
419 : character(len=*), intent(in) :: nlfile ! filepath for file containing namelist input
420 :
421 : ! Namelist variables
422 : real(r8) :: microp_aero_bulk_scale = unset_r8 ! prescribed aerosol bulk sulfur scale factor
423 : real(r8) :: microp_aero_npccn_scale = unset_r8 ! prescribed aerosol bulk sulfur scale factor
424 : real(r8) :: microp_aero_wsub_scale = unset_r8 ! subgrid vertical velocity (liquid) scale factor
425 : real(r8) :: microp_aero_wsubi_scale = unset_r8 ! subgrid vertical velocity (ice) scale factor
426 : real(r8) :: microp_aero_wsub_min = unset_r8 ! subgrid vertical velocity (liquid) minimum (before scale factor)
427 : real(r8) :: microp_aero_wsub_min_asf = unset_r8 ! subgrid vertical velocity (liquid) minimum (after scale factor)
428 : real(r8) :: microp_aero_wsubi_min = unset_r8 ! subgrid vertical velocity (ice) minimum
429 :
430 : ! Local variables
431 : integer :: unitn, ierr
432 : character(len=*), parameter :: subname = 'microp_aero_readnl'
433 :
434 : namelist /microp_aero_nl/ microp_aero_bulk_scale, microp_aero_npccn_scale, microp_aero_wsub_min, &
435 : microp_aero_wsubi_min, microp_aero_wsub_scale, microp_aero_wsubi_scale, microp_aero_wsub_min_asf
436 : !-----------------------------------------------------------------------------
437 :
438 1536 : if (masterproc) then
439 2 : unitn = getunit()
440 2 : open( unitn, file=trim(nlfile), status='old' )
441 2 : call find_group_name(unitn, 'microp_aero_nl', status=ierr)
442 2 : if (ierr == 0) then
443 2 : read(unitn, microp_aero_nl, iostat=ierr)
444 2 : if (ierr /= 0) then
445 0 : call endrun(subname // ':: ERROR reading namelist')
446 : end if
447 : end if
448 2 : close(unitn)
449 2 : call freeunit(unitn)
450 : end if
451 :
452 : ! Broadcast namelist variables
453 1536 : call mpi_bcast(microp_aero_bulk_scale, 1, mpi_real8, mstrid, mpicom, ierr)
454 1536 : if (ierr /= 0) call endrun(subname//": FATAL: mpi_bcast: microp_aero_bulk_scale")
455 1536 : call mpi_bcast(microp_aero_npccn_scale, 1, mpi_real8, mstrid, mpicom, ierr)
456 1536 : if (ierr /= 0) call endrun(subname//": FATAL: mpi_bcast: microp_aero_npccn_scale")
457 1536 : call mpi_bcast(microp_aero_wsub_scale, 1, mpi_real8, mstrid, mpicom, ierr)
458 1536 : if (ierr /= 0) call endrun(subname//": FATAL: mpi_bcast: microp_aero_wsub_scale")
459 1536 : call mpi_bcast(microp_aero_wsubi_scale, 1, mpi_real8, mstrid, mpicom, ierr)
460 1536 : if (ierr /= 0) call endrun(subname//": FATAL: mpi_bcast: microp_aero_wsubi_scale")
461 1536 : call mpi_bcast(microp_aero_wsub_min, 1, mpi_real8, mstrid, mpicom, ierr)
462 1536 : if (ierr /= 0) call endrun(subname//": FATAL: mpi_bcast: microp_aero_wsub_min")
463 1536 : call mpi_bcast(microp_aero_wsub_min_asf, 1, mpi_real8, mstrid, mpicom, ierr)
464 1536 : if (ierr /= 0) call endrun(subname//": FATAL: mpi_bcast: microp_aero_wsub_min_asf")
465 1536 : call mpi_bcast(microp_aero_wsubi_min, 1, mpi_real8, mstrid, mpicom, ierr)
466 1536 : if (ierr /= 0) call endrun(subname//": FATAL: mpi_bcast: microp_aero_wsubi_min")
467 :
468 : ! set local variables
469 1536 : bulk_scale = microp_aero_bulk_scale
470 1536 : npccn_scale = microp_aero_npccn_scale
471 1536 : wsub_scale = microp_aero_wsub_scale
472 1536 : wsubi_scale = microp_aero_wsubi_scale
473 1536 : wsub_min = microp_aero_wsub_min
474 1536 : wsub_min_asf = microp_aero_wsub_min_asf
475 1536 : wsubi_min = microp_aero_wsubi_min
476 :
477 1536 : if(bulk_scale == unset_r8) call endrun(subname//": FATAL: bulk_scale is not set")
478 1536 : if(npccn_scale == unset_r8) call endrun(subname//": FATAL: npccn_scale is not set")
479 1536 : if(wsub_scale == unset_r8) call endrun(subname//": FATAL: wsub_scale is not set")
480 1536 : if(wsubi_scale == unset_r8) call endrun(subname//": FATAL: wsubi_scale is not set")
481 1536 : if(wsub_min == unset_r8) call endrun(subname//": FATAL: wsub_min is not set")
482 1536 : if(wsub_min_asf == unset_r8) call endrun(subname//": FATAL: wsub_min_asf is not set")
483 1536 : if(wsubi_min == unset_r8) call endrun(subname//": FATAL: wsubi_min is not set")
484 :
485 1536 : call nucleate_ice_cam_readnl(nlfile)
486 1536 : call hetfrz_classnuc_cam_readnl(nlfile)
487 :
488 1536 : end subroutine microp_aero_readnl
489 :
490 : !=========================================================================================
491 :
492 58302720 : subroutine microp_aero_run ( &
493 : state, ptend_all, deltatin, pbuf)
494 :
495 : ! input arguments
496 : type(physics_state), intent(in) :: state
497 : type(physics_ptend), intent(out) :: ptend_all
498 : real(r8), intent(in) :: deltatin ! time step (s)
499 : type(physics_buffer_desc), pointer :: pbuf(:)
500 :
501 : ! local workspace
502 : ! all units mks unless otherwise stated
503 :
504 : integer :: i, k, m
505 : integer :: itim_old
506 :
507 241920 : type(physics_state), target :: state1 ! Local copy of state variable
508 58302720 : type(physics_ptend) :: ptend_loc
509 :
510 241920 : real(r8), pointer :: ast(:,:)
511 :
512 241920 : real(r8), pointer :: npccn(:,:) ! number of CCN (liquid activated)
513 :
514 241920 : real(r8), pointer :: rndst(:,:,:) ! radius of 4 dust bins for contact freezing
515 241920 : real(r8), pointer :: nacon(:,:,:) ! number in 4 dust bins for contact freezing
516 :
517 241920 : real(r8), pointer :: num_coarse(:,:) ! number m.r. of coarse mode
518 241920 : real(r8), pointer :: coarse_dust(:,:) ! mass m.r. of coarse dust
519 241920 : real(r8), pointer :: coarse_nacl(:,:) ! mass m.r. of coarse nacl
520 241920 : real(r8), pointer :: coarse_so4(:,:) ! mass m.r. of coarse sulfate
521 :
522 241920 : real(r8), pointer :: kvh(:,:) ! vertical eddy diff coef (m2 s-1)
523 241920 : real(r8), pointer :: tke(:,:) ! TKE from the UW PBL scheme (m2 s-2)
524 241920 : real(r8), pointer :: wp2(:,:) ! CLUBB vertical velocity variance
525 :
526 241920 : real(r8), pointer :: cldn(:,:) ! cloud fraction
527 241920 : real(r8), pointer :: cldo(:,:) ! old cloud fraction
528 :
529 241920 : real(r8), pointer :: dgnumwet(:,:,:) ! aerosol mode diameter
530 :
531 241920 : real(r8), pointer :: aer_mmr(:,:) ! aerosol mass mixing ratio
532 :
533 : real(r8) :: rho(pcols,pver) ! air density (kg m-3)
534 :
535 : real(r8) :: lcldm(pcols,pver) ! liq cloud fraction
536 :
537 : real(r8) :: lcldn(pcols,pver) ! fractional coverage of new liquid cloud
538 : real(r8) :: lcldo(pcols,pver) ! fractional coverage of old liquid cloud
539 : real(r8) :: cldliqf(pcols,pver) ! fractional of total cloud that is liquid
540 : real(r8) :: qcld ! total cloud water
541 : real(r8) :: nctend_mixnuc(pcols,pver)
542 : real(r8) :: dum, dum2 ! temporary dummy variable
543 : real(r8) :: dmc, ssmc, so4mc ! variables for modal scheme.
544 :
545 : ! bulk aerosol variables
546 241920 : real(r8), allocatable :: naer2(:,:,:) ! bulk aerosol number concentration (1/m3)
547 241920 : real(r8), allocatable :: maerosol(:,:,:) ! bulk aerosol mass conc (kg/m3)
548 :
549 : real(r8) :: wsub(pcols,pver) ! diagnosed sub-grid vertical velocity st. dev. (m/s)
550 : real(r8) :: wsubi(pcols,pver) ! diagnosed sub-grid vertical velocity ice (m/s)
551 : real(r8) :: nucboas
552 :
553 : real(r8) :: wght
554 :
555 : integer :: lchnk, ncol, astat
556 :
557 241920 : real(r8), allocatable :: factnum(:,:,:) ! activation fraction for aerosol number
558 :
559 : class(aerosol_state), pointer :: aero_state1_obj
560 :
561 : !-------------------------------------------------------------------------------
562 :
563 241920 : nullify(aero_state1_obj)
564 :
565 241920 : call physics_state_copy(state,state1)
566 :
567 241920 : lchnk = state1%lchnk
568 241920 : ncol = state1%ncol
569 :
570 241920 : itim_old = pbuf_old_tim_idx()
571 967680 : call pbuf_get_field(pbuf, ast_idx, ast, start=(/1,1,itim_old/), kount=(/pcols,pver,1/))
572 :
573 241920 : call pbuf_get_field(pbuf, npccn_idx, npccn)
574 :
575 241920 : call pbuf_get_field(pbuf, nacon_idx, nacon)
576 241920 : call pbuf_get_field(pbuf, rndst_idx, rndst)
577 :
578 241920 : call physics_ptend_init(ptend_all, state%psetcols, 'microp_aero')
579 :
580 : ! create the aerosol state object
581 241920 : if (clim_modal_aero) then
582 241920 : aero_state1_obj => modal_aerosol_state( state1, pbuf )
583 241920 : if (.not.associated(aero_state1_obj)) then
584 0 : call endrun('microp_aero_run: construction of aero_state1_obj modal_aerosol_state object failed')
585 : end if
586 0 : else if (clim_carma_aero) then
587 0 : aero_state1_obj => carma_aerosol_state( state1, pbuf )
588 0 : if (.not.associated(aero_state1_obj)) then
589 0 : call endrun('microp_aero_run: construction of aero_state1_obj carma_aerosol_state object failed')
590 : end if
591 : end if
592 :
593 241920 : if (clim_modal_aero.or.clim_carma_aero) then
594 :
595 241920 : itim_old = pbuf_old_tim_idx()
596 :
597 967680 : call pbuf_get_field(pbuf, ast_idx, cldn, start=(/1,1,itim_old/), kount=(/pcols,pver,1/) )
598 967680 : call pbuf_get_field(pbuf, cldo_idx, cldo, start=(/1,1,itim_old/), kount=(/pcols,pver,1/) )
599 : end if
600 :
601 241920 : if (clim_modal_aero) then
602 241920 : call pbuf_get_field(pbuf, dgnumwet_idx, dgnumwet)
603 : end if
604 :
605 : ! initialize output
606 119460096 : npccn(1:ncol,1:pver) = 0._r8
607 :
608 478082304 : nacon(1:ncol,1:pver,:) = 0._r8
609 :
610 : ! set default or fixed dust bins for contact freezing
611 119460096 : rndst(1:ncol,1:pver,1) = rn_dst1
612 119460096 : rndst(1:ncol,1:pver,2) = rn_dst2
613 119460096 : rndst(1:ncol,1:pver,3) = rn_dst3
614 119460096 : rndst(1:ncol,1:pver,4) = rn_dst4
615 :
616 : ! initialize time-varying parameters
617 7983360 : do k = top_lev, pver
618 119460096 : do i = 1, ncol
619 119218176 : rho(i,k) = state1%pmid(i,k)/(rair*state1%t(i,k))
620 : end do
621 : end do
622 :
623 241920 : if (clim_modal_aero) then
624 : ! mode number mixing ratios
625 241920 : call rad_cnst_get_mode_num(0, mode_coarse_dst_idx, 'a', state1, pbuf, num_coarse)
626 :
627 : ! mode specie mass m.r.
628 241920 : call rad_cnst_get_aer_mmr(0, mode_coarse_dst_idx, coarse_dust_idx, 'a', state1, pbuf, coarse_dust)
629 241920 : call rad_cnst_get_aer_mmr(0, mode_coarse_slt_idx, coarse_nacl_idx, 'a', state1, pbuf, coarse_nacl)
630 241920 : if (mode_coarse_idx>0) then
631 241920 : call rad_cnst_get_aer_mmr(0, mode_coarse_idx, coarse_so4_idx, 'a', state1, pbuf, coarse_so4)
632 : endif
633 :
634 : else
635 : ! init number/mass arrays for bulk aerosols
636 : allocate( &
637 0 : naer2(pcols,pver,naer_all), &
638 0 : maerosol(pcols,pver,naer_all))
639 :
640 0 : do m = 1, naer_all
641 0 : call rad_cnst_get_aer_mmr(0, m, state1, pbuf, aer_mmr)
642 0 : maerosol(:ncol,:,m) = aer_mmr(:ncol,:)*rho(:ncol,:)
643 :
644 0 : if (m .eq. idxsul) then
645 0 : naer2(:ncol,:,m) = maerosol(:ncol,:,m)*num_to_mass_aer(m)*bulk_scale
646 : else
647 0 : naer2(:ncol,:,m) = maerosol(:ncol,:,m)*num_to_mass_aer(m)
648 : end if
649 : end do
650 : end if
651 :
652 : !cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
653 : ! More refined computation of sub-grid vertical velocity
654 : ! Set to be zero at the surface by initialization.
655 :
656 483840 : select case (trim(eddy_scheme))
657 : case ('diag_TKE')
658 0 : call pbuf_get_field(pbuf, tke_idx, tke)
659 : case ('CLUBB_SGS')
660 241920 : itim_old = pbuf_old_tim_idx()
661 967680 : call pbuf_get_field(pbuf, wp2_idx, wp2, start=(/1,1,itim_old/),kount=(/pcols,pverp,1/))
662 241920 : allocate(tke(pcols,pverp))
663 246129408 : tke(:ncol,:) = (3._r8/2._r8)*wp2(:ncol,:)
664 :
665 : case default
666 483840 : call pbuf_get_field(pbuf, kvh_idx, kvh)
667 : end select
668 :
669 : ! Set minimum values above top_lev.
670 241920 : wsub(:ncol,:top_lev-1) = wsub_min
671 241920 : wsubi(:ncol,:top_lev-1) = wsubi_min
672 :
673 7983360 : do k = top_lev, pver
674 119460096 : do i = 1, ncol
675 :
676 222953472 : select case (trim(eddy_scheme))
677 : case ('diag_TKE', 'CLUBB_SGS')
678 111476736 : wsub(i,k) = sqrt(0.5_r8*(tke(i,k) + tke(i,k+1))*(2._r8/3._r8))
679 111476736 : wsub(i,k) = min(wsub(i,k),10._r8)
680 : case default
681 : ! get sub-grid vertical velocity from diff coef.
682 : ! following morrison et al. 2005, JAS
683 : ! assume mixing length of 30 m
684 0 : dum = (kvh(i,k) + kvh(i,k+1))/2._r8/30._r8
685 : ! use maximum sub-grid vertical vel of 10 m/s
686 0 : dum = min(dum, 10._r8)
687 : ! set wsub to value at current vertical level
688 222953472 : wsub(i,k) = dum
689 : end select
690 :
691 111476736 : wsubi(i,k) = max(wsubi_min, wsub(i,k)) * wsubi_scale
692 111476736 : if (.not. use_preexisting_ice) then
693 0 : wsubi(i,k) = min(wsubi(i,k), 0.2_r8)
694 : endif
695 :
696 119218176 : wsub(i,k) = max(wsub_min, wsub(i,k)) * wsub_scale
697 :
698 : end do
699 : end do
700 :
701 241920 : call outfld('WSUB', wsub, pcols, lchnk)
702 241920 : call outfld('WSUBI', wsubi, pcols, lchnk)
703 :
704 241920 : if (trim(eddy_scheme) == 'CLUBB_SGS') deallocate(tke)
705 :
706 : !cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
707 : !ICE Nucleation
708 :
709 241920 : if (associated(aero_props_obj).and.associated(aero_state1_obj)) then
710 241920 : call nucleate_ice_cam_calc(state1, wsubi, pbuf, deltatin, ptend_loc, aero_props_obj, aero_state1_obj)
711 : else
712 0 : call nucleate_ice_cam_calc(state1, wsubi, pbuf, deltatin, ptend_loc)
713 : end if
714 :
715 241920 : call physics_ptend_sum(ptend_loc, ptend_all, ncol)
716 241920 : call physics_update(state1, ptend_loc, deltatin)
717 :
718 : !cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
719 : ! get liquid cloud fraction, check for minimum
720 :
721 7983360 : do k = top_lev, pver
722 119460096 : do i = 1, ncol
723 119218176 : lcldm(i,k) = max(ast(i,k), mincld)
724 : end do
725 : end do
726 :
727 : !cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
728 : ! Droplet Activation
729 :
730 241920 : if (clim_modal_aero .or. clim_carma_aero) then
731 :
732 : ! for modal or carma aerosol
733 :
734 : ! partition cloud fraction into liquid water part
735 241920 : lcldn = 0._r8
736 241920 : lcldo = 0._r8
737 241920 : cldliqf = 0._r8
738 7983360 : do k = top_lev, pver
739 119460096 : do i = 1, ncol
740 111476736 : qcld = state1%q(i,k,cldliq_idx) + state1%q(i,k,cldice_idx)
741 119218176 : if (qcld > qsmall) then
742 46901867 : lcldn(i,k) = cldn(i,k)*state1%q(i,k,cldliq_idx)/qcld
743 46901867 : lcldo(i,k) = cldo(i,k)*state1%q(i,k,cldliq_idx)/qcld
744 46901867 : cldliqf(i,k) = state1%q(i,k,cldliq_idx)/qcld
745 : end if
746 : end do
747 : end do
748 :
749 241920 : call outfld('LCLOUD', lcldn, pcols, lchnk)
750 :
751 725760 : allocate(factnum(pcols,pver,aero_props_obj%nbins()),stat=astat)
752 241920 : if (astat/=0) then
753 0 : call endrun('microp_aero_run: not able to allocate factnum')
754 : endif
755 :
756 : ! If not using preexsiting ice, then only use cloudbourne aerosol for the
757 : ! liquid clouds. This is the same behavior as CAM5.
758 241920 : if (use_preexisting_ice) then
759 : call dropmixnuc( aero_props_obj, aero_state1_obj, &
760 : state1, ptend_loc, deltatin, pbuf, wsub, wsub_min_asf, &
761 241920 : cldn, cldo, cldliqf, nctend_mixnuc, factnum)
762 : else
763 0 : cldliqf = 1._r8
764 : call dropmixnuc( aero_props_obj, aero_state1_obj, &
765 : state1, ptend_loc, deltatin, pbuf, wsub, wsub_min_asf, &
766 0 : lcldn, lcldo, cldliqf, nctend_mixnuc, factnum)
767 : end if
768 :
769 119460096 : npccn(:ncol,:) = nctend_mixnuc(:ncol,:)
770 :
771 119460096 : npccn(:ncol,:) = npccn(:ncol,:) * npccn_scale
772 :
773 : else
774 :
775 : ! for bulk aerosol
776 :
777 : ! no tendencies returned from ndrop_bam_run, so just init ptend here
778 0 : call physics_ptend_init(ptend_loc, state1%psetcols, 'none')
779 :
780 0 : do k = top_lev, pver
781 0 : do i = 1, ncol
782 :
783 0 : if (naer_all > 0 .and. state1%q(i,k,cldliq_idx) >= qsmall) then
784 :
785 : ! get droplet activation rate
786 :
787 : call ndrop_bam_run( &
788 0 : wsub(i,k), state1%t(i,k), rho(i,k), naer2(i,k,:), naer_all, &
789 0 : naer_all, maerosol(i,k,:), &
790 0 : dum2)
791 0 : dum = dum2
792 : else
793 : dum = 0._r8
794 : end if
795 :
796 0 : npccn(i,k) = (dum*lcldm(i,k) - state1%q(i,k,numliq_idx))/deltatin
797 : end do
798 : end do
799 :
800 : end if
801 :
802 241920 : call physics_ptend_sum(ptend_loc, ptend_all, ncol)
803 241920 : call physics_update(state1, ptend_loc, deltatin)
804 :
805 :
806 : !cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
807 : ! Contact freezing (-40<T<-3 C) (Young, 1974) with hooks into simulated dust
808 : ! estimate rndst and nanco for 4 dust bins here to pass to MG microphysics
809 :
810 7983360 : do k = top_lev, pver
811 119460096 : do i = 1, ncol
812 :
813 119218176 : if (state1%t(i,k) < 269.15_r8) then
814 :
815 88795334 : if (clim_modal_aero) then
816 :
817 : ! For modal aerosols:
818 : ! use size '3' for dust coarse mode...
819 : ! scale by dust fraction in coarse mode
820 :
821 88795334 : dmc = coarse_dust(i,k)
822 88795334 : ssmc = coarse_nacl(i,k)
823 :
824 88795334 : if ( separate_dust ) then
825 : ! 7-mode -- has separate dust and seasalt mode types and no need for weighting
826 : wght = 1._r8
827 : else
828 88795334 : so4mc = coarse_so4(i,k)
829 : ! 3-mode -- needs weighting for dust since dust, seasalt, and sulfate are combined in the "coarse" mode type
830 88795334 : wght = dmc/(ssmc + dmc + so4mc)
831 : endif
832 :
833 88795334 : if (dmc > 0.0_r8) then
834 88795334 : nacon(i,k,3) = wght*num_coarse(i,k)*rho(i,k)
835 : else
836 0 : nacon(i,k,3) = 0._r8
837 : end if
838 :
839 : !also redefine parameters based on size...
840 :
841 88795334 : rndst(i,k,3) = 0.5_r8*dgnumwet(i,k,mode_coarse_dst_idx)
842 88795334 : if (rndst(i,k,3) <= 0._r8) then
843 4226521 : rndst(i,k,3) = rn_dst3
844 : end if
845 :
846 : else
847 :
848 : !For Bulk Aerosols: set equal to aerosol number for dust for bins 2-4 (bin 1=0)
849 :
850 0 : if (idxdst2 > 0) then
851 0 : nacon(i,k,2) = naer2(i,k,idxdst2)
852 : end if
853 0 : if (idxdst3 > 0) then
854 0 : nacon(i,k,3) = naer2(i,k,idxdst3)
855 : end if
856 0 : if (idxdst4 > 0) then
857 0 : nacon(i,k,4) = naer2(i,k,idxdst4)
858 : end if
859 : end if
860 :
861 : end if
862 : end do
863 : end do
864 :
865 : !cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
866 : !bulk aerosol ccn concentration (modal does it in ndrop, from dropmixnuc)
867 :
868 241920 : if ((.not. clim_modal_aero) .and. (.not.clim_carma_aero)) then
869 :
870 : ! ccn concentration as diagnostic
871 0 : call ndrop_bam_ccn(lchnk, ncol, maerosol, naer2)
872 :
873 0 : deallocate( &
874 : naer2, &
875 0 : maerosol)
876 :
877 : end if
878 :
879 : ! heterogeneous freezing
880 241920 : if (use_hetfrz_classnuc) then
881 :
882 241920 : call hetfrz_classnuc_cam_calc(aero_props_obj, aero_state1_obj, state1, deltatin, factnum, pbuf)
883 :
884 : end if
885 :
886 241920 : if (clim_modal_aero.or.clim_carma_aero) then
887 241920 : deallocate(factnum)
888 : end if
889 :
890 241920 : if (associated(aero_state1_obj)) then
891 : ! destroy the aerosol state object
892 483840 : deallocate(aero_state1_obj)
893 : nullify(aero_state1_obj)
894 : endif
895 :
896 725760 : end subroutine microp_aero_run
897 :
898 : !=========================================================================================
899 :
900 0 : end module microp_aero
|