Line data Source code
1 : !! This module is a coupler between the CAM model and the Community Aerosol
2 : !! and Radiation Model for Atmospheres (CARMA) microphysics model. It adds the
3 : !! capabilities of CARMA to CAM, allowing for binned microphysics studies
4 : !! within the CAM framework. This module supports the CAM physics interface, and
5 : !! uses the CARMA and CARMASTATE objects to perform the microphysical
6 : !! calculations.
7 : !!
8 : !! @author Chuck Bardeen
9 : !! @version July 2009
10 : module carma_intr
11 :
12 : use carma_precision_mod, only: f
13 : use carma_enums_mod, only: I_OPTICS_FIXED, I_OPTICS_MIXED_CORESHELL, I_OPTICS_MIXED_VOLUME, &
14 : I_OPTICS_MIXED_MAXWELL, I_OPTICS_SULFATE, I_CNSTTYPE_PROGNOSTIC, I_HYBRID, RC_OK, RC_ERROR, &
15 : I_WTPCT_H2SO4, I_PETTERS
16 : use carma_constants_mod, only : GRAV, REARTH, WTMOL_AIR, WTMOL_H2O, R_AIR, CP, RKAPPA, NWAVE, &
17 : CARMA_NAME_LEN, CARMA_SHORT_NAME_LEN, PI, CAM_FILL, RGAS, RM2CGS, RAD2DEG, CLDFRC_INCLOUD, MAXCLDAERDIAG
18 : use carma_types_mod, only : carma_type, carmastate_type
19 : use carma_flags_mod, only : carma_flag, carma_do_fixedinit, carma_model, carma_do_wetdep, carma_do_emission, &
20 : carma_do_pheat, carma_do_substep, carma_do_thermo, carma_do_cldice, carma_diags_file, &
21 : carma_do_grow, carma_ndebugpkgs, carma_conmax, carma_cstick, carma_tstick, carma_vf_const, carma_sulfnuc_method, &
22 : carma_rhcrit, carma_rad_feedback, carma_minsubsteps, carma_maxsubsteps, carma_gstickl, carma_gsticki, &
23 : carma_maxretries, carma_dt_threshold, carma_ds_threshold, carma_do_vtran, carma_do_vdiff, carma_do_pheatatm, &
24 : carma_do_partialinit, carma_do_optics, carma_do_incloud, carma_do_explised, carma_do_drydep, carma_do_detrain, &
25 : carma_do_coremasscheck, carma_do_coag, carma_do_clearsky, carma_do_cldliq, carma_do_aerosol, carma_dgc_threshold, &
26 : carma_diags_packages, carma_ndiagpkgs
27 : use carma_model_mod, only : NGAS, NBIN, NELEM, NGROUP, NMIE_WTP, NREFIDX, MIE_RH, NMIE_RH, NSOLUTE
28 : use carma_model_mod, only : mie_rh, mie_wtp, is_convtran1, CARMAMODEL_DiagnoseBulk, CARMAMODEL_DiagnoseBins, &
29 : CARMAMODEL_Detrain, CARMAMODEL_OutputDiagnostics, CARMAMODEL_CreateOpticsFile, CARMAMODEL_WetDeposition, &
30 : CARMAMODEL_EmitParticle, CARMAMODEL_InitializeParticle, CARMAMODEL_DefineModel, CARMAMODEL_InitializeModel, &
31 : CARMAMODEL_OutputBudgetDiagnostics, CARMAMODEL_OutputCloudborneDiagnostics, CARMAMODEL_CalculateCloudborneDiagnostics
32 : use carmaelement_mod, only : CARMAELEMENT_Get
33 : use carmagas_mod, only : CARMAGAS_Get
34 : use carmagroup_mod, only : CARMAGROUP_Get
35 : use carmastate_mod, only : CARMASTATE_CreateFromReference, CARMASTATE_SetGas, CARMASTATE_Step, CARMASTATE_GetBin, &
36 : CARMASTATE_GetGas, CARMASTATE_GetState, CARMASTATE_Get, CARMASTATE_Create, CARMASTATE_SetBin, CARMASTATE_Destroy
37 : use carma_mod, only : CARMA_Get, CARMA_Create, CARMA_Initialize, CARMA_Destroy
38 :
39 : use shr_kind_mod, only: r8 => shr_kind_r8
40 : use spmd_utils, only: masterproc, mpicom
41 : use ppgrid, only: pcols, pver, pverp
42 : use ref_pres, only: pref_mid, pref_edge, pref_mid_norm, psurf_ref
43 : use physics_types, only: physics_state, physics_ptend, physics_ptend_init, &
44 : set_dry_to_wet, physics_state_copy
45 : use physconst, only: cpair
46 : use constituents, only: pcnst, cnst_add, cnst_get_ind, &
47 : cnst_name, cnst_longname
48 : use cam_abortutils, only: endrun
49 : use physics_buffer, only: physics_buffer_desc, pbuf_add_field, pbuf_old_tim_idx, &
50 : pbuf_get_index, pbuf_get_field, dtype_r8, pbuf_set_field
51 : use pio, only: var_desc_t
52 : use radconstants, only: nlwbands, nswbands
53 : use wv_sat_methods, only: wv_sat_qsat_water
54 :
55 : implicit none
56 :
57 : private
58 : save
59 :
60 : ! Public interfaces
61 :
62 : ! CAM Physics Interface
63 : public carma_register ! register consituents
64 : public carma_is_active ! retrns true if this package is active (microphysics = .true.)
65 : public carma_implements_cnst ! returns true if consituent is implemented by this package
66 : public carma_init_cnst ! initialize constituent mixing ratios, if not read from initial file
67 : public carma_init ! initialize timestep independent variables
68 : public carma_final ! finalize the CARMA module
69 : public carma_timestep_init ! initialize timestep dependent variables
70 : public carma_timestep_tend ! interface to tendency computation
71 : public carma_accumulate_stats ! collect stats from all MPI tasks
72 :
73 :
74 : ! Other Microphysics
75 : public carma_emission_tend ! calculate tendency from emission source function
76 : public carma_calculate_cloudborne_diagnostics ! calculate model specific budget diagnostics for cloudborne aerosols
77 : public carma_output_cloudborne_diagnostics ! output model specific budget diagnostics for cloudborne aerosols
78 : public carma_output_budget_diagnostics ! calculate and output model specific aerosol budget terms
79 : public carma_wetdep_tend ! calculate tendency from wet deposition
80 : public :: carma_restart_init
81 : public :: carma_restart_write
82 : public :: carma_restart_read
83 :
84 : ! Microphysics info from CAM state
85 : !
86 : ! NOTE: These calls can be used in CAM when the CAM state is available, but the CARMASTATE
87 : ! is not available. These will return the instantaneous values instead of relying on
88 : ! pbuf fields that might be from the previous timestep.
89 : public carma_get_bin
90 : public carma_get_bin_cld
91 : public carma_get_dry_radius
92 : public carma_get_elem_for_group
93 : public carma_get_group_by_name
94 : public carma_get_kappa
95 : public carma_get_number
96 : public carma_get_number_cld
97 : public carma_get_total_mmr
98 : public carma_get_total_mmr_cld
99 : public carma_get_wet_radius
100 : public carma_get_bin_rmass
101 : public carma_set_bin
102 : public carma_get_sad
103 : public :: carma_get_wght_pct
104 : public :: carma_effecitive_radius
105 :
106 : ! NOTE: This is required by physpkg.F90, since the carma_intr.F90 stub in physics/cam
107 : ! does not have access to carma_constant.F90, but needs to also provide a defintion
108 : ! for MAXCLDAERDIAG. Thus the definition of this variable needs to come from
109 : ! carma_intr.F90.
110 : public :: MAXCLDAERDIAG
111 :
112 : ! Private data
113 :
114 : ! Particle Group Statistics
115 :
116 : ! Gridbox average
117 : integer, parameter :: NGPDIAGS = 13 ! Number of particle diagnostics ...
118 : integer, parameter :: GPDIAGS_ND = 1 ! Number density
119 : integer, parameter :: GPDIAGS_AD = 2 ! Surface area density
120 : integer, parameter :: GPDIAGS_MD = 3 ! Mass density
121 : integer, parameter :: GPDIAGS_RE = 4 ! Effective Radius
122 : integer, parameter :: GPDIAGS_RM = 5 ! Mitchell [2002] Effective Radius
123 : integer, parameter :: GPDIAGS_JN = 6 ! Nucleation Rate
124 : integer, parameter :: GPDIAGS_MR = 7 ! Mass Mixing Ratio
125 : integer, parameter :: GPDIAGS_EX = 8 ! Extinction
126 : integer, parameter :: GPDIAGS_OD = 9 ! Optical Depth
127 : integer, parameter :: GPDIAGS_VM = 10 ! Mass Weighted Fall Velocity
128 : integer, parameter :: GPDIAGS_PA = 11 ! Projected Area
129 : integer, parameter :: GPDIAGS_AR = 12 ! Area Ratio
130 : integer, parameter :: GPDIAGS_VR = 13 ! Volatile Mixing Ratio
131 :
132 : ! Particle Bin (Element) Statistics
133 : integer, parameter :: NBNDIAGS = 5 ! Number of bin surface diagnostics ...
134 : integer, parameter :: BNDIAGS_TP = 1 ! Delta Particle Temperature [K]
135 : integer, parameter :: BNDIAGS_WETR = 2 ! wet radius
136 : integer, parameter :: BNDIAGS_ND = 3 ! Number density
137 : integer, parameter :: BNDIAGS_RO = 4 ! particle density
138 : integer, parameter :: BNDIAGS_VR = 5 ! Volatile Mixing Ratio
139 :
140 : ! Surface
141 : integer, parameter :: NSBDIAGS = 2 ! Number of bin surface diagnostics ...
142 : integer, parameter :: SBDIAGS_DD = 1 ! Dry deposition flux [kg/m2/s]
143 : integer, parameter :: SBDIAGS_VD = 2 ! Dry deposition velocity [cm/s]
144 :
145 :
146 : ! Gas Statistics
147 : integer, parameter :: NGSDIAGS = 5 ! Number of gas diagnostics ...
148 : integer, parameter :: GSDIAGS_SI = 1 ! saturation wrt ice
149 : integer, parameter :: GSDIAGS_SL = 2 ! saturation wrt water
150 : integer, parameter :: GSDIAGS_EI = 3 ! equilibrium vp wrt ice
151 : integer, parameter :: GSDIAGS_EL = 4 ! equilibrium vp wrt water
152 : integer, parameter :: GSDIAGS_WT = 5 ! weight percent composition for aerosols
153 :
154 : ! Step Statistics
155 : integer, parameter :: NSPDIAGS = 2 ! Number of step diagnostics ...
156 : integer, parameter :: SPDIAGS_NSTEP = 1 ! number of substeps
157 : integer, parameter :: SPDIAGS_LNSTEP = 2 ! ln(number of substeps)
158 :
159 : ! Defaults not in the namelist
160 : character(len=10), parameter :: carma_mixtype = 'wet' ! mixing ratio type for CARMA constituents
161 : integer :: LUNOPRT = 6 ! lun for output
162 :
163 : ! Constituent Mappings
164 : integer :: icnst4elem(NELEM, NBIN) ! constituent index for a carma element
165 : integer :: icnst4gas(NGAS) ! constituent index for a carma gas
166 :
167 : character(len=16) :: btndname(NGROUP, NBIN) ! names of group per bin tendencies
168 : character(len=16) :: etndname(NELEM, NBIN) ! names of element tendencies
169 : character(len=16) :: gtndname(NGAS) ! names of gas tendencies
170 :
171 : ! Flags to indicate whether each constituent could have a CARMA tendency.
172 : logical :: lq_carma(pcnst)
173 :
174 : ! The CARMA object stores the configuration inforamtion about CARMA, only one is
175 : ! is needed per MPI task. In the future, this could potentially be turned into one
176 : ! per model to allow multiple models with different numbers of bins, ... to be
177 : ! run simultaneously. However, it is more complicated than that, since some of the
178 : ! globals here would need to be put into the carma or another object and some sort
179 : ! of callback mechanism is needed to call the correct model implementations for
180 : ! the model specific methods.
181 : type(carma_type), target :: carma ! the carma object
182 :
183 :
184 : ! Physics Buffer Indicies
185 : integer :: ipbuf4gas(NGAS)=-1 ! physics buffer index for a carma gas
186 : integer :: ipbuf4t=-1 ! physics buffer index for a carma temperature
187 : integer :: ipbuf4sati(NGAS)=-1 ! physics buffer index for a carma saturation over ice
188 : integer :: ipbuf4satl(NGAS)=-1 ! physics buffer index for a carma saturation over liquid
189 :
190 : ! Globals used for a reference atmosphere.
191 : real(kind=f) :: carma_t_ref(pver) = -huge(1._f) ! midpoint temperature (Pa)
192 : real(kind=f) :: carma_h2o_ref(pver) = -huge(1._f) ! h2o mmmr (kg/kg)
193 : real(kind=f) :: carma_h2so4_ref(pver) = -huge(1._f) ! h2so4 mmr (kg/kg)
194 :
195 : type(var_desc_t) :: t_ref_desc
196 : type(var_desc_t) :: h2o_ref_desc
197 : type(var_desc_t) :: h2so4_ref_desc
198 :
199 : ! Globals used for total statistics
200 : real(kind=f) :: glob_max_nsubstep = 0._f
201 : real(kind=f) :: glob_max_nretry = 0._f
202 : real(kind=f) :: glob_nstep = 0._f
203 : real(kind=f) :: glob_nsubstep = 0._f
204 : real(kind=f) :: glob_nretry = 0._f
205 :
206 : real(kind=f) :: step_max_nsubstep = 0._f
207 : real(kind=f) :: step_max_nretry = 0._f
208 : real(kind=f) :: step_nstep = 0._f
209 : real(kind=f) :: step_nsubstep = 0._f
210 : real(kind=f) :: step_nretry = 0._f
211 :
212 : contains
213 :
214 :
215 : !! Read the names of the constituents from CARMA and automatically create
216 : !! a list of names based on the constituents and the number of size
217 : !! bins. A naming convention is used to map from CARMA constiuent & bin to
218 : !! CAM constituent, with the smallest bin being <shortname>01, then next
219 : !! shortname<02>, ...
220 : !!
221 : !! A check is done to see if the CARMA gases are already present. If so,
222 : !! they gases are linked; otherwise, the gas is added to CAM. The shortname
223 : !! of the gas is used as the constituent name.
224 : !!
225 : !! NOTE: This call is part of the CAM Physics Interface
226 : !!
227 : !! @author Chuck Bardeen
228 : !! @version May-2009
229 3072 : subroutine carma_register
230 : use radconstants, only : get_sw_spectral_boundaries, get_lw_spectral_boundaries
231 : use cam_logfile, only : iulog
232 : use cam_control_mod, only : initial_run
233 : use physconst, only: gravit, p_rearth=>rearth, mwdry, mwh2o
234 : use phys_control, only: phys_getopts
235 :
236 : implicit none
237 :
238 : integer :: ielem ! element index
239 : integer :: ibin ! bin index
240 : integer :: igas ! gas index
241 : integer :: igroup ! group index
242 : integer :: rc ! CARMA return code
243 : character(len=8) :: c_name ! constituent name
244 : character(len=50) :: c_longname ! constituent long name
245 : real(r8) :: wave(NWAVE) ! wavelength band centers (cm)
246 : real(r8) :: dwave(NWAVE) ! wavelength band width (cm)
247 : logical :: do_wave_emit(NWAVE) ! do emission in band?
248 : real(r8) :: r(NBIN) ! particle radius (cm)
249 : real(r8) :: rmass(NBIN) ! particle mass (g)
250 : character(len=8) :: shortname ! short (CAM) name
251 : character(len=8) :: grp_short ! group short (CAM) name
252 : character(len=50) :: name ! long (CARMA) name
253 : real(r8) :: wtmol ! gas molecular weight
254 : integer :: cnsttype ! constituent type
255 : integer :: maxbin ! last prognostic bin
256 : logical :: ndropmixed ! tracer is vertically mixed in ndrop
257 :
258 : character(len=16) :: radiation_scheme ! CAM's radiation package.
259 :
260 : ! Initialize the return code.
261 1536 : rc = 0
262 :
263 : ! Some constants are set on the fly in CAM, so initialize them and any derived "constants" here.
264 : ! Some of them are needed in CARMA_DefineModel and CARMA_Initialize.
265 1536 : GRAV = gravit * RM2CGS
266 1536 : REARTH = p_rearth * RM2CGS
267 1536 : WTMOL_AIR = mwdry
268 1536 : WTMOL_H2O = mwh2o
269 1536 : R_AIR = RGAS / WTMOL_AIR
270 1536 : CP = cpair * 1.e7_r8 / 1000._r8
271 1536 : RKAPPA = R_AIR / CP
272 :
273 : ! Setup the lun for output.
274 1536 : LUNOPRT = iulog
275 :
276 : ! Find out which radiation scheme is active.
277 1536 : call phys_getopts(radiation_scheme_out = radiation_scheme)
278 :
279 : ! Get the wavelength centers for the CAM longwave and shortwave bands
280 : ! from the radiation code.
281 :
282 : ! Can do this 'in place'; set wave to lower boundaries of all bands,
283 : ! dwave to upper boundaries.
284 1536 : call get_lw_spectral_boundaries( wave(:nlwbands ), dwave(:nlwbands ), 'cm')
285 1536 : call get_sw_spectral_boundaries( wave( nlwbands+1:), dwave( nlwbands+1:), 'cm')
286 :
287 : ! Now make dwave the difference and wave the average
288 47616 : dwave = dwave - wave
289 47616 : wave = wave + (dwave / 2._r8)
290 :
291 : ! NOTE: RRTMG does not calculate emission in the shortwave bands and the first and
292 : ! last shortwave bands overlap the longwave bands. At least the first and last bands
293 : ! needs to be excluded and perhaps all of the shortwave bands need to be excluded or
294 : ! double counting will happen for particle emission. The details of this should
295 : ! probably be moved into the radiation code.
296 26112 : do_wave_emit(:nlwbands) = .TRUE.
297 : ! do_wave_emit(nlwbands+1:) = .FALSE.
298 23040 : do_wave_emit(nlwbands+1:) = .TRUE.
299 1536 : do_wave_emit(nlwbands+1) = .FALSE.
300 1536 : do_wave_emit(NWAVE) = .FALSE.
301 :
302 : ! Create the CARMA object that will contain all the information about the
303 : ! how CARMA is configured.
304 : call CARMA_Create(carma, NBIN, NELEM, NGROUP, NSOLUTE, NGAS, NWAVE, rc, &
305 1536 : LUNOPRT=LUNOPRT, wave=wave, dwave=dwave, do_wave_emit=do_wave_emit, NREFIDX=NREFIDX)
306 1536 : if (rc < 0) call endrun('carma_register::CARMA_Create failed.')
307 :
308 : ! Define the microphysical model.
309 1536 : call CARMAMODEL_DefineModel(carma, rc)
310 1536 : if (rc < 0) call endrun('carma_register::CARMA_DefineModel failed.')
311 :
312 1536 : if (masterproc) then
313 2 : write(LUNOPRT,*) ''
314 2 : write(LUNOPRT,*) 'CARMA general settings for ', trim(carma_model), ' model : '
315 2 : write(LUNOPRT,*) ' carma_do_aerosol = ', carma_do_aerosol
316 2 : write(LUNOPRT,*) ' carma_do_coremasscheck = ',carma_do_coremasscheck
317 2 : write(LUNOPRT,*) ' carma_do_cldice = ', carma_do_cldice
318 2 : write(LUNOPRT,*) ' carma_do_cldliq = ', carma_do_cldliq
319 2 : write(LUNOPRT,*) ' carma_do_clearsky = ', carma_do_clearsky
320 2 : write(LUNOPRT,*) ' carma_do_coag = ', carma_do_coag
321 2 : write(LUNOPRT,*) ' carma_do_detrain = ', carma_do_detrain
322 2 : write(LUNOPRT,*) ' carma_do_drydep = ', carma_do_drydep
323 2 : write(LUNOPRT,*) ' carma_do_emission = ', carma_do_emission
324 2 : write(LUNOPRT,*) ' carma_do_fixedinit = ', carma_do_fixedinit
325 2 : write(LUNOPRT,*) ' carma_do_grow = ', carma_do_grow
326 2 : write(LUNOPRT,*) ' carma_do_explised = ', carma_do_explised
327 2 : write(LUNOPRT,*) ' carma_do_incloud = ', carma_do_incloud
328 2 : write(LUNOPRT,*) ' carma_do_partialinit= ', carma_do_partialinit
329 2 : write(LUNOPRT,*) ' carma_do_pheat = ', carma_do_pheat
330 2 : write(LUNOPRT,*) ' carma_do_pheatatm = ', carma_do_pheatatm
331 2 : write(LUNOPRT,*) ' carma_do_substep = ', carma_do_substep
332 2 : write(LUNOPRT,*) ' carma_do_thermo = ', carma_do_thermo
333 2 : write(LUNOPRT,*) ' carma_do_vdiff = ', carma_do_vdiff
334 2 : write(LUNOPRT,*) ' carma_do_vtran = ', carma_do_vtran
335 2 : write(LUNOPRT,*) ' carma_do_wetdep = ', carma_do_wetdep
336 2 : write(LUNOPRT,*) ' carma_dgc_threshold = ', carma_dgc_threshold
337 2 : write(LUNOPRT,*) ' carma_ds_threshold = ', carma_ds_threshold
338 2 : write(LUNOPRT,*) ' carma_dt_threshold = ', carma_dt_threshold
339 2 : write(LUNOPRT,*) ' carma_cstick = ', carma_cstick
340 2 : write(LUNOPRT,*) ' carma_gsticki = ', carma_gsticki
341 2 : write(LUNOPRT,*) ' carma_gstickl = ', carma_gstickl
342 2 : write(LUNOPRT,*) ' carma_tstick = ', carma_tstick
343 2 : write(LUNOPRT,*) ' carma_rhcrit = ', carma_rhcrit
344 2 : write(LUNOPRT,*) ' carma_conmax = ', carma_conmax
345 2 : write(LUNOPRT,*) ' carma_minsubsteps = ', carma_minsubsteps
346 2 : write(LUNOPRT,*) ' carma_maxsubsteps = ', carma_maxsubsteps
347 2 : write(LUNOPRT,*) ' carma_maxretries = ', carma_maxretries
348 2 : write(LUNOPRT,*) ' carma_vf_const = ', carma_vf_const
349 2 : write(LUNOPRT,*) ' cldfrc_incloud = ', CLDFRC_INCLOUD
350 2 : write(LUNOPRT,*) ' carma_rad_feedback = ', carma_rad_feedback
351 2 : write(LUNOPRT,*) ' carma_sulfnuc_method = ', carma_sulfnuc_method
352 2 : write(LUNOPRT,*) ''
353 : endif
354 :
355 : ! Intialize the model based upon the namelist configuration.
356 : !
357 : ! NOTE: When used with CAM, the latents heats (of melting and evaporation)
358 : ! need to be constant for energy balance to work. This allows them to match the
359 : ! assumptions made in the CAM energy checking and microphysics code.
360 : call CARMA_Initialize(carma, &
361 : rc, &
362 : sulfnucl_method = carma_sulfnuc_method, &
363 : do_coremasscheck = carma_do_coremasscheck, &
364 : do_clearsky = carma_do_clearsky, &
365 : do_cnst_rlh = .true., &
366 : do_coag = carma_do_coag, &
367 : do_detrain = carma_do_detrain, &
368 : do_drydep = carma_do_drydep, &
369 : do_fixedinit = carma_do_fixedinit, &
370 : do_grow = carma_do_grow, &
371 : do_explised = carma_do_explised, &
372 : do_incloud = carma_do_incloud, &
373 : do_partialinit= carma_do_partialinit, &
374 : do_pheat = carma_do_pheat, &
375 : do_pheatatm = carma_do_pheatatm, &
376 : do_print_init = masterproc, &
377 : do_substep = carma_do_substep, &
378 : do_thermo = carma_do_thermo, &
379 : do_vdiff = carma_do_vdiff, &
380 : do_vtran = carma_do_vtran, &
381 : minsubsteps = carma_minsubsteps, &
382 : maxsubsteps = carma_maxsubsteps, &
383 : maxretries = carma_maxretries, &
384 : vf_const = carma_vf_const, &
385 : conmax = carma_conmax, &
386 : cstick = carma_cstick, &
387 : dt_threshold = carma_dt_threshold, &
388 : gsticki = carma_gsticki, &
389 : gstickl = carma_gstickl, &
390 1536 : tstick = carma_tstick)
391 1536 : if (rc < 0) call endrun('carma_register::CARMA_Initialize failed.')
392 :
393 1536 : ndropmixed = carma_model(:10)=='trop_strat'
394 :
395 : ! The elements and gases from CARMA need to be added as constituents in
396 : ! CAM (if they don't already exist). For the elements, each radius bin
397 : ! needs to be its own constiuent in CAM.
398 : !
399 : ! Some rules about the constituents:
400 : ! 1) The shortname must be 8 characters or less, so the CARMA short name
401 : ! is limited to 6 characters and 2 characters for the bin number.
402 : ! 2) The molecular weight is in kg/kmol.
403 : ! 3) The specific heat at constant pressure is in J/kg/K.
404 : ! 4) The consituents are added sequentially.
405 :
406 : ! Add a CAM constituents for each bin of each element.
407 12288 : do ielem = 1, NELEM
408 :
409 10752 : call CARMAELEMENT_Get(carma, ielem, rc, igroup=igroup, shortname=shortname, name=name)
410 10752 : if (rc < 0) call endrun('carma_register::CARMAELEMENT_Get failed.')
411 :
412 10752 : call CARMAGROUP_Get(carma, igroup, rc, cnsttype=cnsttype, r=r, rmass=rmass, maxbin=maxbin, shortname=grp_short)
413 10752 : if (rc < 0) call endrun('carma_register::CARMAGROUP_Get failed.')
414 :
415 : ! For prognostic groups, all of the bins need to be represented as actual CAM
416 : ! constituents. Diagnostic groups are determined from state information that
417 : ! is already present in CAM, and thus their bins only exist in CARMA.
418 248832 : do ibin = 1, NBIN
419 215040 : write(btndname(igroup, ibin), '(A, I2.2)') trim(grp_short), ibin
420 :
421 225792 : if (cnsttype == I_CNSTTYPE_PROGNOSTIC) then
422 : ! Bins past maxbin are treated as diagnostic even if the group
423 : ! is prognostic and thus are not advected in the paerent model.
424 215040 : if (ibin <= maxbin) then
425 :
426 215040 : write(c_name, '(A, I2.2)') trim(shortname), ibin
427 215040 : write(c_longname, '(A, e11.4, A)') trim(name) // ', ', r(ibin)*1.e4_r8, ' um'
428 :
429 : ! The molecular weight seems to be used for molecular diffusion, which
430 : ! doesn't make sense for particles. The CAM solvers are unstable if the
431 : ! mass provided is large.
432 430080 : call cnst_add(c_name, WTMOL_AIR, cpair, 0._r8, icnst4elem(ielem, ibin), &
433 215040 : longname=c_longname, mixtype=carma_mixtype, is_convtran1=is_convtran1(igroup), &
434 860160 : ndropmixed=ndropmixed )
435 : end if
436 : end if
437 : end do
438 : end do
439 :
440 : ! Find the constituent for the gas or add it if not found.
441 4608 : do igas = 1, NGAS
442 :
443 3072 : call CARMAGAS_Get(carma, igas, rc, shortname=shortname, name=name, wtmol=wtmol)
444 3072 : if (rc < 0) call endrun('carma_register::CARMAGAS_Get failed.')
445 :
446 : ! Is the gas already defined?
447 3072 : call cnst_get_ind(shortname, icnst4gas(igas))
448 :
449 : ! For substepping, we need to store the last mmr values for the gas.
450 3072 : call pbuf_add_field('CG' // shortname, 'global',dtype_r8, (/pcols, pver/), ipbuf4gas(igas))
451 :
452 : ! For substepping, we need to store the last supersaturations.
453 3072 : call pbuf_add_field('CI' // shortname, 'global',dtype_r8, (/pcols, pver/), ipbuf4sati(igas))
454 10752 : call pbuf_add_field('CL' // shortname, 'global',dtype_r8, (/pcols, pver/), ipbuf4satl(igas))
455 : end do
456 :
457 :
458 : ! For substepping, we need to store the temperature.
459 1536 : call pbuf_add_field('CT', 'global',dtype_r8, (/pcols, pver/), ipbuf4t)
460 :
461 :
462 : ! Create the optical properties files needed for RRTMG radiative transfer
463 : ! calculations.
464 : !
465 : ! NOTE: This only needs to be done once at the start of the run and does not need
466 : ! to be done for restarts.
467 : !
468 : ! NOTE: We only want to do this with RRTMG. If CAM_RT is being used, then skip this.
469 1536 : if ((masterproc) .and. (initial_run) .and. (radiation_scheme == "rrtmg") .and. (carma_do_optics)) then
470 0 : call CARMA_CreateOpticsFile(carma, rc)
471 0 : if (rc < 0) call endrun('carma_register::carma_CreateOpticsFiles failed.')
472 : end if
473 :
474 1536 : return
475 : end subroutine carma_register
476 :
477 :
478 : !! Returns true if the CARMA package is active
479 : !!
480 : !! NOTE: This call is part of the CAM Physics Interface
481 : !!
482 : !! @author Chuck Bardeen
483 : !! @version May 2009
484 0 : function carma_is_active()
485 : implicit none
486 :
487 : logical :: carma_is_active
488 :
489 0 : carma_is_active = carma_flag
490 :
491 : return
492 : end function carma_is_active
493 :
494 :
495 : !! Returns true if specified constituent is implemented by CARMA
496 : !!
497 : !! NOTE: This call is part of the CAM Physics Interface
498 : !!
499 : !! @author Chuck Bardeen
500 : !! @version May 2009
501 92160 : function carma_implements_cnst(name)
502 : implicit none
503 :
504 : character(len=*), intent(in) :: name !! constituent name
505 : logical :: carma_implements_cnst ! return value
506 :
507 : integer :: igroup
508 : integer :: ielem
509 : integer :: ibin
510 : integer :: igas
511 : integer :: rc
512 :
513 : integer :: cnsttype ! constituent type
514 : integer :: maxbin ! last prognostic bin
515 :
516 92160 : rc = 0
517 :
518 92160 : carma_implements_cnst = .false.
519 :
520 : ! Check each bin to see if it this constituent.
521 138240 : do ielem = 1, NELEM
522 1935360 : do ibin = 1, NBIN
523 1889280 : call CARMAELEMENT_Get(carma, ielem, rc, igroup=igroup)
524 1889280 : if (rc < 0) call endrun('carma_implements_cnst::CARMAELEMENT_Get failed.')
525 :
526 1889280 : call CARMAGROUP_Get(carma, igroup, rc, cnsttype=cnsttype, maxbin=maxbin)
527 1889280 : if (rc < 0) call endrun('carma_implements_cnst::CARMAGROUP_Get failed.')
528 :
529 5713920 : if (cnsttype == I_CNSTTYPE_PROGNOSTIC) then
530 :
531 : ! Bins past maxbin are treated as diagnostic even if the group
532 : ! is prognostic and thus are not advected in the parent model.
533 1889280 : if (ibin <= maxbin) then
534 :
535 1889280 : if (name == cnst_name(icnst4elem(ielem, ibin))) then
536 92160 : carma_implements_cnst = .true.
537 : return
538 : end if
539 : end if
540 : end if
541 : end do
542 : end do
543 :
544 : ! Check each gas to see if it this constituent.
545 0 : do igas = 1, NGAS
546 0 : if (name == cnst_name(icnst4gas(igas))) then
547 92160 : carma_implements_cnst = .true.
548 : return
549 : end if
550 : end do
551 :
552 : return
553 : end function carma_implements_cnst
554 :
555 :
556 : !! Initialize items in CARMA that only need to be initialized once. This
557 : !! routine is called after carma_register has been called.
558 : !!
559 : !! NOTE: This call is part of the CAM Physics Interface
560 : !!
561 : !! @author Chuck Bardeen
562 : !! @version May 2009
563 1536 : subroutine carma_init(pbuf2d)
564 : use cam_history, only: addfld, add_default, horiz_only
565 : use wrap_nf
566 : use time_manager, only: is_first_step
567 : use phys_control, only: phys_getopts
568 :
569 : type(physics_buffer_desc), pointer :: pbuf2d(:,:)
570 :
571 : integer :: ielem ! element index
572 : integer :: ibin ! bin index
573 : integer :: igas ! gas index
574 : integer :: igroup ! group index
575 : integer :: icnst ! constituent index
576 : integer :: rc ! CARMA return code
577 : character(len=8) :: sname ! short (CAM) name
578 : integer :: cnsttype ! constituent type
579 : integer :: maxbin ! last prognostic bin
580 : logical :: is_cloud ! is the group a cloud?
581 : logical :: do_drydep ! is dry deposition enabled?
582 : integer :: ncore ! number of core elements in the group
583 :
584 : logical :: history_carma
585 : logical :: history_carma_srf_flx
586 : integer :: astat
587 :
588 : 1 format(a6,4x,a11,4x,a11,4x,a11)
589 : 2 format(i6,4x,3(1pe11.3,4x))
590 :
591 : ! Initialize the return code.
592 1536 : rc = 0
593 :
594 1536 : call phys_getopts(history_carma_out=history_carma, history_carma_srf_flx_out=history_carma_srf_flx)
595 :
596 : ! Set names of constituent sources and declare them as history variables; howver,
597 : ! only prognostic variables have.
598 1536 : lq_carma(:) = .false.
599 :
600 12288 : do ielem = 1, NELEM
601 227328 : do ibin = 1, NBIN
602 215040 : call CARMAELEMENT_Get(carma, ielem, rc, igroup=igroup)
603 215040 : if (rc < 0) call endrun('carma_init::CARMAELEMENT_Get failed.')
604 :
605 215040 : call CARMAGROUP_Get(carma, igroup, rc, cnsttype=cnsttype, maxbin=maxbin, do_drydep=do_drydep)
606 215040 : if (rc < 0) call endrun('carma_init::CARMAGROUP_Get failed.')
607 :
608 655872 : if (cnsttype == I_CNSTTYPE_PROGNOSTIC) then
609 :
610 : ! Bins past maxbin are treated as diagnostic even if the group
611 : ! is prognostic and thus are not advected in the parent model.
612 215040 : if (ibin <= maxbin) then
613 :
614 215040 : icnst = icnst4elem(ielem, ibin)
615 :
616 : ! Indicate that this is a constituent whose tendency could be changed by
617 : ! CARMA.
618 215040 : lq_carma(icnst) = .true.
619 :
620 215040 : etndname(ielem, ibin) = trim(cnst_name(icnst))
621 :
622 430080 : call addfld(cnst_name(icnst), (/ 'lev' /), 'A', 'kg/kg', cnst_longname(icnst))
623 215040 : if (history_carma) then
624 215040 : call add_default(cnst_name(icnst), 1, ' ')
625 : end if
626 :
627 215040 : call addfld(trim(etndname(ielem, ibin))//'TC', (/ 'lev' /), 'A', 'kg/kg/s', &
628 645120 : trim(cnst_name(icnst)) // ' tendency')
629 215040 : call addfld(trim(etndname(ielem, ibin))//'SF', horiz_only, 'A', 'kg/m2/s', &
630 430080 : trim(cnst_name(icnst)) // ' surface emission')
631 215040 : call addfld(trim(etndname(ielem, ibin))//'EM', (/ 'lev' /), 'A', 'kg/kg/s', &
632 645120 : trim(cnst_name(icnst)) // ' emission')
633 215040 : call addfld(trim(etndname(ielem, ibin))//'WD', (/ 'lev' /), 'A', 'kg/kg/s', &
634 645120 : trim(cnst_name(icnst)) // ' wet deposition')
635 215040 : call addfld(trim(etndname(ielem, ibin))//'SW', horiz_only, 'A', 'kg/m2/s', &
636 430080 : trim(cnst_name(icnst)) // ' wet deposition flux at surface')
637 :
638 215040 : if (history_carma_srf_flx) then
639 215040 : call add_default(trim(etndname(ielem, ibin))//'EM', 1, ' ')
640 215040 : call add_default(trim(etndname(ielem, ibin))//'SF', 1, ' ')
641 215040 : call add_default(trim(etndname(ielem, ibin))//'SW', 1, ' ')
642 : end if
643 :
644 215040 : if (do_drydep) then
645 215040 : call addfld(trim(etndname(ielem, ibin))//'DD', horiz_only, 'A', 'kg/m2/s ', &
646 430080 : trim(cnst_name(icnst)) // ' dry deposition')
647 215040 : if (history_carma_srf_flx) then
648 215040 : call add_default(trim(etndname(ielem, ibin))//'DD', 1, ' ')
649 : end if
650 : end if
651 :
652 215040 : if (carma_do_pheat) then
653 0 : call addfld(trim(etndname(ielem, ibin))//'TP', (/ 'lev' /), 'A', 'K ', &
654 0 : trim(cnst_name(icnst)) // ' delta particle temperature')
655 : end if
656 : end if
657 : end if
658 : end do
659 : end do
660 :
661 4608 : do igroup = 1, NGROUP
662 3072 : call CARMAGROUP_Get(carma, igroup, rc, shortname=sname, is_cloud=is_cloud, do_drydep=do_drydep, ncore=ncore)
663 3072 : if (rc < 0) call endrun('carma_init::CARMAGROUP_GetGroup failed.')
664 :
665 : ! Gridbox average
666 : !
667 : ! NOTE: Would like use flag_xf_fill for the reffective radius fields, but cam_history
668 : ! currently only supports fill values in the entire column.
669 6144 : call addfld(trim(sname)//'ND', (/ 'lev' /), 'A', '#/cm3', trim(sname) // ' number density')
670 6144 : call addfld(trim(sname)//'AD', (/ 'lev' /), 'A', 'um2/cm3', trim(sname) // ' surface area density')
671 6144 : call addfld(trim(sname)//'MD', (/ 'lev' /), 'A', 'g/cm3', trim(sname) // ' mass density')
672 6144 : call addfld(trim(sname)//'RE', (/ 'lev' /), 'A', 'um', trim(sname) // ' effective radius')
673 6144 : call addfld(trim(sname)//'RM', (/ 'lev' /), 'A', 'um', trim(sname) // ' Mitchell effective radius')
674 6144 : call addfld(trim(sname)//'JN', (/ 'lev' /), 'A', '#/cm3/s', trim(sname) // ' nucleation rate')
675 6144 : call addfld(trim(sname)//'MR', (/ 'lev' /), 'A', 'kg/kg', trim(sname) // ' mass mixing ratio')
676 6144 : call addfld(trim(sname)//'EX', (/ 'lev' /), 'A', 'km-1', trim(sname) // ' extinction')
677 6144 : call addfld(trim(sname)//'OD', (/ 'lev' /), 'A', ' ', trim(sname) // ' optical depth')
678 6144 : call addfld(trim(sname)//'PA', (/ 'lev' /), 'A', 'cm2', trim(sname) // ' projected area')
679 6144 : call addfld(trim(sname)//'AR', (/ 'lev' /), 'A', ' ', trim(sname) // ' area ratio')
680 6144 : call addfld(trim(sname)//'VM', (/ 'lev' /), 'A', 'm/s', trim(sname) // ' fall velocity')
681 6144 : call addfld(trim(sname)//'VR', (/ 'lev' /), 'A', 'kg/kg', trim(sname) // ' volatile mass mixing ratio')
682 :
683 3072 : if (history_carma) then
684 3072 : call add_default(trim(sname)//'ND', 1, ' ')
685 3072 : call add_default(trim(sname)//'AD', 1, ' ')
686 3072 : call add_default(trim(sname)//'MD', 1, ' ')
687 3072 : call add_default(trim(sname)//'RE', 1, ' ')
688 3072 : call add_default(trim(sname)//'RM', 1, ' ')
689 3072 : call add_default(trim(sname)//'MR', 1, ' ')
690 3072 : call add_default(trim(sname)//'EX', 1, ' ')
691 3072 : call add_default(trim(sname)//'OD', 1, ' ')
692 3072 : call add_default(trim(sname)//'PA', 1, ' ')
693 3072 : call add_default(trim(sname)//'AR', 1, ' ')
694 3072 : call add_default(trim(sname)//'VM', 1, ' ')
695 3072 : call add_default(trim(sname)//'VR', 1, ' ')
696 :
697 3072 : if (carma_do_grow) then
698 3072 : call add_default(trim(sname)//'JN', 1, ' ')
699 : end if
700 : end if
701 :
702 : ! Per bin stats ..
703 3072 : if (do_drydep) then
704 64512 : do ibin = 1, NBIN
705 122880 : call addfld(trim(btndname(igroup, ibin))//'VD', horiz_only, 'A', 'm/s', &
706 187392 : trim(btndname(igroup, ibin)) // ' dry deposition velocity')
707 : end do
708 : end if
709 :
710 69120 : do ibin = 1, NBIN
711 122880 : call addfld(trim(btndname(igroup, ibin))//'ND', (/ 'lev' /), 'A', '#/cm3', &
712 245760 : trim(btndname(igroup, ibin)) // ' number density')
713 61440 : call addfld(trim(btndname(igroup, ibin))//'WR', (/ 'lev' /), 'A', 'um', &
714 184320 : trim(btndname(igroup, ibin)) // ' wet radius')
715 61440 : call addfld(trim(btndname(igroup, ibin))//'RO', (/ 'lev' /), 'A', 'g/cm3', &
716 184320 : trim(btndname(igroup, ibin)) // ' wet particle density')
717 61440 : call addfld(trim(btndname(igroup, ibin))//'VR', (/ 'lev' /), 'A', 'um', &
718 184320 : trim(btndname(igroup, ibin)) // ' volatile mixing ratio')
719 :
720 :
721 64512 : if ((carma_ndebugpkgs > 0) .and. (ncore > 0)) then
722 0 : call addfld(trim(btndname(igroup, ibin))//'LCFM', horiz_only, 'A', 'kg/m2', trim(btndname(igroup, ibin)) // ' CARMA local mass fixer fail mass ')
723 0 : call addfld(trim(btndname(igroup, ibin))//'LCFP', horiz_only, 'A', 'probability', trim(btndname(igroup, ibin)) // ' CARMA mass local fail PDF')
724 0 : call addfld(trim(btndname(igroup, ibin))//'LCR', (/ 'lev' /), 'A', 'kg/kg', trim(btndname(igroup, ibin)) // ' CARMA local mass fix MMR')
725 0 : call addfld(trim(btndname(igroup, ibin))//'LCP', (/ 'lev' /), 'A', 'probability', trim(btndname(igroup, ibin)) // ' CARMA local fix PDF')
726 :
727 0 : if (carma_diags_file > 0) then
728 0 : call add_default(trim(btndname(igroup, ibin))//'LCFM', carma_diags_file, ' ')
729 0 : call add_default(trim(btndname(igroup, ibin))//'LCFP', carma_diags_file, ' ')
730 0 : call add_default(trim(btndname(igroup, ibin))//'LCR', carma_diags_file, ' ')
731 0 : call add_default(trim(btndname(igroup, ibin))//'LCP', carma_diags_file, ' ')
732 : end if
733 : end if
734 :
735 : end do
736 :
737 : end do
738 :
739 4608 : do igas = 1, NGAS
740 3072 : icnst = icnst4gas(igas)
741 :
742 : ! Indicate that this is a constituent whose tendency could be changed by
743 : ! CARMA.
744 3072 : lq_carma(icnst) = .true.
745 3072 : gtndname(igas) = trim(cnst_name(icnst)) // 'TC'
746 :
747 6144 : call addfld(gtndname(igas), (/ 'lev' /), 'A', 'kg/kg/s', trim(cnst_name(icnst)) // ' CARMA tendency')
748 :
749 : call addfld(trim(cnst_name(icnst))//'SI', (/ 'lev' /), 'A', 'ratio', &
750 6144 : trim(cnst_name(icnst)) // ' saturation wrt ice')
751 : call addfld(trim(cnst_name(icnst))//'SL', (/ 'lev' /), 'A', 'ratio', &
752 6144 : trim(cnst_name(icnst)) // ' saturation wrt liquid')
753 : call addfld(trim(cnst_name(icnst))//'EI', (/ 'lev' /), 'A', 'mol/mol', &
754 6144 : trim(cnst_name(icnst)) // ' equilibrium vmr wrt ice')
755 : call addfld(trim(cnst_name(icnst))//'EL', (/ 'lev' /), 'A', 'mol/mol', &
756 6144 : trim(cnst_name(icnst)) // ' equilibrium vmr wrt liquid')
757 : call addfld(trim(cnst_name(icnst))//'WT', (/ 'lev' /), 'A', '%', &
758 6144 : trim(cnst_name(icnst)) // ' weight percent aerosol composition')
759 :
760 4608 : if (history_carma) then
761 3072 : call add_default(trim(cnst_name(icnst))//'SI', 1, ' ')
762 3072 : call add_default(trim(cnst_name(icnst))//'SL', 1, ' ')
763 : end if
764 : end do
765 :
766 1536 : if (carma_do_thermo) then
767 0 : call addfld('CRTT', (/ 'lev' /), 'A', 'K/s', ' CARMA temperature tendency')
768 : end if
769 :
770 : ! Add fields for diagnostic fields, and make them defaults on the first tape.
771 1536 : if (carma_do_substep) then
772 3072 : call addfld('CRNSTEP', (/ 'lev' /), 'A', ' ', 'number of carma substeps')
773 3072 : call addfld('CRLNSTEP', (/ 'lev' /), 'A', ' ', 'ln(number of carma substeps)')
774 :
775 1536 : if (history_carma) then
776 1536 : call add_default('CRNSTEP', 1, ' ')
777 1536 : call add_default('CRLNSTEP', 1, ' ')
778 : end if
779 : end if
780 :
781 :
782 : ! Set up the reference atmosphere that can be used for fixed initialization. This is
783 : ! an approximate atmospheric used to define average fall velocities, coagulation
784 : ! kernels, and growth parameters.
785 1536 : if (carma_do_fixedinit) then
786 :
787 : ! NOTE: Reading the initial condtion file using the supplied routines must
788 : ! be done outside of masterproc, so does this in all threads before deciding
789 : ! if it will be used. The initial condition file is only opened on an initial run.
790 0 : if (is_first_step()) then
791 0 : call carma_getT(carma_t_ref)
792 0 : if (carma%f_igash2o /= 0) call carma_getH2O(carma_h2o_ref)
793 0 : if (carma%f_igash2So4 /= 0) call carma_getH2SO4(carma_h2so4_ref)
794 : end if
795 : end if
796 :
797 1536 : if (is_first_step()) then
798 : ! initialize physics buffer fields
799 2304 : do igas = 1, NGAS
800 1536 : call pbuf_set_field(pbuf2d, ipbuf4gas(igas), 0.0_r8)
801 1536 : call pbuf_set_field(pbuf2d, ipbuf4sati(igas), 0.0_r8)
802 2304 : call pbuf_set_field(pbuf2d, ipbuf4satl(igas), 0.0_r8)
803 : end do
804 768 : call pbuf_set_field(pbuf2d, ipbuf4t, 0.0_r8)
805 : endif
806 :
807 : ! Do a model specific initialization.
808 1536 : call CARMAMODEL_InitializeModel(carma, lq_carma, pbuf2d, rc)
809 1536 : if (rc < 0) call endrun('carma_init::CARMA_InitializeModel failed.')
810 :
811 1536 : return
812 1536 : end subroutine carma_init
813 :
814 :
815 : !! Finalize (cleanup allocations) in the CARMA model.
816 : !!
817 : !! NOTE: This call is part of the CAM Physics Interface
818 : !!
819 : !! @author Chuck Bardeen
820 : !! @version October 2009
821 3072 : subroutine carma_final
822 : implicit none
823 :
824 : integer :: rc ! CARMA return code
825 : integer :: LUNOPRT ! logical unit number for output
826 : logical :: do_print ! do print output?
827 :
828 : 2 format(' carma_final: overall substepping statistics',/,&
829 : ' max nsubstep=',1F9.0,/,' avg nsubstep=',1F9.2,/,&
830 : ' max nretry=',1F9.0,/,' avg nretry=',1F10.4)
831 :
832 : ! Initialize the return code.
833 1536 : rc = 0
834 :
835 : ! Output the end of run statistics for CARMA
836 1536 : if (carma_do_substep) then
837 1536 : if (masterproc) then
838 2 : call CARMA_Get(carma, rc, do_print=do_print, LUNOPRT=LUNOPRT)
839 2 : if (rc < 0) call endrun('carma_final::CARMA_Get failed.')
840 :
841 2 : if (glob_nstep > 0) then
842 4 : if (do_print) write(LUNOPRT,2) glob_max_nsubstep, &
843 2 : glob_nsubstep / glob_nstep, &
844 2 : glob_max_nretry, &
845 4 : glob_nretry / glob_nstep
846 : else
847 0 : if (do_print) write(LUNOPRT,2) glob_max_nsubstep, &
848 0 : 0._r8, &
849 0 : glob_max_nretry, &
850 0 : 0._r8
851 : end if
852 : end if
853 : end if
854 :
855 :
856 : ! Do a model specific initialization.
857 1536 : call CARMA_Destroy(carma, rc)
858 1536 : if (rc < 0) call endrun('carma_final::CARMA_Destroy failed.')
859 :
860 1536 : return
861 1536 : end subroutine carma_final
862 :
863 :
864 : !! Initialization that needs to be done prior to each timestep.
865 : !!
866 : !! NOTE: This call is part of the CAM Physics Interface
867 : !!
868 : !! @author Chuck Bardeen
869 : !! @version May-2009
870 16128 : subroutine carma_timestep_init
871 : implicit none
872 :
873 16128 : if (.not. carma_flag) return
874 :
875 : ! Reset the stats, so that they are per timestep values.
876 16128 : step_max_nsubstep = 0._f
877 16128 : step_max_nretry = 0._f
878 16128 : step_nstep = 0._f
879 16128 : step_nsubstep = 0._f
880 16128 : step_nretry = 0._f
881 :
882 16128 : return
883 : end subroutine carma_timestep_init
884 :
885 :
886 : !! Calculates the tendencies for all of the constituents handled by CARMA.
887 : !! To do this:
888 : !!
889 : !! - a CARMASTATE object is created
890 : !! - it is set to the current CAM state
891 : !! - a new state is determined by CARMA
892 : !! - the difference between these states is used to determine the tendencies
893 : !! - statistics arecollected and reported
894 : !!
895 : !! NOTE: This call is part of the CAM Physics Interface
896 : !!
897 : !! NOTE: Need to add code for getting/putting last fields into the physics
898 : !! buffer from substeping.
899 : !!
900 : !! @author Chuck Bardeen
901 : !! @version May-2009
902 15978240 : subroutine carma_timestep_tend(state, cam_in, cam_out, ptend, dt, pbuf, dlf, rliq, prec_str, snow_str, &
903 : prec_sed, snow_sed, ustar, obklen)
904 : use time_manager, only: get_nstep, is_first_step
905 : use camsrfexch, only: cam_in_t, cam_out_t
906 : use planck, only: planckIntensity
907 :
908 : implicit none
909 :
910 : type(physics_state), intent(in) :: state !! physics state variables
911 : type(cam_in_t), intent(in) :: cam_in !! surface inputs
912 : type(cam_out_t), intent(inout) :: cam_out !! cam output to surface models
913 : type(physics_ptend), intent(out) :: ptend !! constituent tendencies
914 : real(r8), intent(in) :: dt !! time step (s)
915 : type(physics_buffer_desc), pointer :: pbuf(:) !! physics buffer
916 : real(r8), intent(in), optional :: dlf(pcols,pver) !! Detraining cld H20 from convection (kg/kg/s)
917 : real(r8), intent(inout), optional :: rliq(pcols) !! vertical integral of liquid not yet in q(ixcldliq)
918 : real(r8), intent(out), optional :: prec_str(pcols) !! [Total] sfc flux of precip from stratiform (m/s)
919 : real(r8), intent(out), optional :: snow_str(pcols) !! [Total] sfc flux of snow from stratiform (m/s)
920 : real(r8), intent(out), optional :: prec_sed(pcols) !! total precip from cloud sedimentation (m/s)
921 : real(r8), intent(out), optional :: snow_sed(pcols) !! snow from cloud ice sedimentation (m/s)
922 : real(r8), intent(in), optional :: ustar(pcols) !! friction velocity (m/s)
923 : real(r8), intent(in), optional :: obklen(pcols) !! Obukhov length [ m ]
924 :
925 : ! Local variables
926 72960 : type(physics_state) :: state_loc ! local physics state using wet mmr
927 : type(carma_type), pointer :: carma_ptr ! the carma state object
928 72960 : type(carmastate_type) :: cstate ! the carma state object
929 : integer :: igroup ! group index
930 : integer :: ielem ! element index
931 : integer :: ibin ! bin index
932 : integer :: igas ! gas index
933 : integer :: icol ! column index
934 : integer :: icnst ! constituent index
935 : integer :: icnst_q ! H2O constituent index
936 : integer :: rc ! CARMA return code
937 : integer :: cnsttype ! constituent type
938 : integer :: maxbin ! last prognostic bin
939 : real(r8) :: spdiags(pcols, pver, NSPDIAGS) ! CARMA step diagnostic output
940 : real(r8) :: gsdiags(pcols, pver, NGAS, NGSDIAGS) ! CARMA gas diagnostic output
941 : real(r8) :: gpdiags(pcols, pver, NGROUP, NGPDIAGS) ! CARMA group diagnostic output
942 : real(r8) :: sbdiags(pcols, NBIN, NELEM, NSBDIAGS) ! CARMA surface bin diagnostic output
943 : real(r8) :: bndiags(pcols, pver, NBIN, NELEM, NBNDIAGS) ! CARMA bin diagnostic output
944 : real(r8) :: newstate(pver) ! next state for a physics state field
945 : real(r8) :: dz(pver) ! z width
946 : real(r8) :: satice(pver) ! saturation wrt ice
947 : real(r8) :: satliq(pver) ! saturation wrt liquid
948 : real(r8) :: eqice(pver) ! equil vp wrt ice
949 : real(r8) :: eqliq(pver) ! equil vp wrt liquid
950 : real(r8) :: wtpct(pver) ! weight percent aerosol composition
951 : real(r8) :: time ! the total elapsed time (s)
952 : real(r8) :: r(NBIN) ! particle radius (cm)
953 : real(r8) :: rmass(NBIN) ! particle mass (g)
954 : real(r8) :: rrat(NBIN) ! particle maximum radius ratio ()
955 : real(r8) :: arat(NBIN) ! particle area ration ()
956 : real(r8) :: nd(pver) ! number density (cm-3)
957 : real(r8) :: ad(pver) ! area density (um2/cm3)
958 : real(r8) :: md(pver) ! mass density (g cm-3)
959 : real(r8) :: mr(pver) ! mass mixing ratio (kg/kg)
960 : real(r8) :: re(pver) ! effective radius (um)
961 : real(r8) :: rm(pver) ! Mitchell effective radius (um)
962 : real(r8) :: ex(pver) ! extinction (km-1)
963 : real(r8) :: od(pver) ! optical depth
964 : real(r8) :: re2(pver) ! N(r)*r^2 (cm2)
965 : real(r8) :: re3(pver) ! N(r)*r^3 (cm3)
966 : real(r8) :: pa(pver) ! Projected Area (cm2)
967 : real(r8) :: ar(pver) ! Area Ratio
968 : real(r8) :: vm(pver) ! Massweighted fall velocity (cm2)
969 : real(r8) :: jn(pver) ! nucleation (cm-3)
970 : real(r8) :: totalmmr(pver) ! total particle mmr (kg/kg)
971 : real(r8) :: numberDensity(pver) ! number density (cm-3)
972 : real(r8) :: nucleationRate(pver) ! nucleation rate (cm-3 s-1)
973 : real(r8) :: extinctionCoefficient(pver) ! extinction coefficient (cm2)
974 : real(r8) :: r_wet(pver) ! wet radius (um)
975 : real(r8) :: rhop_wet(pver) ! wet particle density (g/cm3)
976 : real(r8) :: dd ! dry deposition (kg/m2)
977 : real(r8) :: vd ! dry deposition velocity (cm/s)
978 : real(r8) :: vf(pverp) ! fall velocity (cm/s)
979 : real(r8) :: dtpart(pver) ! delta particle temperature (K)
980 72960 : real(r8), pointer, dimension(:, :) :: t_ptr ! last temperature
981 72960 : real(r8), pointer, dimension(:, :) :: gc_ptr ! last gas mmr
982 72960 : real(r8), pointer, dimension(:, :) :: sati_ptr ! last saturation wrt ice
983 72960 : real(r8), pointer, dimension(:, :) :: satl_ptr ! last saturation wrt liquid
984 72960 : real(r8), pointer, dimension(:, :, :) :: su_ptr ! shortwave flux up (W/m2)
985 72960 : real(r8), pointer, dimension(:, :, :) :: sd_ptr ! shortwave flux down (W/m2)
986 72960 : real(r8), pointer, dimension(:, :, :) :: lu_ptr ! longwave flux up (W/m2)
987 72960 : real(r8), pointer, dimension(:, :, :) :: ld_ptr ! longwave flux down (W/m2)
988 72960 : real(r8), pointer, dimension(:,:) :: tnd_qsnow ! external tendency on snow mass (kg/kg/s)
989 72960 : real(r8), pointer, dimension(:,:) :: tnd_nsnow ! external tendency on snow number(#/kg/s)
990 72960 : real(r8), pointer, dimension(:,:) :: re_ice ! ice effective radius (m)
991 : integer :: iz
992 : real(r8) :: cldfrc(pver) ! cloud fraction [fraction]
993 : real(r8) :: rhcrit(pver) ! relative humidity for onset of liquid clouds [fraction]
994 : real(r8) :: lndram ! land aerodynamical resistance [s/m]
995 : real(r8) :: lndfv ! surface friction velocity from land [m/s]
996 : real(r8) :: ocnram ! ocean aerodynamical resistance [s/m]
997 : real(r8) :: ocnfv ! surface friction velocity from ocean [m/s]
998 : real(r8) :: iceram ! sea ice aerodynamical resistance [s/m]
999 : real(r8) :: icefv ! surface friction velocity from sea ice [m/s]
1000 : real(r8) :: radint(pver,NWAVE) ! radiative intensity (W/m2/sr/cm)
1001 : real(kind=f) :: dwave(NWAVE) ! the wavelengths widths (cm)
1002 : real(kind=f) :: wave(NWAVE) ! the center wavelengths (cm)
1003 :
1004 : integer :: max_nsubstep
1005 : real(kind=f) :: max_nretry
1006 : real(kind=f) :: nstep
1007 : integer :: nsubstep
1008 : real(kind=f) :: nretry
1009 : real(kind=f) :: zsubsteps(pver)
1010 : logical :: is_cloud ! is the group a cloud?
1011 : logical :: is_ice ! is the group ice?
1012 : logical :: grp_do_drydep ! is dry depostion enabled for group?
1013 : logical :: do_drydep ! is dry depostion enabled?
1014 : logical :: do_detrain ! do convective detrainment?
1015 : integer :: iwvl
1016 : real(r8), parameter :: zzocen = 0.0001_r8 ! Ocean aerodynamic roughness length [m]
1017 : real(r8), parameter :: zzsice = 0.0400_r8 ! Sea ice aerodynamic roughness length [m]
1018 :
1019 :
1020 : ! Initialize the return code.
1021 72960 : rc = 0
1022 :
1023 : ! Initialize the output tendency structure.
1024 72960 : call physics_ptend_init(ptend,state%psetcols,'CARMA', ls=carma_do_thermo, lq=lq_carma)
1025 :
1026 72960 : if (present(prec_sed)) prec_sed(:) = 0._f
1027 72960 : if (present(snow_sed)) snow_sed(:) = 0._f
1028 72960 : if (present(prec_str)) prec_str(:) = 0._f
1029 72960 : if (present(snow_str)) snow_str(:) = 0._f
1030 :
1031 72960 : if (.not. carma_flag) return
1032 :
1033 : ! Determine the current time in seconds.
1034 72960 : time = dt * get_nstep() - 1
1035 :
1036 : ! The CARMA interface assumes that mass mixing ratios are relative to a
1037 : ! wet atmosphere, so convert any dry mass mixing ratios to wet.
1038 72960 : call physics_state_copy(state, state_loc)
1039 72960 : call set_dry_to_wet(state_loc, convert_cnst_type='dry')
1040 :
1041 72960 : spdiags(:, :, :) = 0.0_r8
1042 72960 : gpdiags(:, :, :, :) = 0.0_r8
1043 72960 : gsdiags(:, :, :, :) = 0.0_r8
1044 72960 : sbdiags(:, :, :, :) = 0.0_r8
1045 72960 : bndiags(:, :, :, :, :) = 0.0_r8
1046 :
1047 : ! Find the constituent index for water vapor.
1048 72960 : call cnst_get_ind('Q', icnst_q)
1049 :
1050 : ! Get pointers into pbuf ...
1051 72960 : call pbuf_get_field(pbuf, ipbuf4t, t_ptr)
1052 :
1053 : ! If doing particle heating, then get pointers to the spectral flux data provided
1054 : ! by the radiation code in the physics buffer.
1055 : !
1056 : ! NOTE: RRTMG can now be done in a subset of all levels, rather than the full
1057 : ! model height. Any implications for this code have not been worked out.
1058 72960 : if (carma_do_pheat) then
1059 0 : call pbuf_get_field(pbuf, pbuf_get_index("SU"), su_ptr)
1060 0 : call pbuf_get_field(pbuf, pbuf_get_index("SD"), sd_ptr)
1061 0 : call pbuf_get_field(pbuf, pbuf_get_index("LU"), lu_ptr)
1062 0 : call pbuf_get_field(pbuf, pbuf_get_index("LD"), ld_ptr)
1063 : end if
1064 :
1065 : ! Cloud ice pbuf fields
1066 72960 : if (carma_do_cldice) then
1067 0 : call pbuf_get_field(pbuf, pbuf_get_index("TND_QSNOW"), tnd_qsnow)
1068 0 : call pbuf_get_field(pbuf, pbuf_get_index("TND_NSNOW"), tnd_nsnow)
1069 0 : call pbuf_get_field(pbuf, pbuf_get_index("RE_ICE"), re_ice)
1070 : end if
1071 :
1072 :
1073 : ! Create a CARMASTATE object which contains state information about one
1074 : ! column of the atmosphere.
1075 72960 : carma_ptr => carma
1076 :
1077 :
1078 : ! If initializing CARMASTATE from a reference state, do it before entering the main
1079 : ! loop.
1080 : !
1081 72960 : call CARMA_Get(carma, rc, do_drydep=do_drydep)
1082 72960 : if (rc < 0) call endrun('carma_timestep_tend::CARMA_Get failed.')
1083 :
1084 72960 : if (carma_do_fixedinit) then
1085 : call CARMASTATE_CreateFromReference(cstate, &
1086 : carma_ptr, &
1087 : time, &
1088 : dt, &
1089 : pver, &
1090 : I_HYBRID, &
1091 : 40._r8, &
1092 : 255._r8, &
1093 : pref_mid_norm, &
1094 : pref_edge/psurf_ref, &
1095 : pref_mid(:), &
1096 : pref_edge(:), &
1097 : carma_t_ref(:), &
1098 : rc, &
1099 : qh2o=carma_h2o_ref, &
1100 0 : qh2so4=carma_h2so4_ref)
1101 0 : if (rc < 0) call endrun('carma_timestep_tend::CARMASTATE_CreateFromReference failed.')
1102 : end if
1103 :
1104 :
1105 : ! Process each column.
1106 1123584 : do icol = 1, state_loc%ncol
1107 :
1108 1050624 : if (is_first_step()) then
1109 3926016 : t_ptr(icol,:) = state_loc%t(icol,:)
1110 : end if
1111 :
1112 : ! For particle heating, need to get the incoming radiative intensity from
1113 : ! the radiation code.
1114 : !
1115 : ! The radiation code can optionally provide the flux up and down per band in W/m2,
1116 : ! when the compute_spectral_flux namelist variable is provided to the radiation. This
1117 : ! data needs to be scaled to a radiative intensity by assuming it is isotrotropic.
1118 1050624 : radint(:,:) = 0._f
1119 :
1120 1050624 : if (carma_do_pheat) then
1121 0 : call CARMA_Get(carma, rc, dwave=dwave, wave=wave)
1122 0 : if (rc < 0) call endrun('carma_timestep_tend::CARMA_Get failed.')
1123 :
1124 : ! CARMA may run before the radiation code for the very first time step.
1125 : ! In that case, the lu, ld, su and sd values are NaN. NaN will crash
1126 : ! the model, so instead substitute an approximation that is roughly a
1127 : ! nighttime (su=sd=0) with a black body temperature of the grid point
1128 : ! temperature (lu=ld=B(T)).
1129 : !
1130 : ! NOTE: planckIntensity is in erg/cm2/s/sr/cm and lu is in W/m2,
1131 : ! so some conversion factors are needed.
1132 0 : if (is_first_step()) then
1133 0 : su_ptr(icol, :, :) = 0._r8
1134 0 : sd_ptr(icol, :, :) = 0._r8
1135 :
1136 0 : do iwvl = 1, nlwbands
1137 0 : do iz = 1, pver
1138 0 : lu_ptr(icol, iz, iwvl) = planckIntensity(wave(iwvl), state_loc%t(icol, iz)) / 1e7_f * 1e4_f * dwave(iwvl) * PI
1139 : end do
1140 0 : lu_ptr(icol, pverp, iwvl) = lu_ptr(icol, pver, iwvl)
1141 :
1142 0 : ld_ptr(icol, 2:pverp, iwvl) = lu_ptr(icol, 1:pver, iwvl)
1143 0 : ld_ptr(icol, 1, iwvl) = lu_ptr(icol, 2, iwvl)
1144 : end do
1145 : end if
1146 :
1147 :
1148 0 : do iwvl = 1, nlwbands
1149 0 : radint(:, iwvl) = (lu_ptr(icol, 2:, iwvl) + ld_ptr(icol, :pver, iwvl)) / 2._r8 / PI / dwave(iwvl)
1150 : end do
1151 :
1152 0 : do iwvl = 1, nswbands
1153 0 : radint(:, nlwbands+iwvl) = (su_ptr(icol, 2:, iwvl) + sd_ptr(icol, :pver, iwvl)) / 2._r8 / PI / dwave(nlwbands+iwvl)
1154 : end do
1155 : end if
1156 :
1157 : call CARMASTATE_Create(cstate, &
1158 : carma_ptr, &
1159 : time, &
1160 : dt, &
1161 : pver, &
1162 : I_HYBRID, &
1163 0 : state_loc%lat(icol) * RAD2DEG, &
1164 0 : state_loc%lon(icol) * RAD2DEG, &
1165 : pref_mid_norm, &
1166 : pref_edge/psurf_ref, &
1167 0 : state_loc%pmid(icol, :), &
1168 0 : state_loc%pint(icol, :), &
1169 0 : state_loc%t(icol, :), &
1170 : rc, &
1171 0 : qh2o=state_loc%q(icol, :, icnst_q), &
1172 : told=t_ptr(icol, :), &
1173 370870272 : radint=radint)
1174 1050624 : if (rc < 0) call endrun('carma_timestep_tend::CARMASTATE_Create failed.')
1175 :
1176 :
1177 : ! Store information about the CARMA particles.
1178 :
1179 : ! For prognostic groups, the mass of the particles for each bin is stored as
1180 : ! a unique constituent within CAM.
1181 8404992 : do ielem = 1, NELEM
1182 7354368 : call CARMAELEMENT_Get(carma, ielem, rc, igroup=igroup)
1183 7354368 : if (rc < 0) call endrun('carma_timestep_tend::CARMAELEMENT_Get failed.')
1184 :
1185 7354368 : call CARMAGROUP_Get(carma, igroup, rc, cnsttype=cnsttype, maxbin=maxbin)
1186 7354368 : if (rc < 0) call endrun('carma_timestep_tend::CARMAGROUP_Get failed.')
1187 :
1188 23113728 : if (cnsttype == I_CNSTTYPE_PROGNOSTIC) then
1189 :
1190 : ! For prognostic groups, set the bin from the corresponding constituent.
1191 154441728 : do ibin = 1, NBIN
1192 :
1193 : ! Bins past maxbin are treated as diagnostic even if the group
1194 : ! is prognostic and thus are not advected in the parent model.
1195 154441728 : if (ibin <= maxbin) then
1196 10443202560 : call CARMASTATE_SetBin(cstate, ielem, ibin, state_loc%q(icol, :, icnst4elem(ielem, ibin)), rc)
1197 147087360 : if (rc < 0) call endrun('carma_timestep_tend::CARMASTATE_SetBin failed.')
1198 : else
1199 0 : newstate(:) = 0._f
1200 :
1201 0 : call CARMASTATE_SetBin(cstate, ielem, ibin, newstate, rc)
1202 0 : if (rc < 0) call endrun('carma_timestep_tend::CARMASTATE_SetBin failed.')
1203 : end if
1204 : end do
1205 : end if
1206 : end do
1207 :
1208 : ! Store information about CARMA gases.
1209 3151872 : do igas = 1, NGAS
1210 2101248 : call pbuf_get_field(pbuf, ipbuf4gas(igas), gc_ptr)
1211 2101248 : call pbuf_get_field(pbuf, ipbuf4sati(igas), sati_ptr)
1212 2101248 : call pbuf_get_field(pbuf, ipbuf4satl(igas), satl_ptr)
1213 :
1214 : ! Handle the initial case where we don't have last values.
1215 2101248 : if (is_first_step()) then
1216 7852032 : gc_ptr(icol,:) = state_loc%q(icol, :, icnst4gas(igas))
1217 7852032 : sati_ptr(icol,:) = -1._f
1218 7852032 : satl_ptr(icol,:) = -1._f
1219 : end if
1220 :
1221 2101248 : call CARMASTATE_SetGas(cstate, igas, state_loc%q(icol, :, icnst4gas(igas)), rc, &
1222 151289856 : mmr_old=gc_ptr(icol,:), satice_old=sati_ptr(icol,:), satliq_old=satl_ptr(icol,:))
1223 9455616 : if (rc < 0) call endrun('carma_timestep_tend::CARMASTATE_SetGas failed.')
1224 : end do
1225 :
1226 :
1227 1050624 : call CARMAMODEL_DiagnoseBins(carma, cstate, state_loc, pbuf, icol, dt, rc, rliq=rliq, prec_str=prec_str, snow_str=snow_str)
1228 1050624 : if (rc < 0) call endrun('carma_timestep_tend::CARMA_DiagnoseBins failed.')
1229 :
1230 :
1231 : ! If the model supports detraining of condensed water from convection, then pass
1232 : ! along the condensed H2O.
1233 1050624 : call CARMA_Get(carma, rc, do_detrain=do_detrain)
1234 1050624 : if (rc < 0) call endrun('CARMA_Detrain::CARMA_Get failed.')
1235 :
1236 1050624 : if (do_detrain) then
1237 : call CARMAMODEL_Detrain(carma, cstate, cam_in, dlf, state_loc, icol, dt, rc, rliq=rliq, prec_str=prec_str, &
1238 0 : snow_str=snow_str, tnd_qsnow=tnd_qsnow, tnd_nsnow=tnd_nsnow)
1239 0 : if (rc < 0) call endrun('carma_timestep_tend::CARMA_Detrain failed.')
1240 : end if
1241 :
1242 :
1243 : ! Now that detrainment has happened, determine the cloud fractions.
1244 : ! These will be used to scale the cloud amount to go from gridbox average to in-cloud
1245 : ! values and back.
1246 : !
1247 : ! For the cirrus model, assume the cloud fraction is just the ice cloud fraction.
1248 1050624 : call CARMA_CloudFraction(carma, cstate, cam_in, state_loc, icol, cldfrc, rhcrit, rc)
1249 1050624 : if (rc < 0) call endrun('carma_timestep_tend::carma_CloudFraction failed.')
1250 :
1251 : ! A fixed value for rhcrit can be specified in the namelist rather than using the
1252 : ! one from the cloud fraction.
1253 1050624 : if (carma_rhcrit /= 0._f) then
1254 74594304 : rhcrit(:) = carma_rhcrit
1255 : end if
1256 :
1257 :
1258 : ! For dry deposition, provide a surface friction velocity and an aerodynamic
1259 : ! resistance for each of the land surface types. The values for the land come
1260 : ! from the land model, but those for ocean and sea ice need to be calculated.
1261 1050624 : if (do_drydep) then
1262 :
1263 : ! Land
1264 1050624 : lndfv = cam_in%fv(icol)
1265 1050624 : lndram = cam_in%ram1(icol)
1266 :
1267 : ! Ocean
1268 1050624 : ocnfv = ustar(icol)
1269 1050624 : ocnram = 0._r8
1270 1050624 : if (cam_in%ocnfrac(icol) > 0._r8) then
1271 : call CARMA_calcram(ocnfv, &
1272 : zzocen, &
1273 0 : state_loc%pdel(icol, pver), &
1274 0 : state_loc%pmid(icol, pver), &
1275 0 : state_loc%t(icol, pver), &
1276 : obklen(icol), &
1277 704075 : ocnram)
1278 : end if
1279 :
1280 : ! Sea Ice
1281 1050624 : icefv = ustar(icol)
1282 1050624 : iceram = 0._r8
1283 1050624 : if (cam_in%icefrac(icol) > 0._r8) then
1284 : call CARMA_calcram(ocnfv, &
1285 : zzocen, &
1286 0 : state_loc%pdel(icol, pver), &
1287 0 : state_loc%pmid(icol, pver), &
1288 0 : state_loc%t(icol, pver), &
1289 : obklen(icol), &
1290 148388 : iceram)
1291 : end if
1292 : end if
1293 :
1294 :
1295 : ! Advance the microphysics one timestep.
1296 : call CARMASTATE_Step(cstate, rc, cldfrc=cldfrc, rhcrit=rhcrit, &
1297 : lndfv=lndfv, ocnfv=ocnfv, icefv=icefv, lndram=lndram, &
1298 1050624 : ocnram=ocnram, iceram=iceram, lndfrac=cam_in%landfrac(icol), &
1299 1050624 : ocnfrac=cam_in%ocnfrac(icol), icefrac=cam_in%icefrac(icol))
1300 1050624 : if (rc < 0) call endrun('carma_timestep_tend::CARMA_Step failed.')
1301 :
1302 :
1303 : ! Get the results for the CARMA particles.
1304 :
1305 : ! For diagnostic groups, a special routine needs to be called to determine how
1306 : ! bins affect the bulk state, since there is not an individual constituent for
1307 : ! each bin.
1308 : !
1309 : ! NOTE: To work around an XL Fortran compiler bug, the optional arguments can only
1310 : ! be passed when defined.
1311 1050624 : if (present(rliq)) then
1312 : call CARMAMODEL_DiagnoseBulk(carma, cstate, cam_out, state_loc, pbuf, ptend, icol, dt, rc, &
1313 : rliq=rliq, prec_str=prec_str, snow_str=snow_str, prec_sed=prec_sed, &
1314 0 : snow_sed=snow_sed, tnd_qsnow=tnd_qsnow, tnd_nsnow=tnd_nsnow, re_ice=re_ice)
1315 : else
1316 1050624 : call CARMAMODEL_DiagnoseBulk(carma, cstate, cam_out, state_loc, pbuf, ptend, icol, dt, rc)
1317 : end if
1318 1050624 : if (rc < 0) call endrun('carma_timestep_tend::CARMASTATE_DiagnoseBulk failed.')
1319 :
1320 :
1321 : ! Calculate the group statistics for all elements.
1322 74594304 : dz(:) = state_loc%zi(icol, 1:pver) - state_loc%zi(icol, 2:pverp)
1323 :
1324 8404992 : do ielem = 1, NELEM
1325 :
1326 7354368 : call CARMAELEMENT_Get(carma, ielem, rc, igroup=igroup)
1327 7354368 : if (rc < 0) call endrun('carma_timestep_tend::CARMAELEMENT_Get failed.')
1328 :
1329 : call CARMAGROUP_Get(carma, igroup, rc, cnsttype=cnsttype, r=r, rmass=rmass, maxbin=maxbin, &
1330 7354368 : is_cloud=is_cloud, is_ice=is_ice, do_drydep=grp_do_drydep, rrat=rrat, arat=arat)
1331 7354368 : if (rc < 0) call endrun('carma_timestep_tend::CARMAGROUP_Get failed.')
1332 :
1333 : ! Intialize the group totals
1334 7354368 : nd(:) = 0.0_r8
1335 7354368 : ad(:) = 0.0_r8
1336 7354368 : md(:) = 0.0_r8
1337 7354368 : mr(:) = 0.0_r8
1338 7354368 : re(:) = 0.0_r8
1339 7354368 : rm(:) = 0.0_r8
1340 7354368 : ex(:) = 0.0_r8
1341 7354368 : od(:) = 0.0_r8
1342 7354368 : re2(:) = 0.0_r8
1343 7354368 : re3(:) = 0.0_r8
1344 7354368 : jn(:) = 0.0_r8
1345 7354368 : pa(:) = 0.0_r8
1346 7354368 : vm(:) = 0.0_r8
1347 7354368 : ar(:) = 0.0_r8
1348 :
1349 154441728 : do ibin = 1, NBIN
1350 : call CARMASTATE_GetBin(cstate, ielem, ibin, newstate(:), rc, &
1351 : numberDensity=numberDensity, nucleationRate=nucleationRate, r_wet=r_wet, &
1352 147087360 : rhop_wet=rhop_wet, sedimentationflux=dd, vd=vd, vf=vf, dtpart=dtpart, totalmmr=totalmmr)
1353 147087360 : if (rc < 0) call endrun('carma_timestep_tend::CARMASTATE_GetBin failed.')
1354 :
1355 : ! For prognostic groups, set the tendency from the corresponding constituents.
1356 147087360 : if (cnsttype == I_CNSTTYPE_PROGNOSTIC) then
1357 :
1358 : ! Bins past maxbin are treated as diagnostic even if the group
1359 : ! is prognostic and thus are not advected in the paerent model.
1360 147087360 : if (ibin <= maxbin) then
1361 :
1362 147087360 : icnst = icnst4elem(ielem, ibin)
1363 :
1364 : ! Update the consituent tendency.
1365 10443202560 : ptend%q(icol, :, icnst) = (newstate(:) - state_loc%q(icol, :, icnst)) / dt
1366 :
1367 147087360 : if (grp_do_drydep) then
1368 147087360 : sbdiags(icol, ibin, ielem, SBDIAGS_DD) = dd
1369 147087360 : sbdiags(icol, ibin, ielem, SBDIAGS_VD) = - vd / 100._r8
1370 : end if
1371 : end if
1372 : end if
1373 :
1374 : ! Calculate the total densities.
1375 : !
1376 : ! NOTE: Convert AD to um2/cm3.
1377 147087360 : if (numberDensity(1) /= CAM_FILL) then
1378 2983772160 : nd(:) = nd(:) + numberDensity(:)
1379 2983772160 : re2(:) = re2(:) + numberDensity(:) * ((r(ibin)*rrat(ibin))**2)
1380 2983772160 : re3(:) = re3(:) + numberDensity(:) * ((r(ibin)*rrat(ibin))**3)
1381 2983772160 : ad(:) = ad(:) + numberDensity(:) * 4.0_r8 * PI * (r(ibin)**2) * 1.0e8_r8
1382 2983772160 : md(:) = md(:) + numberDensity(:) * rmass(ibin)
1383 2983772160 : mr(:) = mr(:) + totalmmr(:)
1384 2983772160 : pa(:) = pa(:) + numberDensity(:) * PI * ((r(ibin) * rrat(ibin))**2) * arat(ibin)
1385 2983772160 : vm(:) = vm(:) + numberDensity(:) * rmass(ibin) * vf(2:) / 100._f
1386 :
1387 : ! Calculate the optical depth and extinction.
1388 : !
1389 : ! NOTE: Assume Qext = 2 for optical depth. This can be pulled out of CARMA
1390 : ! mie claculations later.
1391 : !
1392 : ! Convert extinction coefficient to km-1.
1393 2983772160 : extinctionCoefficient(:) = 2.0_r8 * PI * (r(ibin)**2)
1394 2983772160 : ex(:) = ex(:) + numberDensity(:) * extinctionCoefficient(:) * 1e5_r8
1395 2983772160 : od(:) = od(:) + numberDensity(:) * extinctionCoefficient(:) * dz(:) * 100._r8
1396 : end if
1397 :
1398 10443202560 : bndiags(icol,:,ibin,ielem,BNDIAGS_VR) = bndiags(icol,:,ibin,ielem,BNDIAGS_VR) + totalmmr(:)
1399 10443202560 : gpdiags(icol, :, igroup, GPDIAGS_VR) = gpdiags(icol, :, igroup, GPDIAGS_VR) + totalmmr(:)
1400 :
1401 : ! Particle temperatures from particle heating.
1402 147087360 : if (carma_do_pheat) then
1403 0 : bndiags(icol, :, ibin, ielem, BNDIAGS_TP) = dtpart(:)
1404 : end if
1405 :
1406 147087360 : if (nucleationRate(1) /= CAM_FILL) then
1407 2983772160 : jn(:) = jn(:) + nucleationRate(:)
1408 : end if
1409 :
1410 : ! Output nd and wet radius for each bin.
1411 10443202560 : r_wet = r_wet * 1e4_r8 ! cm to um
1412 10443202560 : bndiags(icol,:,ibin,ielem,BNDIAGS_WETR) = r_wet(:)
1413 10443202560 : bndiags(icol,:,ibin,ielem,BNDIAGS_ND) = numberDensity(:)
1414 10597644288 : bndiags(icol,:,ibin,ielem,BNDIAGS_RO) = rhop_wet(:)
1415 : end do
1416 :
1417 : ! If this is the number element for the group, then write out the
1418 : ! statistics.
1419 23113728 : if (numberDensity(1) /= CAM_FILL) then
1420 :
1421 : ! Calculate the effective radius (total volume / total area). Places
1422 : ! with no surface area will cause NaN values.
1423 : !
1424 : ! NOTE: Convert RE to um.
1425 590450688 : where (re2(:) > 0.0_r8)
1426 : re(:) = (re3(:) / re2(:)) * 1e4_r8
1427 : rm(:) = (3._r8 / 4._r8) * (md(:) / (0.917_r8 * pa(:))) * 1e4_r8
1428 : ar(:) = pa(:) / PI / re2(:)
1429 : end where
1430 :
1431 149188608 : where (md(:) > 0.0_r8)
1432 : vm(:) = vm(:) / md(:)
1433 : end where
1434 :
1435 : ! Store the statistics.
1436 :
1437 : ! Gridbox average
1438 149188608 : gpdiags(icol, :, igroup, GPDIAGS_ND) = nd
1439 149188608 : gpdiags(icol, :, igroup, GPDIAGS_AD) = ad
1440 149188608 : gpdiags(icol, :, igroup, GPDIAGS_MD) = md
1441 149188608 : gpdiags(icol, :, igroup, GPDIAGS_RE) = re
1442 149188608 : gpdiags(icol, :, igroup, GPDIAGS_RM) = rm
1443 149188608 : gpdiags(icol, :, igroup, GPDIAGS_MR) = mr
1444 149188608 : gpdiags(icol, :, igroup, GPDIAGS_EX) = ex
1445 149188608 : gpdiags(icol, :, igroup, GPDIAGS_OD) = od
1446 149188608 : gpdiags(icol, :, igroup, GPDIAGS_VM) = vm
1447 149188608 : gpdiags(icol, :, igroup, GPDIAGS_PA) = pa
1448 149188608 : gpdiags(icol, :, igroup, GPDIAGS_AR) = ar
1449 :
1450 2101248 : if (nucleationRate(1) /= CAM_FILL) then
1451 149188608 : gpdiags(icol, :, igroup, GPDIAGS_JN) = jn
1452 : end if
1453 : end if
1454 : end do
1455 :
1456 :
1457 : ! Get the results for the CARMA gases.
1458 3151872 : do igas = 1, NGAS
1459 2101248 : call pbuf_get_field(pbuf, ipbuf4gas(igas), gc_ptr)
1460 2101248 : call pbuf_get_field(pbuf, ipbuf4sati(igas), sati_ptr)
1461 2101248 : call pbuf_get_field(pbuf, ipbuf4satl(igas), satl_ptr)
1462 :
1463 : call CARMASTATE_GetGas(cstate, igas, newstate(:), rc, satice=satice, satliq=satliq, &
1464 2101248 : eqice=eqice, eqliq=eqliq, wtpct=wtpct)
1465 2101248 : if (rc < 0) call endrun('carma_timestep_tend::CARMASTATE_GetGas failed.')
1466 :
1467 2101248 : icnst = icnst4gas(igas)
1468 :
1469 149188608 : ptend%q(icol, :, icnst) = (newstate(:) - state_loc%q(icol, :, icnst)) / dt
1470 :
1471 149188608 : gsdiags(icol, :, igas, GSDIAGS_SI) = satice(:)
1472 149188608 : gsdiags(icol, :, igas, GSDIAGS_SL) = satliq(:)
1473 149188608 : gsdiags(icol, :, igas, GSDIAGS_EI) = eqice(:)
1474 149188608 : gsdiags(icol, :, igas, GSDIAGS_EL) = eqliq(:)
1475 149188608 : gsdiags(icol, :, igas, GSDIAGS_WT) = wtpct(:)
1476 :
1477 : ! Store the values needed for substepping in the physics buffer.
1478 149188608 : gc_ptr(icol,:) = newstate(:)
1479 149188608 : sati_ptr(icol, :) = satice(:)
1480 156542976 : satl_ptr(icol, :) = satliq(:)
1481 : end do
1482 :
1483 :
1484 : ! Get the results for temperature.
1485 1050624 : call CARMASTATE_GetState(cstate, rc, t=newstate(:))
1486 1050624 : if (rc < 0) call endrun('carma_timestep_tend::CARMASTATE_GetState failed.')
1487 :
1488 : ! Store the values needed for substepping in the physics buffer.
1489 74594304 : t_ptr(icol,:) = newstate(:)
1490 :
1491 1050624 : if (carma_do_thermo) then
1492 0 : ptend%s(icol, :) = (newstate(:) - state_loc%t(icol, :)) * cpair / dt
1493 : endif
1494 :
1495 :
1496 : ! Get the substepping statistics
1497 5326080 : if (carma_do_substep) then
1498 1050624 : call CARMASTATE_Get(cstate, rc, zsubsteps=zsubsteps)
1499 1050624 : if (rc < 0) call endrun('carma_timestep_tend::CARMASTATE_Get failed.')
1500 :
1501 74594304 : spdiags(icol, :, SPDIAGS_NSTEP) = zsubsteps(:)
1502 74594304 : where (zsubsteps/=0.0_r8)
1503 : spdiags(icol, :, SPDIAGS_LNSTEP) = log(zsubsteps(:))
1504 : end where
1505 : end if
1506 : end do
1507 :
1508 :
1509 : ! Report substep diagnostics
1510 72960 : if (carma_do_substep) then
1511 : call CARMASTATE_Get(cstate, rc, max_nsubstep=max_nsubstep, max_nretry=max_nretry, &
1512 72960 : nstep=nstep, nsubstep=nsubstep, nretry=nretry)
1513 72960 : if (rc < 0) call endrun('carma_timestep_tend::CARMASTATE_Get failed.')
1514 :
1515 : !$OMP CRITICAL
1516 72960 : step_max_nsubstep = max(step_max_nsubstep, real(max_nsubstep, f))
1517 72960 : step_max_nretry = max(step_max_nretry, max_nretry)
1518 :
1519 72960 : step_nstep = step_nstep + nstep
1520 72960 : step_nsubstep = step_nsubstep + real(nsubstep, f)
1521 72960 : step_nretry = step_nretry + nretry
1522 : !$OMP END CRITICAL
1523 : end if
1524 :
1525 : ! The CARMASTATE object is no longer needed.
1526 72960 : call CARMASTATE_Destroy(cstate, rc)
1527 72960 : if (rc < 0) call endrun('carma_timestep_tend::CARMASTATE_Destroy failed.')
1528 :
1529 : ! Output diagnostic fields.
1530 72960 : call carma_output_diagnostics(state_loc, ptend, pbuf, cam_in, gpdiags, sbdiags, gsdiags, spdiags, bndiags)
1531 :
1532 145920 : end subroutine carma_timestep_tend
1533 :
1534 :
1535 : !! Get the index for the constituents array for the specified bin
1536 : !! of the specified element.
1537 : !!
1538 : !! @author Yunqian Zhu, Francis Vitt
1539 : !! @version September-2022
1540 : subroutine carma_getcnstforbin(ielem, ibin, icnst)
1541 : implicit none
1542 :
1543 : integer, intent(in) :: ielem, ibin
1544 : integer, intent(out) :: icnst
1545 :
1546 : icnst = icnst4elem(ielem,ibin)
1547 : return
1548 72960 : end subroutine carma_getcnstforbin
1549 :
1550 : !! Collect CARMA substep statistics from all MPI tasks.
1551 : !!
1552 : !! @author Chuck Bardeen
1553 : !! @version May-2009
1554 14592 : subroutine carma_accumulate_stats()
1555 : #if ( defined SPMD )
1556 : use mpishorthand
1557 : #endif
1558 : implicit none
1559 :
1560 : integer :: istat
1561 : integer :: rc
1562 : real(kind=f) :: wrk
1563 : integer :: LUNOPRT ! logical unit number for output
1564 : logical :: do_print ! do print output?
1565 :
1566 : ! Define formats
1567 : 1 format(' carma: max nsubstep=',1F9.0,3x,'avg nsubstep=',1F9.2,3x,'max nretry=',1F9.0,3x,'avg nretry=',1F10.4)
1568 :
1569 14592 : if (carma_do_substep) then
1570 :
1571 14592 : call CARMA_Get(carma, rc, do_print=do_print, LUNOPRT=LUNOPRT)
1572 14592 : if (rc < 0) call endrun('carma_init::CARMA_Get failed.')
1573 :
1574 : #ifdef SPMD
1575 14592 : call mpi_allreduce(step_max_nsubstep, wrk, 1, mpir8, mpi_max, mpicom, istat)
1576 14592 : if( istat /= MPI_SUCCESS ) then
1577 0 : if (do_print) write(LUNOPRT,*) 'carma_timestep_tend: MPI_ALLREDUCE for max_nsubstep failed; error = ',istat
1578 0 : call endrun
1579 : end if
1580 14592 : step_max_nsubstep = wrk
1581 14592 : glob_max_nsubstep = max(glob_max_nsubstep, wrk)
1582 :
1583 14592 : call mpi_allreduce(step_max_nretry, wrk, 1, mpir8, mpi_max, mpicom, istat)
1584 14592 : if( istat /= MPI_SUCCESS ) then
1585 0 : if (do_print) write(LUNOPRT,*) 'carma_timestep_tend: MPI_ALLREDUCE for max_nsubstep failed; error = ',istat
1586 0 : call endrun
1587 : end if
1588 14592 : step_max_nretry = wrk
1589 14592 : glob_max_nretry = max(glob_max_nretry, wrk)
1590 :
1591 14592 : call mpi_allreduce(step_nstep, wrk, 1, mpir8, mpi_sum, mpicom, istat)
1592 14592 : if( istat /= MPI_SUCCESS ) then
1593 0 : if (do_print) write(LUNOPRT,*) 'carma_timestep_tend: MPI_ALLREDUCE for nstep failed; error = ',istat
1594 0 : call endrun
1595 : end if
1596 14592 : step_nstep = wrk
1597 14592 : glob_nstep = glob_nstep + wrk
1598 :
1599 14592 : call mpi_allreduce(step_nsubstep, wrk, 1, mpir8, mpi_sum, mpicom, istat)
1600 14592 : if( istat /= MPI_SUCCESS ) then
1601 0 : if (do_print) write(LUNOPRT,*) 'carma_timestep_tend: MPI_ALLREDUCE for nsubstep failed; error = ',istat
1602 0 : call endrun
1603 : end if
1604 14592 : step_nsubstep = wrk
1605 14592 : glob_nsubstep = glob_nsubstep + wrk
1606 :
1607 14592 : call mpi_allreduce(step_nretry, wrk, 1, mpir8, mpi_sum, mpicom, istat)
1608 14592 : if( istat /= MPI_SUCCESS ) then
1609 0 : if (do_print) write(LUNOPRT,*) 'carma_timestep_tend: MPI_ALLREDUCE for nretry failed; error = ',istat
1610 0 : call endrun
1611 : end if
1612 14592 : step_nretry = wrk
1613 14592 : glob_nretry = glob_nretry + wrk
1614 : #else
1615 :
1616 : ! For single CPU or OMP, just set the globals directly.
1617 : glob_max_nsubstep = max(glob_max_nsubstep, step_max_nsubstep)
1618 : glob_max_nretry = max(glob_max_nretry, step_max_nretry)
1619 : glob_nstep = glob_nstep + step_nstep
1620 : glob_nsubstep = glob_nsubstep + step_nsubstep
1621 : glob_nretry = glob_nretry + step_nretry
1622 :
1623 : #endif
1624 :
1625 14592 : if (masterproc) then
1626 19 : if (step_nstep > 0) then
1627 38 : if (do_print) write(LUNOPRT,1) step_max_nsubstep, &
1628 19 : step_nsubstep / step_nstep, &
1629 19 : step_max_nretry, &
1630 38 : step_nretry / step_nstep
1631 : else
1632 0 : if (do_print) write(LUNOPRT,1) step_max_nsubstep, &
1633 0 : 0._r8, &
1634 0 : step_max_nretry, &
1635 0 : 0._r8
1636 : end if
1637 : end if
1638 : end if
1639 :
1640 14592 : end subroutine carma_accumulate_stats
1641 :
1642 :
1643 : !! Set initial mass mixing ratios of constituents, if nothing is specifed
1644 : !! in the initial conditions file.
1645 : !!
1646 : !! NOTE: This call is part of the CAM Physics Interface
1647 : !!
1648 : !! @author Chuck Bardeen
1649 : !! @version May-2009
1650 92160 : subroutine carma_init_cnst(name, latvals, lonvals, mask, q)
1651 : implicit none
1652 :
1653 : character(len=*), intent(in) :: name !! constituent name
1654 : real(r8), intent(in) :: latvals(:) !! lat in degrees (ncol)
1655 : real(r8), intent(in) :: lonvals(:) !! lon in degrees (ncol)
1656 : logical, intent(in) :: mask(:) !! Only initialize where .true.
1657 : real(r8), intent(out) :: q(:,:) !! mass mixing ratio (gcol, lev)
1658 :
1659 : integer :: igroup ! group index
1660 : integer :: ielem ! element index
1661 : integer :: ilev ! level index
1662 : integer :: ibin ! bin index
1663 : integer :: icnst ! constituent index
1664 : integer :: rc ! CARMA return code
1665 : integer :: cnsttype ! constituent type
1666 : integer :: maxbin ! last prognostic bin
1667 :
1668 : ! Initialize the return code.
1669 92160 : rc = 0
1670 :
1671 : ! Determine the element an bin for the particle
1672 737280 : do ielem = 1, NELEM
1673 13639680 : do ibin = 1, NBIN
1674 :
1675 12902400 : call CARMAELEMENT_Get(carma, ielem, rc, igroup=igroup)
1676 12902400 : if (rc < 0) call endrun('carma_timestep_tend::CARMAELEMENT_Get failed.')
1677 :
1678 12902400 : call CARMAGROUP_Get(carma, igroup, rc, cnsttype=cnsttype, maxbin=maxbin)
1679 12902400 : if (rc < 0) call endrun('carma_timestep_tend::CARMAGROUP_Get failed.')
1680 :
1681 39352320 : if (cnsttype == I_CNSTTYPE_PROGNOSTIC) then
1682 :
1683 : ! Bins past maxbin are treated as diagnostic even if the group
1684 : ! is prognostic and thus are not advected in the paerent model.
1685 12902400 : if (ibin <= maxbin) then
1686 :
1687 12902400 : icnst = icnst4elem(ielem, ibin)
1688 :
1689 12902400 : if (cnst_name(icnst) == name) then
1690 :
1691 : ! By default, initialize all constituents to 0.
1692 6543360 : do ilev = 1, size(q, 2)
1693 167823360 : where(mask)
1694 6451200 : q(:, ilev) = 0.0_r8
1695 : end where
1696 : end do
1697 :
1698 92160 : call CARMAMODEL_InitializeParticle(carma, ielem, ibin, latvals, lonvals, mask, q, rc)
1699 92160 : if (rc < 0) call endrun('carma_init_cnst::CARMA_InitializeParticle failed.')
1700 : end if
1701 : end if
1702 : end if
1703 : end do
1704 : end do
1705 :
1706 : ! NOTE: There is currently no initialization for gases, but it could be
1707 : ! added here.
1708 :
1709 92160 : return
1710 : end subroutine carma_init_cnst
1711 :
1712 : !! Calculate amounts of cloudborne aerosols for use in budget diagnostics. This should
1713 : !! be called before the timestep, and the results passed to CARMA_output_cloudborne_diagnostics()
1714 : !! after the timestep to calculate the tendencies and write them out the the history files.
1715 : !!
1716 : !! NOTE: The exact fields that are calculated are determined by the particular CARMA model.
1717 : !!
1718 : !! @author Chuck Bardeen
1719 : !! @version January-2023
1720 0 : subroutine carma_calculate_cloudborne_diagnostics(state, pbuf, aerclddiag)
1721 :
1722 : implicit none
1723 :
1724 : type(physics_state), intent(in) :: state !! Physics state variables - before CARMA
1725 : type(physics_buffer_desc), pointer :: pbuf(:) !! physics buffer
1726 : real(r8), intent(out) :: aerclddiag(pcols, MAXCLDAERDIAG) !! previous cloudborne diagnostics
1727 :
1728 : integer :: rc
1729 :
1730 0 : call CARMAMODEL_CalculateCloudborneDiagnostics(carma, state, pbuf, aerclddiag, rc)
1731 :
1732 0 : return
1733 : end subroutine carma_calculate_cloudborne_diagnostics
1734 :
1735 :
1736 : !! Output cloudborne aerosol budget tendencies to the history files for physics packages
1737 : !! other than CARMA that may be affecting the CARMA aerosols. Since cloudborne aerosols
1738 : !! are not in the physics_state, you must call CARMA_calculate_cloudborne_diagnostics()
1739 : !! before the timestep tend to capture the prior state. This call will calculate the
1740 : !! final state and output the difference as a tendency. This may be useful for
1741 : !! debugging and for calculating aerosol budgets.
1742 : !!
1743 : !! @author Chuck Bardeen
1744 : !! @version January-2023
1745 0 : subroutine carma_output_cloudborne_diagnostics(state, pbuf, pname, dt, oldaerclddiag)
1746 :
1747 : implicit none
1748 :
1749 : type(physics_state), intent(in) :: state !! Physics state variables - before CARMA
1750 : type(physics_buffer_desc), pointer :: pbuf(:) !! physics buffer
1751 : character(*), intent(in) :: pname !! short name of the physics package
1752 : real(r8), intent(in) :: dt !! timestep (s)
1753 : real(r8), intent(in) :: oldaerclddiag(pcols, MAXCLDAERDIAG) !! previous cloudborne diagnostics
1754 :
1755 : integer :: rc
1756 : integer :: i
1757 :
1758 : ! Check to make sure the the package is in the packages list.
1759 0 : do i = 1, carma_ndiagpkgs
1760 0 : if (trim(carma_diags_packages(i)) .eq. trim(pname)) then
1761 :
1762 : ! Allow models to output their own diagnostics related to aerosol
1763 : ! budgets related to physics packages other than CARMA
1764 0 : call CARMAMODEL_OutputCloudborneDiagnostics(carma, state, pbuf, dt, pname, oldaerclddiag, rc)
1765 0 : exit
1766 : end if
1767 : end do
1768 :
1769 0 : return
1770 : end subroutine carma_output_cloudborne_diagnostics
1771 :
1772 :
1773 :
1774 : !! Output budget tendencies to the history files for physics packages
1775 : !! other than CARMA that may be affecting the CARMA aerosols. This can be
1776 : !! called for any physics package that is using ptend to modify the CARMA
1777 : !! aerosol, and may be useful for debugging and for calculating aerosol budgets.
1778 : !!
1779 : !! All the columns in the chunk should be output at the same time.
1780 : !!
1781 : !! @author Chuck Bardeen
1782 : !! @version January-2023
1783 0 : subroutine carma_output_budget_diagnostics(state, ptend, old_cflux, cflux, dt, pname)
1784 :
1785 : implicit none
1786 :
1787 : type(physics_state), intent(in) :: state !! Physics state variables - before CARMA
1788 : type(physics_ptend), intent(in) :: ptend !! indivdual parameterization tendencies
1789 : real(r8) :: old_cflux(pcols,pcnst) !! cam_in%clfux from before the timestep_tend
1790 : real(r8) :: cflux(pcols,pcnst) !! cam_in%clfux from after the timestep_tend
1791 : real(r8), intent(in) :: dt !! timestep (s)
1792 : character(*), intent(in) :: pname !! short name of the physics package
1793 :
1794 : integer :: rc
1795 :
1796 : integer :: i
1797 :
1798 : ! Check to make sure the the package is in the packages list.
1799 0 : do i = 1, carma_ndiagpkgs
1800 0 : if (trim(carma_diags_packages(i)) .eq. trim(pname)) then
1801 :
1802 : ! Allow models to output their own diagnostics related to aerosol
1803 : ! budgets related to physics packages other than CARMA
1804 0 : call CARMAMODEL_OutputBudgetDiagnostics(carma, icnst4elem, icnst4gas, state, ptend, old_cflux, cflux, dt, pname, rc)
1805 0 : exit
1806 : end if
1807 : end do
1808 :
1809 0 : return
1810 : end subroutine carma_output_budget_diagnostics
1811 :
1812 : !! Outputs tracer tendencies and diagnositc fields to the history files.
1813 : !! All the columns in the chunk should be output at the same time.
1814 : !!
1815 : !! @author Chuck Bardeen
1816 : !! @version May-2009
1817 72960 : subroutine carma_output_diagnostics(state, ptend, pbuf, cam_in, gpdiags, sbdiags, gsdiags, spdiags, bndiags)
1818 : use cam_history, only: outfld
1819 : use camsrfexch, only: cam_in_t
1820 :
1821 : implicit none
1822 :
1823 : type(physics_state), intent(in) :: state !! Physics state variables - before CARMA
1824 : type(physics_ptend), intent(in) :: ptend !! indivdual parameterization tendencies
1825 : type(physics_buffer_desc), pointer :: pbuf(:) !! physics buffer
1826 : type(cam_in_t), intent(in) :: cam_in !! surface inputs
1827 : real(r8), intent(in), dimension(pcols, pver, NGROUP, NGPDIAGS) :: gpdiags !! CARMA group diagnostic output
1828 : real(r8), intent(in), dimension(pcols, NBIN, NELEM, NSBDIAGS) :: sbdiags !! CARMA surface bin diagnostic output
1829 : real(r8), intent(in), dimension(pcols, pver, NGAS, NGSDIAGS) :: gsdiags !! CARMA gas diagnostic output
1830 : real(r8), intent(in), dimension(pcols, pver, NSPDIAGS) :: spdiags !! CARMA step diagnostic output
1831 : real(r8), intent(in), dimension(pcols, pver, NBIN, NELEM, NBNDIAGS) :: bndiags !! CARMA bin diagnostic output
1832 :
1833 : ! Local variables
1834 : integer :: igroup ! group index
1835 : integer :: ielem ! element index
1836 : integer :: ibin ! bin index
1837 : integer :: igas ! gas index
1838 : integer :: ienconc ! element index for group's concentration element
1839 : integer :: icnst ! constituent index
1840 : integer :: lchnk ! chunk identifier
1841 : integer :: rc ! CARMA return code
1842 : character(len=8) :: sname ! short (CAM) name
1843 : integer :: cnsttype ! constituent type
1844 : integer :: maxbin ! last prognostic bin
1845 : logical :: is_cloud ! is the group a cloud?
1846 : logical :: do_drydep ! is dry deposition enabled?
1847 :
1848 : character(len=*), parameter :: subname = 'carma_output_diagnostics'
1849 :
1850 : ! Initialize the return code.
1851 72960 : rc = 0
1852 :
1853 : ! Check each column int the chunk.
1854 72960 : lchnk = state%lchnk
1855 :
1856 : ! Output step diagnostics.
1857 72960 : if (carma_do_substep) then
1858 72960 : call outfld('CRNSTEP', spdiags(:, :, SPDIAGS_NSTEP), pcols, lchnk)
1859 72960 : call outfld('CRLNSTEP', spdiags(:, :, SPDIAGS_LNSTEP), pcols, lchnk)
1860 : end if
1861 :
1862 : ! Output the particle tendencies.
1863 583680 : do ielem = 1, NELEM
1864 10798080 : do ibin = 1, NBIN
1865 :
1866 10214400 : call CARMAELEMENT_Get(carma, ielem, rc, igroup=igroup)
1867 10214400 : if (rc < 0) call endrun(subname//'::CARMAELEMENT_Get failed.')
1868 :
1869 10214400 : call CARMAGROUP_Get(carma, igroup, rc, cnsttype=cnsttype, maxbin=maxbin, do_drydep=do_drydep)
1870 10214400 : if (rc < 0) call endrun(subname//'::CARMAGROUP_Get failed.')
1871 :
1872 31153920 : if (cnsttype == I_CNSTTYPE_PROGNOSTIC) then
1873 :
1874 : ! Bins past maxbin are treated as diagnostic even if the group
1875 : ! is prognostic and thus are not advected in the paerent model.
1876 10214400 : if (ibin <= maxbin) then
1877 :
1878 10214400 : icnst = icnst4elem(ielem, ibin)
1879 :
1880 10214400 : call outfld(trim(etndname(ielem, ibin))//'TC', ptend%q(:, :, icnst), pcols, lchnk)
1881 :
1882 10214400 : if (do_drydep) then
1883 10214400 : call outfld(trim(etndname(ielem, ibin))//'DD', sbdiags(:, ibin, ielem, SBDIAGS_DD), pcols, lchnk)
1884 : end if
1885 :
1886 10214400 : if (carma_do_pheat) then
1887 :
1888 : ! Only specified for the number density element of the group.
1889 0 : if (bndiags(1, 1, ibin, ielem, BNDIAGS_TP) /= CAM_FILL) then
1890 0 : call outfld(trim(etndname(ielem, ibin))//'TP', bndiags(:, :, ibin, ielem, BNDIAGS_TP), pcols, lchnk)
1891 : end if
1892 : end if
1893 : end if
1894 : end if
1895 : end do
1896 : end do
1897 :
1898 : ! Output the particle diagnostics.
1899 218880 : do igroup = 1, NGROUP
1900 145920 : call CARMAGROUP_Get(carma, igroup, rc, shortname=sname, is_cloud=is_cloud, do_drydep=do_drydep, ienconc=ienconc)
1901 145920 : if (rc < 0) call endrun(subname//'::CARMAGROUP_Get failed.')
1902 :
1903 : ! Gridbox average
1904 145920 : call outfld(trim(sname)//'ND', gpdiags(:, :, igroup, GPDIAGS_ND), pcols, lchnk)
1905 145920 : call outfld(trim(sname)//'AD', gpdiags(:, :, igroup, GPDIAGS_AD), pcols, lchnk)
1906 145920 : call outfld(trim(sname)//'MD', gpdiags(:, :, igroup, GPDIAGS_MD), pcols, lchnk)
1907 145920 : call outfld(trim(sname)//'RE', gpdiags(:, :, igroup, GPDIAGS_RE), pcols, lchnk)
1908 145920 : call outfld(trim(sname)//'RM', gpdiags(:, :, igroup, GPDIAGS_RM), pcols, lchnk)
1909 145920 : call outfld(trim(sname)//'JN', gpdiags(:, :, igroup, GPDIAGS_JN), pcols, lchnk)
1910 145920 : call outfld(trim(sname)//'MR', gpdiags(:, :, igroup, GPDIAGS_MR), pcols, lchnk)
1911 145920 : call outfld(trim(sname)//'EX', gpdiags(:, :, igroup, GPDIAGS_EX), pcols, lchnk)
1912 145920 : call outfld(trim(sname)//'OD', gpdiags(:, :, igroup, GPDIAGS_OD), pcols, lchnk)
1913 145920 : call outfld(trim(sname)//'PA', gpdiags(:, :, igroup, GPDIAGS_PA), pcols, lchnk)
1914 145920 : call outfld(trim(sname)//'AR', gpdiags(:, :, igroup, GPDIAGS_AR), pcols, lchnk)
1915 145920 : call outfld(trim(sname)//'VM', gpdiags(:, :, igroup, GPDIAGS_VM), pcols, lchnk)
1916 145920 : call outfld(trim(sname)//'VR', gpdiags(:, :, igroup, GPDIAGS_VR), pcols, lchnk)
1917 :
1918 145920 : if (do_drydep) then
1919 3064320 : do ibin = 1, NBIN
1920 3064320 : call outfld(trim(btndname(igroup, ibin))//'VD', sbdiags(:, ibin, ienconc, SBDIAGS_VD), pcols, lchnk)
1921 : end do
1922 : end if
1923 :
1924 3283200 : do ibin = 1,NBIN
1925 2918400 : call outfld(trim(btndname(igroup, ibin))//'ND',bndiags(:, :, ibin, ienconc, BNDIAGS_ND), pcols, lchnk)
1926 2918400 : call outfld(trim(btndname(igroup, ibin))//'WR',bndiags(:, :, ibin, ienconc, BNDIAGS_WETR), pcols, lchnk)
1927 2918400 : call outfld(trim(btndname(igroup, ibin))//'RO',bndiags(:, :, ibin, ienconc, BNDIAGS_RO), pcols, lchnk)
1928 3064320 : call outfld(trim(btndname(igroup, ibin))//'VR',bndiags(:, :, ibin, ienconc, BNDIAGS_VR), pcols, lchnk)
1929 : end do
1930 : end do
1931 :
1932 : ! Output the gas tendencies.
1933 218880 : do igas = 1, NGAS
1934 145920 : icnst = icnst4gas(igas)
1935 :
1936 145920 : call outfld(gtndname(igas), ptend%q(:, :, icnst), pcols, lchnk)
1937 :
1938 : ! Output the supersaturations.
1939 145920 : call outfld(trim(cnst_name(icnst))//'SI', gsdiags(:, :, igas, GSDIAGS_SI), pcols, lchnk)
1940 145920 : call outfld(trim(cnst_name(icnst))//'SL', gsdiags(:, :, igas, GSDIAGS_SL), pcols, lchnk)
1941 145920 : call outfld(trim(cnst_name(icnst))//'EI', gsdiags(:, :, igas, GSDIAGS_EI), pcols, lchnk)
1942 145920 : call outfld(trim(cnst_name(icnst))//'EL', gsdiags(:, :, igas, GSDIAGS_EL), pcols, lchnk)
1943 218880 : call outfld(trim(cnst_name(icnst))//'WT', gsdiags(:, :, igas, GSDIAGS_WT), pcols, lchnk)
1944 : end do
1945 :
1946 : ! Output the temperature tendency.
1947 72960 : if (carma_do_thermo) then
1948 0 : call outfld('CRTT', ptend%s(:, :) / cpair, pcols, lchnk)
1949 : end if
1950 :
1951 : ! Allow models to output their own diagnostics
1952 72960 : call CARMAMODEL_OutputDiagnostics(carma, icnst4elem, state, ptend, pbuf, cam_in, rc)
1953 :
1954 72960 : return
1955 72960 : end subroutine carma_output_diagnostics
1956 :
1957 : !! Calculate the emissions for CARMA aerosols. This is taken from
1958 : !! the routine aerosol_emis_intr in aerosol_intr.F90 and dust_emis_intr in
1959 : !! dust_intr.F90 by Phil Rasch.
1960 : !!
1961 : !! @author Chuck Bardeen
1962 : !! @version May-2009
1963 72960 : subroutine carma_emission_tend (state, ptend, cam_in, dt, pbuf)
1964 72960 : use cam_history, only: outfld
1965 : use camsrfexch, only: cam_in_t
1966 :
1967 : implicit none
1968 :
1969 : type(physics_state), intent(in ) :: state !! physics state
1970 : type(physics_ptend), intent(inout) :: ptend !! physics state tendencies
1971 : type(cam_in_t), intent(inout) :: cam_in !! surface inputs
1972 : real(r8), intent(in) :: dt !! time step (s)
1973 : type(physics_buffer_desc), pointer :: pbuf(:) !! physics buffer
1974 :
1975 : integer :: lchnk ! chunk identifier
1976 : integer :: ncol ! number of columns in chunk
1977 : integer :: igroup ! group index
1978 : integer :: ielem ! element index
1979 : integer :: ibin ! bin index
1980 : integer :: icnst ! consituent index
1981 : real(r8) :: tendency(pcols, pver) ! constituent tendency (kg/kg/s)
1982 : real(r8) :: surfaceFlux(pcols) ! constituent surface flux (kg/m^2/s)
1983 : integer :: cnsttype ! constituent type
1984 : integer :: maxbin ! last prognostic bin
1985 : integer :: rc ! CARMA return code
1986 :
1987 : ! Initialize the return code.
1988 72960 : rc = 0
1989 :
1990 : ! Initialize the output tendency structure.
1991 72960 : call physics_ptend_init(ptend,state%psetcols, 'CARMA (emission)', lq=lq_carma)
1992 :
1993 72960 : if (.not. carma_flag) return
1994 72960 : if (.not. carma_do_emission) return
1995 :
1996 72960 : ncol = state%ncol
1997 72960 : lchnk = state%lchnk
1998 :
1999 : ! Provide emissions rates for particles.
2000 : !
2001 : ! NOTE: This can only be done for prognostic groups.
2002 583680 : do ielem = 1, NELEM
2003 510720 : call CARMAELEMENT_Get(carma, ielem, rc, igroup=igroup)
2004 510720 : if (rc < 0) call endrun('carma_drydep_tend::CARMAELEMENT_Get failed.')
2005 :
2006 510720 : call CARMAGROUP_Get(carma, igroup, rc, cnsttype=cnsttype, maxbin=maxbin)
2007 510720 : if (rc < 0) call endrun('carma_drydep_tend::CARMAGROUP_Get failed.')
2008 :
2009 1605120 : if (cnsttype == I_CNSTTYPE_PROGNOSTIC) then
2010 :
2011 10725120 : do ibin = 1, NBIN
2012 :
2013 : ! Bins past maxbin are treated as diagnostic even if the group
2014 : ! is prognostic and thus are not advected in the paerent model.
2015 10725120 : if (ibin <= maxbin) then
2016 :
2017 10214400 : icnst = icnst4elem(ielem, ibin)
2018 :
2019 10214400 : call CARMAMODEL_EmitParticle(carma, ielem, ibin, icnst, dt, state, cam_in, tendency, surfaceFlux, pbuf, rc)
2020 10214400 : if (rc < 0) call endrun('carma_emission_tend::CARMA_EmitParticle failed.')
2021 :
2022 : ! Add any surface flux here.
2023 157301760 : cam_in%cflx(:ncol, icnst) = surfaceFlux(:ncol)
2024 10214400 : call outfld(trim(cnst_name(icnst))//'SF', cam_in%cflx(:ncol, icnst), ncol, lchnk)
2025 :
2026 : ! For emissions into the atmosphere, put the emission here.
2027 11021337600 : ptend%q(:ncol, :pver, icnst) = tendency(:ncol, :pver)
2028 11021337600 : call outfld(trim(cnst_name(icnst))//'EM', ptend%q(:ncol, :, icnst), ncol, lchnk)
2029 : end if
2030 : enddo
2031 : end if
2032 : enddo
2033 :
2034 : ! No emissions rate is set up for gases, but it could be added here.
2035 :
2036 : return
2037 72960 : end subroutine carma_emission_tend
2038 :
2039 :
2040 : !! Calculate the wet deposition for the CARMA aerosols. This is taken from
2041 : !! the routine aerosol_wet_int in aerosol_intr.F90 and dust_wet_intr in
2042 : !! dust_intr.F90 by Phil Rasch.
2043 : !!
2044 : !! Method:
2045 : !! Use a modified version of the scavenging parameterization described in
2046 : !! Barth et al, 2000, JGR (sulfur cycle paper)
2047 : !! Rasch et al, 2001, JGR (INDOEX paper)
2048 : !!
2049 : !! @author Chuck Bardeen
2050 : !! @version May-2009
2051 0 : subroutine carma_wetdep_tend(state, ptend, dt, pbuf, dlf, cam_out)
2052 72960 : use cam_history, only: outfld
2053 : use phys_control, only: cam_physpkg_is
2054 : use wetdep, only: clddiag, wetdepa_v1, wetdepa_v2
2055 : use camsrfexch, only: cam_out_t
2056 : use physconst, only: gravit
2057 :
2058 : implicit none
2059 :
2060 : real(r8), intent(in) :: dt !! time step (s)
2061 : type(physics_state), intent(in ) :: state !! physics state
2062 : type(physics_ptend), intent(inout) :: ptend !! physics state tendencies
2063 : type(physics_buffer_desc), pointer :: pbuf(:) !! physics buffer
2064 : real(r8), intent(in) :: dlf(pcols,pver) !! Detrainment of convective condensate
2065 : type(cam_out_t), intent(inout) :: cam_out !! cam output to surface models
2066 :
2067 : ! local vars
2068 : real(r8) :: rainmr(pcols,pver) ! mixing ratio of rain within cloud volume
2069 : real(r8) :: cldv(pcols,pver) ! cloudy volume undergoing wet chem and scavenging
2070 : real(r8) :: cldvcu(pcols,pver) ! Convective precipitation area, top interface of current layer
2071 : real(r8) :: cldvst(pcols,pver) ! Stratiform precipitation area, top interface of current layer
2072 : integer :: ielem ! element index
2073 : integer :: igroup ! group index
2074 : integer :: ibin ! bin index
2075 : integer :: icnst ! constituent index
2076 : real(r8) :: conicw(pcols,pver) ! convective in-cloud water
2077 : real(r8) :: cmfdqr(pcols,pver) ! convective production of rain
2078 : real(r8) :: cldc(pcols,pver) ! convective cloud fraction, currently empty
2079 : real(r8) :: clds(pcols,pver) ! Stratiform cloud fraction
2080 : real(r8) :: evapc(pcols,pver) ! Evaporation rate of convective precipitation
2081 : real(r8) :: iscavt(pcols, pver)
2082 : real(r8) :: scavt(pcols, pver)
2083 : integer :: ixcldliq
2084 : integer :: ixcldice
2085 : real(r8) :: totcond(pcols, pver) ! total condensate
2086 : real(r8) :: solfac(pcols, pver) ! solubility factor
2087 : real(r8) :: solfac_in ! solubility factor
2088 : real(r8) :: scavcoef ! scavenging Coefficient
2089 : logical :: do_wetdep
2090 : integer :: ncol ! number of columns
2091 : integer :: lchnk ! chunk identifier
2092 : integer :: rc ! CARMA return code
2093 : real(r8) :: z_scavcoef(pcols,pver) ! Dana and Hales coefficient (/mm)
2094 : integer :: cnsttype ! constituent type
2095 : integer :: k
2096 : real(r8) :: sflx(pcols) ! Surface Flux (kg/m2/s)
2097 : integer :: maxbin
2098 :
2099 : ! physics buffer
2100 : integer itim_old
2101 0 : real(r8), pointer, dimension(:,:) :: cldn ! cloud fraction
2102 0 : real(r8), pointer, dimension(:,:) :: cme
2103 0 : real(r8), pointer, dimension(:,:) :: prain
2104 0 : real(r8), pointer, dimension(:,:) :: evapr
2105 0 : real(r8), pointer, dimension(:,:) :: icwmrdp ! in cloud water mixing ratio, deep convection
2106 0 : real(r8), pointer, dimension(:,:) :: rprddp ! rain production, deep convection
2107 0 : real(r8), pointer, dimension(:,:) :: icwmrsh ! in cloud water mixing ratio, deep convection
2108 0 : real(r8), pointer, dimension(:,:) :: rprdsh ! rain production, deep convection
2109 0 : real(r8), pointer, dimension(:,:,:) :: fracis ! fraction of transported species that are insoluble
2110 0 : real(r8), pointer, dimension(:,:) :: sh_frac ! Shallow convective cloud fraction
2111 0 : real(r8), pointer, dimension(:,:) :: dp_frac ! Deep convective cloud fraction
2112 0 : real(r8), pointer, dimension(:,:) :: evapcsh ! Evaporation rate of shallow convective precipitation >=0.
2113 0 : real(r8), pointer, dimension(:,:) :: evapcdp ! Evaporation rate of deep convective precipitation >=0.
2114 :
2115 : ! Initialize the return code.
2116 0 : rc = 0
2117 :
2118 : ! Initialize the output tendency structure.
2119 0 : call physics_ptend_init(ptend,state%psetcols, 'CARMA (wetdep)', lq=lq_carma)
2120 :
2121 0 : if (.not. carma_flag) return
2122 0 : if (.not. carma_do_wetdep) return
2123 :
2124 0 : ncol = state%ncol
2125 0 : lchnk = state%lchnk
2126 :
2127 : ! Associate pointers with physics buffer fields
2128 0 : itim_old = pbuf_old_tim_idx()
2129 :
2130 0 : call pbuf_get_field(pbuf, pbuf_get_index('CLD'), cldn, (/1,1,itim_old/),(/pcols,pver,1/))
2131 0 : call pbuf_get_field(pbuf, pbuf_get_index('QME'), cme )
2132 0 : call pbuf_get_field(pbuf, pbuf_get_index('PRAIN'), prain )
2133 0 : call pbuf_get_field(pbuf, pbuf_get_index('NEVAPR'), evapr )
2134 0 : call pbuf_get_field(pbuf, pbuf_get_index('FRACIS'), fracis )
2135 0 : call pbuf_get_field(pbuf, pbuf_get_index('ICWMRDP'), icwmrdp )
2136 0 : call pbuf_get_field(pbuf, pbuf_get_index('RPRDDP'), rprddp )
2137 0 : call pbuf_get_field(pbuf, pbuf_get_index('ICWMRSH'), icwmrsh )
2138 0 : call pbuf_get_field(pbuf, pbuf_get_index('RPRDSH'), rprdsh )
2139 :
2140 : ! sum deep and shallow convection contributions
2141 0 : conicw(:ncol,:) = icwmrdp(:ncol,:) + icwmrsh(:ncol,:)
2142 0 : cmfdqr(:ncol,:) = rprddp(:ncol,:) + rprdsh(:ncol,:)
2143 :
2144 0 : call pbuf_get_field(pbuf, pbuf_get_index('SH_FRAC'), sh_frac )
2145 0 : call pbuf_get_field(pbuf, pbuf_get_index('DP_FRAC'), dp_frac )
2146 0 : call pbuf_get_field(pbuf, pbuf_get_index('NEVAPR_SHCU'), evapcsh )
2147 0 : call pbuf_get_field(pbuf, pbuf_get_index('NEVAPR_DPCU'), evapcdp )
2148 :
2149 0 : cldc(:ncol,:) = dp_frac(:ncol,:) + sh_frac(:ncol,:) ! Sungsu included this.
2150 0 : evapc(:ncol,:) = evapcsh(:ncol,:) + evapcdp(:ncol,:) ! Sungsu included this.
2151 0 : clds(:ncol,:) = cldn(:ncol,:) - cldc(:ncol,:) ! Stratiform cloud fraction
2152 :
2153 :
2154 0 : cmfdqr(:ncol,:) = rprddp(:ncol,:) + rprdsh(:ncol,:)
2155 :
2156 : ! fields needed for wet scavenging
2157 : call clddiag( state%t, state%pmid, state%pdel, cmfdqr, evapc, cldn, cldc, clds, cme, evapr, prain, &
2158 0 : cldv, cldvcu, cldvst, rainmr, ncol )
2159 :
2160 0 : call cnst_get_ind('CLDICE', ixcldice)
2161 0 : call cnst_get_ind('CLDLIQ', ixcldliq)
2162 0 : totcond(:ncol,:) = state%q(:ncol,:,ixcldliq) + &
2163 0 : state%q(:ncol,:,ixcldice)
2164 :
2165 : ! Iterate over each particle and calculate a tendency from wet
2166 : ! scavenging for it.
2167 0 : do ielem = 1, NELEM
2168 :
2169 : ! NOTE: This can only be done for prognistic groups.
2170 :
2171 0 : call CARMAELEMENT_Get(carma, ielem, rc, igroup=igroup)
2172 0 : if (rc < 0) call endrun('carma_wetdep_tend::CARMAELEMENT_Get failed.')
2173 :
2174 : call CARMAGROUP_Get(carma, igroup, rc, cnsttype=cnsttype, do_wetdep=do_wetdep, &
2175 0 : solfac=solfac_in, scavcoef=scavcoef, maxbin=maxbin)
2176 0 : if (rc < 0) call endrun('carma_wetdep_tend::CARMAGROUP_Get failed.')
2177 :
2178 0 : solfac(:,:) = solfac_in
2179 :
2180 0 : if ((do_wetdep) .and. (cnsttype == I_CNSTTYPE_PROGNOSTIC)) then
2181 :
2182 0 : do ibin = 1, NBIN
2183 :
2184 : ! Bins past maxbin are treated as diagnostic even if the group
2185 : ! is prognostic and thus are not advected in the parent model.
2186 0 : if (ibin <= maxbin) then
2187 :
2188 0 : icnst = icnst4elem(ielem, ibin)
2189 :
2190 0 : scavt = 0._r8
2191 :
2192 : ! The scavenging coefficient might be calculated as a function of
2193 : ! the aerosol bin at each grid point. However, for now, we will just
2194 : ! use a constant value for each group.
2195 0 : z_scavcoef(:, :) = scavcoef
2196 :
2197 0 : if (cam_physpkg_is('cam5') .or. cam_physpkg_is('cam6')) then
2198 :
2199 : call wetdepa_v2( &
2200 : state%pmid, &
2201 : state%q, &
2202 : state%pdel, &
2203 : cldn, &
2204 : cldc, &
2205 : cmfdqr, &
2206 : evapc, &
2207 : conicw, &
2208 : prain, &
2209 : cme, &
2210 : evapr, &
2211 : totcond, &
2212 : state%q(:, :, icnst), &
2213 : dt, &
2214 : scavt, &
2215 : iscavt, &
2216 : cldvcu, &
2217 : cldvst, &
2218 : dlf, &
2219 : fracis(:, :, icnst), &
2220 : solfac, &
2221 : ncol, &
2222 0 : z_scavcoef)
2223 :
2224 0 : else if (cam_physpkg_is('cam4')) then
2225 :
2226 : call wetdepa_v1(state%t, &
2227 : state%pmid, &
2228 : state%q, &
2229 : state%pdel, &
2230 : cldn, &
2231 : cldc, &
2232 : cmfdqr, &
2233 : conicw, &
2234 : prain, &
2235 : cme, &
2236 : evapr, &
2237 : totcond, &
2238 : state%q(:, :, icnst), &
2239 : dt, &
2240 : scavt, &
2241 : iscavt, &
2242 : cldv, &
2243 : fracis(:, :, icnst), &
2244 : solfac_in, &
2245 : ncol, &
2246 0 : z_scavcoef)
2247 : else
2248 :
2249 0 : call endrun('carma_wetdep_tend:: No wet deposition routine is available for this configuration.')
2250 : end if
2251 :
2252 0 : ptend%q(:, :, icnst) = scavt
2253 0 : call outfld(trim(cnst_name(icnst))//'WD', ptend%q(:, :, icnst), pcols, lchnk)
2254 :
2255 : !
2256 : ! ptend%q(kg/kg air/s) * pdel(Pa) / gravit (m/s2) => (kg/m2/s)
2257 : ! note: 1Pa = 1 kg air * (m/s2) / m2
2258 0 : sflx(:) = 0._r8
2259 :
2260 0 : do k = 1,pver
2261 0 : sflx(:ncol) = sflx(:ncol) - ptend%q(:ncol, k, icnst) * state%pdel(:ncol,k) / gravit
2262 : enddo
2263 :
2264 0 : call outfld(trim(cnst_name(icnst))//'SW', sflx, pcols, lchnk)
2265 :
2266 : ! Add this to the surface amount of the constituent
2267 0 : call CARMAMODEL_WetDeposition(carma, ielem, ibin, sflx, cam_out, state, rc)
2268 :
2269 : end if
2270 : end do
2271 : end if
2272 : end do
2273 :
2274 : return
2275 0 : end subroutine carma_wetdep_tend
2276 :
2277 :
2278 : !! This routine creates files containing optical properties for each radiatively
2279 : !! active particle type. These optical properties are used by the RRTMG radiation
2280 : !! code to include the impact of CARMA particles in the radiative transfer
2281 : !! calculation.
2282 : !!
2283 : !! The opticsType that is specified for the group determines how the optical
2284 : !! properties will be generated for that group. Each group can use a different
2285 : !! optics method if needed. Refractive indices need for these calculation are
2286 : !! are specified in the group's elements rather than at the group level. This
2287 : !! allows various mixing approaches to be used to determine the refractive index
2288 : !! for the particle as a whole.
2289 : !!
2290 : !! The I_OPTICS_MIXED_YU2105 and I_OPTICS_SULFATE_YU2015 optics methods are
2291 : !! designed to trop_strat models as define in the Yu et al. (2015) paper. The
2292 : !! other optics types can be applied more generically to a number of different
2293 : !! aerosol/cloud models.
2294 : !!
2295 : !! NOTE: The format of this file is determined by the needs of the radiative tranfer
2296 : !! code, so ideally a routine would exist in that module that could create a file
2297 : !! with the proper format. Since that doesn't exist, we do it all here.
2298 0 : subroutine CARMA_CreateOpticsFile(carma, rc)
2299 :
2300 : implicit none
2301 :
2302 : type(carma_type), intent(inout) :: carma !! the carma object
2303 : integer, intent(out) :: rc !! return code, negative indicates failure
2304 :
2305 : ! Local variables
2306 : integer :: igroup
2307 : logical :: do_mie
2308 : integer :: cnsttype ! constituent type
2309 : integer :: opticsType
2310 :
2311 : ! Assume success.
2312 0 : rc = 0
2313 :
2314 : ! Process each group that is defined in the model.
2315 0 : do igroup = 1, NGROUP
2316 :
2317 : ! Get the necessary group properties.
2318 0 : call CARMAGROUP_Get(carma, igroup, rc, do_mie=do_mie, cnsttype=cnsttype, iopticstype=opticsType)
2319 0 : if (rc < 0) call endrun('carma_CreateOpticsFile::CARMAGROUP_Get failed.')
2320 :
2321 : ! Are we supposed to do the mie calculation for this group?
2322 0 : if ((do_mie) .and. (cnsttype == I_CNSTTYPE_PROGNOSTIC)) then
2323 :
2324 : ! What type of calculation is needed for this group?
2325 : !
2326 : ! NOTE: Some of these calculations generate optical properties as single mass
2327 : ! coefficients, while others are lookup tables designed around multiple
2328 : ! dimensions.
2329 0 : select case (opticsType)
2330 :
2331 : ! This is for fixed composition, but the particle may swell in response
2332 : ! to changes in RH. Only one refractive index specified at the group level.
2333 : !
2334 : ! NOTE: This is what was used by the first CARMA models that were radiatively
2335 : ! active.
2336 : case (I_OPTICS_FIXED)
2337 0 : call CARMA_CreateOpticsFile_Fixed(carma, igroup, rc)
2338 0 : if (rc < 0) call endrun('carma_CreateOpticsFile::CreateOpticsFile_Fixed failed.')
2339 :
2340 : ! This is similar to Yu (2015) in that handles mixed particles treated as
2341 : ! core shell particles; however the dimensions of the lookup table are the
2342 : ! the radii and the refractive indicies, so it can be used with various
2343 : ! aerosol configurations (not just as in the Yu(2015)).
2344 : case(I_OPTICS_MIXED_CORESHELL)
2345 0 : call endrun('carma_CreateOpticsFile mixed_coreshell has not been implemented.')
2346 :
2347 : ! This is similar to MAM4, in that a volume mixing approach is used to
2348 : ! mixed both the core and the shell together and thus only one radius and
2349 : ! one refractive index are needed in the lookup table.
2350 : case(I_OPTICS_MIXED_VOLUME)
2351 0 : call endrun('carma_CreateOpticsFile mixed_volume has not been implemented.')
2352 :
2353 : ! This is similar to "mixed_volume", except that Maxwell-Garnett mixing
2354 : ! is used instead of volume mixing.
2355 : case(I_OPTICS_MIXED_MAXWELL)
2356 0 : call endrun('carma_CreateOpticsFile mixed_maxwell has not been implemented.')
2357 :
2358 : ! This is for a pure sulfate group where the table is based upon weight
2359 : ! percent; however, unlike sulfate_Yu, the refractive index of the sulfate
2360 : ! changes with the weight percent of H2SO4.
2361 : case(I_OPTICS_SULFATE)
2362 0 : call CARMA_CreateOpticsFile_Sulfate(carma, igroup, rc)
2363 0 : if (rc < 0) call endrun('carma_CreateOpticsFile::CreateOpticsFile_Sulfate failed.')
2364 :
2365 : ! Other types are not generically useful are are particular to the
2366 : ! specific model, so thos are handled by model specific code. These
2367 : ! include:
2368 : ! I_OPTICS_MIXED_YU2015
2369 : ! I_OPTICS_MIXED_YU_H2O
2370 : ! I_OPTICS_SULFATE_YU2015
2371 : case default
2372 0 : call CARMAMODEL_CreateOpticsFile(carma, igroup, opticsType, rc)
2373 :
2374 : end select
2375 : end if
2376 : end do
2377 :
2378 0 : return
2379 0 : end subroutine CARMA_CreateOpticsFile
2380 :
2381 :
2382 : !! This routine creates files containing optical properties for each radiatively
2383 : !! active particle type. These optical properties are used by the RRTMG radiation
2384 : !! code to include the impact of CARMA particles in the radiative transfer
2385 : !! calculation.
2386 : !!
2387 : !! NOTE: The format of this file is determined by the needs of the radiative tranfer
2388 : !! code, so ideally a routine would exist in that module that could create a file
2389 : !! with the proper format. Since that doesn't exist, we do it all here.
2390 0 : subroutine CARMA_CreateOpticsFile_Fixed(carma, igroup, rc)
2391 : use wrap_nf
2392 : use wetr, only : getwetr
2393 :
2394 : implicit none
2395 :
2396 : type(carma_type), intent(inout) :: carma !! the carma object
2397 : integer, intent(in) :: igroup !! group index
2398 : integer, intent(out) :: rc !! return code, negative indicates failure
2399 :
2400 : ! Local variables
2401 : integer :: ibin, iwave, irh
2402 : integer :: irhswell
2403 : integer :: ienconc
2404 : real(kind=f) :: rho(NBIN), rhopwet
2405 : real(kind=f) :: r(NBIN), rmass(NBIN), rlow(NBIN), rup(NBIN)
2406 : real(kind=f) :: wave(NWAVE)
2407 : complex(kind=f) :: refidx(NWAVE, NREFIDX)
2408 : character(len=CARMA_NAME_LEN) :: name
2409 : character(len=CARMA_SHORT_NAME_LEN) :: shortname
2410 : logical :: do_mie
2411 : integer :: fid
2412 : integer :: rhdim, lwdim, swdim
2413 : integer :: rhvar, lwvar, swvar
2414 : integer :: abs_lw_var
2415 : integer :: ext_sw_var, ssa_sw_var, asm_sw_var
2416 : integer :: omdim, andim, namedim
2417 : integer :: omvar, anvar, namevar
2418 : integer :: dimids(2)
2419 : integer :: denvar, slogvar, dryrvar, rminvar, rmaxvar, hygrovar, ntmvar
2420 : real(kind=f) :: abs_lw(NMIE_RH, nlwbands)
2421 : real(kind=f) :: ext_sw(NMIE_RH, nswbands)
2422 : real(kind=f) :: ssa_sw(NMIE_RH, nswbands)
2423 : real(kind=f) :: asm_sw(NMIE_RH, nswbands)
2424 : character(len=8) :: c_name ! constituent name
2425 : character(len=32) :: aer_name ! long enough for both aername and name
2426 : character(len=255) :: filepath
2427 : real(kind=f) :: rwet
2428 : real(kind=f) :: Qext
2429 : real(kind=f) :: Qsca
2430 : real(kind=f) :: asym
2431 : integer :: start_text(2), count_text(2)
2432 : integer :: sw_r_refidx_var, sw_i_refidx_var, lw_r_refidx_var, lw_i_refidx_var
2433 : integer :: nrh
2434 : integer :: cnsttype ! constituent type
2435 : integer :: maxbin ! last prognostic bin
2436 : integer :: LUNOPRT ! logical unit number for output
2437 : logical :: do_print ! do print output?
2438 : integer :: ret
2439 :
2440 :
2441 : ! Assume success.
2442 0 : rc = 0
2443 :
2444 : ! Get the wavelength structure.
2445 0 : call CARMA_GET(carma, rc, wave=wave, do_print=do_print, LUNOPRT=LUNOPRT)
2446 0 : if (rc < 0) call endrun('carma_CreateOpticsFile::CARMA_Get failed.')
2447 :
2448 : ! Get the necessary group properties.
2449 : call CARMAGROUP_Get(carma, igroup, rc, do_mie=do_mie, name=name, shortname=shortname, r=r, &
2450 : rlow=rlow, rup=rup, rmass=rmass, irhswell=irhswell, &
2451 0 : ienconc=ienconc, cnsttype=cnsttype, maxbin=maxbin)
2452 0 : if (rc < 0) call endrun('carma_CreateOpticsFile::CARMAGROUP_Get failed.')
2453 :
2454 0 : call CARMAELEMENT_Get(carma, ienconc, rc, rho=rho, refidx=refidx)
2455 0 : if (rc < 0) call endrun('carma_CreateOpticsFile::CARMAELEMENT_Get failed.')
2456 :
2457 : ! A file needs to be created for each bin.
2458 0 : do ibin = 1, NBIN
2459 :
2460 : ! Bins past maxbin are treated as diagnostic even if the group
2461 : ! is prognostic and thus are not advected in the paerent model.
2462 0 : if (ibin <= maxbin) then
2463 :
2464 0 : write(c_name, '(A, I2.2)') trim(shortname), ibin
2465 :
2466 : ! Construct the path to the file. Each model will have its own subdirectory
2467 : ! where the optical property files are stored.
2468 0 : filepath = trim(carma_model) // '_' // trim(c_name) // '_rrtmg.nc'
2469 :
2470 0 : if (do_print) write(LUNOPRT,*) 'Creating CARMA optics file ... ', trim(filepath)
2471 :
2472 : ! Create the file.
2473 0 : call wrap_create(filepath, NF90_CLOBBER, fid)
2474 :
2475 : ! For non-hygroscopic, only use 1 RH value.
2476 0 : if (irhswell /= 0) then
2477 0 : nrh = NMIE_RH
2478 : else
2479 0 : nrh = min(NMIE_RH, 1)
2480 : end if
2481 :
2482 : ! Define the dimensions: rh, lwbands, swbands
2483 0 : call wrap_def_dim(fid, 'rh_idx', nrh, rhdim)
2484 0 : call wrap_def_dim(fid, 'lw_band', nlwbands, lwdim)
2485 0 : call wrap_def_dim(fid, 'sw_band', nswbands, swdim)
2486 :
2487 0 : write(LUNOPRT,*) "Defined rh_idx, lw_band, and sw_band dims."
2488 :
2489 0 : dimids(1) = rhdim
2490 0 : call wrap_def_var(fid, 'rh', NF90_DOUBLE, 1, dimids(1:1), rhvar)
2491 :
2492 0 : dimids(1) = lwdim
2493 0 : call wrap_def_var(fid, 'lw_band', NF90_DOUBLE, 1, dimids(1:1), lwvar)
2494 :
2495 0 : dimids(1) = swdim
2496 0 : call wrap_def_var(fid, 'sw_band', NF90_DOUBLE, 1, dimids(1:1), swvar)
2497 :
2498 0 : write(LUNOPRT,*) "Defined rh_idx, lw_band, and sw_band vars."
2499 :
2500 0 : call wrap_put_att_text(fid, rhvar, 'units', 'fraction')
2501 0 : call wrap_put_att_text(fid, lwvar, 'units', 'm')
2502 0 : call wrap_put_att_text(fid, swvar, 'units', 'm')
2503 :
2504 0 : call wrap_put_att_text(fid, rhvar, 'long_name', 'relative humidity')
2505 0 : call wrap_put_att_text(fid, lwvar, 'long_name', 'longwave bands')
2506 0 : call wrap_put_att_text(fid, swvar, 'long_name', 'shortwave bands')
2507 :
2508 : ! Define the variables: abs_lw, ext_sw, ssa_sw, asm_sw
2509 0 : dimids(1) = rhdim
2510 0 : dimids(2) = lwdim
2511 0 : call wrap_def_var(fid, 'abs_lw', NF90_DOUBLE, 2, dimids, abs_lw_var)
2512 :
2513 0 : write(LUNOPRT,*) "Defined abs_lw."
2514 :
2515 0 : call wrap_put_att_text(fid, abs_lw_var, 'units', 'meter^2 kilogram^-1')
2516 :
2517 0 : dimids(1) = rhdim
2518 0 : dimids(2) = swdim
2519 0 : call wrap_def_var(fid, 'ext_sw', NF90_DOUBLE, 2, dimids, ext_sw_var)
2520 0 : call wrap_def_var(fid, 'ssa_sw', NF90_DOUBLE, 2, dimids, ssa_sw_var)
2521 0 : call wrap_def_var(fid, 'asm_sw', NF90_DOUBLE, 2, dimids, asm_sw_var)
2522 :
2523 0 : write(LUNOPRT,*) "Defined ext_sw, ssa_sw, and asm_sw."
2524 :
2525 0 : call wrap_put_att_text(fid, ssa_sw_var, 'units', 'fraction')
2526 0 : call wrap_put_att_text(fid, ext_sw_var, 'units', 'meter^2 kilogram^-1')
2527 0 : call wrap_put_att_text(fid, asm_sw_var, 'units', '-')
2528 :
2529 : ! Define the variables for the refractive indicies.
2530 0 : dimids(1) = swdim
2531 0 : call wrap_def_var(fid, 'refindex_real_aer_sw', NF90_DOUBLE, 1, dimids(1:1), sw_r_refidx_var)
2532 0 : call wrap_def_var(fid, 'refindex_im_aer_sw', NF90_DOUBLE, 1, dimids(1:1), sw_i_refidx_var)
2533 :
2534 0 : write(LUNOPRT,*) "Defined lw refindex."
2535 :
2536 0 : dimids(1) = lwdim
2537 0 : call wrap_def_var(fid, 'refindex_real_aer_lw', NF90_DOUBLE, 1, dimids(1:1), lw_r_refidx_var)
2538 0 : call wrap_def_var(fid, 'refindex_im_aer_lw', NF90_DOUBLE, 1, dimids(1:1), lw_i_refidx_var)
2539 :
2540 0 : write(LUNOPRT,*) "Defined sw refindex."
2541 :
2542 0 : call wrap_put_att_text(fid, sw_r_refidx_var, 'units', '-')
2543 0 : call wrap_put_att_text(fid, sw_i_refidx_var, 'units', '-')
2544 0 : call wrap_put_att_text(fid, lw_r_refidx_var, 'units', '-')
2545 0 : call wrap_put_att_text(fid, lw_i_refidx_var, 'units', '-')
2546 :
2547 0 : call wrap_put_att_text(fid, sw_r_refidx_var, 'long_name', 'real refractive index of aerosol - shortwave')
2548 0 : call wrap_put_att_text(fid, sw_i_refidx_var, 'long_name', 'imaginary refractive index of aerosol - shortwave')
2549 0 : call wrap_put_att_text(fid, lw_r_refidx_var, 'long_name', 'real refractive index of aerosol - longwave')
2550 0 : call wrap_put_att_text(fid, lw_i_refidx_var, 'long_name', 'imaginary refractive index of aerosol - longwave')
2551 :
2552 :
2553 : ! Define fields that define the aerosol properties.
2554 0 : call wrap_def_dim(fid, 'opticsmethod_len', 32, omdim)
2555 0 : dimids(1) = omdim
2556 0 : call wrap_def_var(fid, 'opticsmethod', NF90_CHAR, 1, dimids(1:1), omvar)
2557 :
2558 0 : write(LUNOPRT,*) "Defined omdim."
2559 :
2560 0 : call wrap_def_dim(fid, 'namelength', 20, andim)
2561 0 : dimids(1) = andim
2562 0 : call wrap_def_var(fid, 'aername', NF90_CHAR, 1, dimids(1:1), anvar)
2563 :
2564 0 : write(LUNOPRT,*) "Defined aername."
2565 :
2566 0 : call wrap_def_dim(fid, 'name_len', 32, namedim)
2567 0 : dimids(1) = namedim
2568 0 : call wrap_def_var(fid, 'name', NF90_CHAR, 1, dimids(1:1), namevar)
2569 :
2570 0 : write(LUNOPRT,*) "Defined name."
2571 :
2572 0 : call wrap_def_var(fid, 'density', NF90_DOUBLE, 0, dimids(1:0), denvar)
2573 0 : call wrap_def_var(fid, 'sigma_logr', NF90_DOUBLE, 0, dimids(1:0), slogvar)
2574 0 : call wrap_def_var(fid, 'dryrad', NF90_DOUBLE, 0, dimids(1:0), dryrvar)
2575 0 : call wrap_def_var(fid, 'radmin_aer', NF90_DOUBLE, 0, dimids(1:0), rminvar)
2576 0 : call wrap_def_var(fid, 'radmax_aer', NF90_DOUBLE, 0, dimids(1:0), rmaxvar)
2577 0 : call wrap_def_var(fid, 'hygroscopicity', NF90_DOUBLE, 0, dimids(1:0), hygrovar)
2578 0 : call wrap_def_var(fid, 'num_to_mass_ratio', NF90_DOUBLE, 0, dimids(1:0), ntmvar)
2579 :
2580 0 : call wrap_put_att_text(fid, denvar, 'units', 'kg m^-3')
2581 0 : call wrap_put_att_text(fid, slogvar, 'units', '-')
2582 0 : call wrap_put_att_text(fid, dryrvar, 'units', 'm')
2583 0 : call wrap_put_att_text(fid, rminvar, 'units', 'm')
2584 0 : call wrap_put_att_text(fid, rmaxvar, 'units', 'm')
2585 0 : call wrap_put_att_text(fid, hygrovar, 'units', '-')
2586 0 : call wrap_put_att_text(fid, ntmvar, 'units', 'kg^-1')
2587 :
2588 0 : call wrap_put_att_text(fid, denvar, 'long_name', 'aerosol material density')
2589 0 : call wrap_put_att_text(fid, slogvar, 'long_name', 'geometric standard deviation of aerosol')
2590 0 : call wrap_put_att_text(fid, dryrvar, 'long_name', 'dry number mode radius of aerosol')
2591 0 : call wrap_put_att_text(fid, rminvar, 'long_name', 'minimum dry radius of aerosol for bin')
2592 0 : call wrap_put_att_text(fid, rmaxvar, 'long_name', 'maximum dry radius of aerosol for bin')
2593 0 : call wrap_put_att_text(fid, hygrovar, 'long_name', 'hygroscopicity of aerosol')
2594 0 : call wrap_put_att_text(fid, ntmvar, 'long_name', 'ratio of number to mass of aerosol')
2595 :
2596 :
2597 0 : write(LUNOPRT,*) "Defined all variables."
2598 :
2599 : ! End the defintion phase of the netcdf file.
2600 0 : call wrap_enddef(fid)
2601 :
2602 : ! Write out the dimensions.
2603 0 : call wrap_put_var_realx(fid, rhvar, mie_rh(:nrh))
2604 0 : call wrap_put_var_realx(fid, lwvar, wave(:nlwbands) * 1e-2_f)
2605 0 : call wrap_put_var_realx(fid, swvar, wave(nlwbands+1:) * 1e-2_f)
2606 :
2607 : ! Write out the refractive indicies.
2608 0 : call wrap_put_var_realx(fid, sw_r_refidx_var, real(refidx(nlwbands+1:, 1)))
2609 0 : call wrap_put_var_realx(fid, sw_i_refidx_var, aimag(refidx(nlwbands+1:, 1)))
2610 0 : call wrap_put_var_realx(fid, lw_r_refidx_var, real(refidx(:nlwbands, 1)))
2611 0 : call wrap_put_var_realx(fid, lw_i_refidx_var, aimag(refidx(:nlwbands, 1)))
2612 :
2613 :
2614 : ! Pad the names out with spaces.
2615 0 : aer_name = ' '
2616 0 : aer_name(1:len(trim(c_name))) = c_name
2617 :
2618 0 : start_text(1) = 1
2619 0 : count_text(1) = 32
2620 0 : call wrap_put_vara_text(fid, namevar, start_text, count_text, (/ aer_name /))
2621 0 : count_text(1) = 20
2622 0 : call wrap_put_vara_text(fid, anvar, start_text, count_text, (/ aer_name /))
2623 :
2624 : ! These fields control whether the particle is treated as a CCN. For now,
2625 : ! set these so that CARMA particles are not considered as CCN by the
2626 : ! CAM microphysics.
2627 0 : if (irhswell /= 0) then
2628 0 : count_text(1) = len('hygroscopic ')
2629 0 : call wrap_put_vara_text(fid, omvar, start_text, count_text, (/ 'hygroscopic ' /))
2630 : else
2631 0 : count_text(1) = len('insoluble ')
2632 0 : call wrap_put_vara_text(fid, omvar, start_text, count_text, (/ 'insoluble ' /))
2633 : end if
2634 :
2635 0 : call wrap_put_var_realx(fid, denvar, (/ rho(ibin) * 1e-3_f / 1e-6_f /))
2636 0 : call wrap_put_var_realx(fid, slogvar, (/ 0._f /))
2637 0 : call wrap_put_var_realx(fid, dryrvar, (/ r(ibin) * 1e-2_f /))
2638 0 : call wrap_put_var_realx(fid, rminvar, (/ rlow(ibin) * 1e-2_f /))
2639 0 : call wrap_put_var_realx(fid, rmaxvar, (/ rup(ibin) * 1e-2_f /))
2640 0 : call wrap_put_var_realx(fid, hygrovar, (/ 0._f /))
2641 0 : call wrap_put_var_realx(fid, ntmvar, (/ 1._f / rmass(ibin) / 1e-3_f /))
2642 :
2643 : ! Iterate over a range of relative humidities, since the particle may swell
2644 : ! with relative humidity which will change its optical properties.
2645 0 : do irh = 1, nrh
2646 :
2647 : ! Determine the wet radius.
2648 0 : call getwetr(carma, igroup, mie_rh(irh), r(ibin), rwet, rho(ibin), rhopwet, rc)
2649 0 : if (rc < 0) call endrun('carma_CreateOpticsFile::wetr failed.')
2650 :
2651 : ! Calculate at each wavelength.
2652 0 : do iwave = 1, NWAVE
2653 :
2654 : ! Using Mie code, calculate the optical properties: extinction coefficient,
2655 : ! single scattering albedo and asymmetry factor.
2656 : ! Assume the particle is homogeneous (no core).
2657 : !
2658 : ! NOTE: nmon, df, rmon and falpha are only used for fractal particles.
2659 : call mie(carma, &
2660 0 : carma%f_group(igroup)%f_imiertn, &
2661 : rwet, &
2662 0 : carma%f_wave(iwave), &
2663 0 : real(carma%f_group(igroup)%f_nmon(ibin),kind=f), &
2664 0 : carma%f_group(igroup)%f_df(ibin), &
2665 : carma%f_group(igroup)%f_rmon, &
2666 : carma%f_group(igroup)%f_falpha, &
2667 0 : refidx(iwave, 1), &
2668 : 0.0_f, &
2669 : refidx(iwave, 1), &
2670 : Qext, &
2671 : Qsca, &
2672 : asym, &
2673 0 : rc)
2674 0 : if (rc < 0) call endrun('carma_CreateOpticsFile::mie failed.')
2675 :
2676 : ! Calculate the shortwave and longwave properties?
2677 : !
2678 : ! NOTE: miess is in cgs units, but the optics file needs to be in mks
2679 : ! units, so perform the necessary conversions.
2680 0 : if (iwave <= nlwbands) then
2681 :
2682 : ! Longwave just needs absorption: abs_lw.
2683 0 : abs_lw(irh, iwave) = (Qext - Qsca) * PI * (rwet * 1e-2_f)**2 / (rmass(ibin) * 1e-3_f)
2684 : else
2685 :
2686 : ! Shortwave needs extinction, single scattering albedo and asymmetry factor:
2687 : ! ext_sw, ssa_sw and asm_sw.
2688 0 : ext_sw(irh, iwave - nlwbands) = Qext * PI * (rwet * 1e-2_f)**2 / (rmass(ibin) * 1e-3_f)
2689 0 : ssa_sw(irh, iwave - nlwbands) = Qsca / Qext
2690 0 : asm_sw(irh, iwave - nlwbands) = asym
2691 : end if
2692 : end do
2693 : end do
2694 :
2695 : ! Write out the longwave fields.
2696 0 : ret = nf90_put_var (fid, abs_lw_var, abs_lw(:nrh, :))
2697 0 : if (ret/=NF90_NOERR) then
2698 0 : write(iulog,*)'CARMA_CreateOpticsFile: error writing varid =', abs_lw_var
2699 0 : call handle_error (ret)
2700 : end if
2701 :
2702 : ! Write out the shortwave fields.
2703 0 : ret = nf90_put_var (fid, ext_sw_var, ext_sw(:nrh, :))
2704 0 : if (ret/=NF90_NOERR) then
2705 0 : write(iulog,*)'CARMA_CreateOpticsFile: error writing varid =', ext_sw_var
2706 0 : call handle_error (ret)
2707 : end if
2708 0 : ret = nf90_put_var (fid, ssa_sw_var, ssa_sw(:nrh, :))
2709 0 : if (ret/=NF90_NOERR) then
2710 0 : write(iulog,*)'CARMA_CreateOpticsFile: error writing varid =', ssa_sw_var
2711 0 : call handle_error (ret)
2712 : end if
2713 0 : ret = nf90_put_var (fid, asm_sw_var, asm_sw(:nrh, :))
2714 0 : if (ret/=NF90_NOERR) then
2715 0 : write(iulog,*)'CARMA_CreateOpticsFile: error writing varid =', asm_sw_var
2716 0 : call handle_error (ret)
2717 : end if
2718 :
2719 : ! Close the file.
2720 0 : call wrap_close(fid)
2721 : end if
2722 : end do
2723 :
2724 0 : return
2725 : end subroutine CARMA_CreateOpticsFile_Fixed
2726 :
2727 :
2728 :
2729 : !! This routine creates files containing optical properties for the pure sulfate group
2730 : !! following Yu et al. (2015). These optical properties are used by the RRTMG radiation
2731 : !! code to include the impact of CARMA particles in the radiative transfer
2732 : !! calculation.
2733 0 : subroutine CARMA_CreateOpticsFile_Sulfate(carma, igroup, rc)
2734 : use wrap_nf
2735 : use wetr, only : getwetr
2736 :
2737 : implicit none
2738 :
2739 : type(carma_type), intent(inout) :: carma !! the carma object
2740 : integer, intent(in) :: igroup !! group index
2741 : integer, intent(out) :: rc !! return code, negative indicates failure
2742 :
2743 : ! Local variables
2744 : integer :: ibin, iwave, iwtp
2745 : integer :: irhswell
2746 : integer :: imiertn
2747 : integer :: ienconc
2748 : real(kind=f) :: rho(NBIN), rhopwet
2749 : real(kind=f) :: r(NBIN), rmass(NBIN), rlow(NBIN), rup(NBIN)
2750 : real(kind=f) :: wave(NWAVE)
2751 : complex(kind=f) :: refidx(NWAVE)
2752 : complex(kind=f) :: refidxS(NWAVE, NREFIDX)
2753 : complex(kind=f) :: refidxW(NWAVE)
2754 : character(len=CARMA_NAME_LEN) :: name
2755 : character(len=CARMA_SHORT_NAME_LEN) :: shortname
2756 : integer :: fid
2757 : integer :: rhdim, lwdim, swdim, wtpdim
2758 : integer :: rhvar, lwvar, swvar, wtp_var
2759 : integer :: rwetvar
2760 : integer :: abs_lw_wtp_var, qabs_lw_wtp_var
2761 : integer :: ext_sw_wtp_var, ssa_sw_wtp_var, asm_sw_wtp_var, qext_sw_wtp_var
2762 : integer :: omdim, andim, namedim
2763 : integer :: omvar, anvar, namevar
2764 : integer :: dimids(2)
2765 : integer :: denvar, slogvar, dryrvar, rminvar, rmaxvar, hygrovar, ntmvar
2766 : real(kind=f) :: abs_lw_wtp(NMIE_WTP, nlwbands)
2767 : real(kind=f) :: qabs_lw_wtp(NMIE_WTP, nlwbands)
2768 : real(kind=f) :: ext_sw_wtp(NMIE_WTP, nswbands)
2769 : real(kind=f) :: qext_sw_wtp(NMIE_WTP, nswbands)
2770 : real(kind=f) :: ssa_sw_wtp(NMIE_WTP, nswbands)
2771 : real(kind=f) :: asm_sw_wtp(NMIE_WTP, nswbands)
2772 : character(len=8) :: c_name ! constituent name
2773 : character(len=32) :: aer_name ! long enough for both aername and name
2774 : character(len=255) :: filepath
2775 : real(kind=f) :: rwet
2776 : real(kind=f) :: Qext
2777 : real(kind=f) :: Qsca
2778 : real(kind=f) :: asym
2779 : integer :: start_text(2), count_text(2)
2780 : integer :: sw_r_refidx_var, sw_i_refidx_var, lw_r_refidx_var, lw_i_refidx_var
2781 : integer :: cnsttype ! constituent type
2782 : integer :: maxbin ! last prognostic bin
2783 : integer :: LUNOPRT ! logical unit number for output
2784 : logical :: do_print ! do print output?
2785 : integer :: ret
2786 : real(kind=f) :: volwater
2787 : real(kind=f) :: volsulfate
2788 : real(kind=f) :: volshell
2789 : integer :: igash2o
2790 :
2791 :
2792 : ! Assume success.
2793 0 : rc = 0
2794 :
2795 : ! Get the wavelength structure.
2796 0 : call CARMA_GET(carma, rc, wave=wave, do_print=do_print, LUNOPRT=LUNOPRT, igash2o=igash2o)
2797 0 : if (rc < 0) call endrun('carma_CreateOpticsFile::CARMA_Get failed.')
2798 :
2799 : ! Get the necessary group properties.
2800 : call CARMAGROUP_Get(carma, igroup, rc, name=name, shortname=shortname, r=r, &
2801 : rlow=rlow, rup=rup, rmass=rmass, irhswell=irhswell, &
2802 0 : ienconc=ienconc, cnsttype=cnsttype, maxbin=maxbin, imiertn=imiertn)
2803 0 : if (rc < 0) call endrun('carma_CreateOpticsFile::CARMAGROUP_Get failed.')
2804 :
2805 : ! Get the necessary element properties.
2806 0 : call CARMAELEMENT_Get(carma, ienconc, rc, rho=rho, refidx=refidxS)
2807 0 : if (rc < 0) call endrun('carma_CreateOpticsFile::CARMAELEMENT_Get failed.')
2808 :
2809 : ! Get the refractive index for water.
2810 0 : call CARMAGAS_Get(carma, igash2o, rc, refidx=refidxW)
2811 0 : if (rc < 0) call endrun('carma_CreateOpticsFile::CARMAGAS_Get failed.')
2812 :
2813 : ! A file needs to be created for each bin.
2814 0 : do ibin = 1, NBIN
2815 :
2816 : ! Bins past maxbin are treated as diagnostic even if the group
2817 : ! is prognostic and thus are not advected in the paerent model.
2818 0 : if (ibin <= maxbin) then
2819 :
2820 0 : write(c_name, '(A, I2.2)') trim(shortname), ibin
2821 :
2822 : ! Construct the path to the file. Each model will have its own subdirectory
2823 : ! where the optical property files are stored.
2824 0 : filepath = trim(carma_model) // '_' // trim(c_name) // '_rrtmg.nc'
2825 :
2826 0 : if (do_print) write(LUNOPRT,*) 'Creating CARMA optics file ... ', trim(filepath)
2827 :
2828 : ! Create the file.
2829 0 : call wrap_create(filepath, NF90_CLOBBER, fid)
2830 :
2831 : ! Define the dimensions: rh, lwbands, swbands
2832 0 : call wrap_def_dim(fid, 'rh_idx', NMIE_RH, rhdim)
2833 0 : call wrap_def_dim(fid, 'lw_band', nlwbands, lwdim)
2834 0 : call wrap_def_dim(fid, 'sw_band', nswbands, swdim)
2835 :
2836 0 : call wrap_def_dim(fid, 'wgtpct', NMIE_WTP, wtpdim)
2837 :
2838 0 : dimids(1) = rhdim
2839 0 : call wrap_def_var(fid, 'rh', NF90_DOUBLE, 1, dimids(1), rhvar)
2840 0 : call wrap_def_var(fid, 'rwet',NF90_DOUBLE, 1, dimids(1), rwetvar)
2841 :
2842 0 : dimids(1) = lwdim
2843 0 : call wrap_def_var(fid, 'lw_band', NF90_DOUBLE, 1, dimids(1), lwvar)
2844 :
2845 0 : dimids(1) = swdim
2846 0 : call wrap_def_var(fid, 'sw_band', NF90_DOUBLE, 1, dimids(1), swvar)
2847 :
2848 0 : dimids(1) = wtpdim
2849 0 : call wrap_def_var(fid, 'wgtpct', NF90_DOUBLE, 1, dimids(1), wtp_var)
2850 :
2851 0 : call wrap_put_att_text(fid, rhvar, 'units', 'fraction')
2852 0 : call wrap_put_att_text(fid, rwetvar, 'units', 'cm')
2853 0 : call wrap_put_att_text(fid, lwvar, 'units', 'm')
2854 0 : call wrap_put_att_text(fid, swvar, 'units', 'm')
2855 :
2856 0 : call wrap_put_att_text(fid, wtp_var,'units', 'unitless')
2857 0 : call wrap_put_att_text(fid, wtp_var,'long_name', 'weight percent')
2858 :
2859 0 : call wrap_put_att_text(fid, rhvar, 'long_name', 'relative humidity')
2860 0 : call wrap_put_att_text(fid, rwetvar, 'long_name', 'wet radius')
2861 0 : call wrap_put_att_text(fid, lwvar, 'long_name', 'longwave bands')
2862 0 : call wrap_put_att_text(fid, swvar, 'long_name', 'shortwave bands')
2863 :
2864 : ! Define the variables: abs_lw, ext_sw, ssa_sw, asm_sw
2865 : ! Define 2-dimension (:nrh,:nswbands) LW optics properties: abs_lw, qabs_lw
2866 0 : dimids(1) = wtpdim
2867 0 : dimids(2) = lwdim
2868 0 : call wrap_def_var(fid, 'abs_lw_wtp', NF90_DOUBLE, 2, dimids(1:2), abs_lw_wtp_var)
2869 0 : call wrap_def_var(fid, 'qabs_lw_wtp',NF90_DOUBLE, 2, dimids(1:2), qabs_lw_wtp_var)
2870 :
2871 0 : call wrap_put_att_text(fid, abs_lw_wtp_var, 'units', 'meter^2 kilogram^-1')
2872 0 : call wrap_put_att_text(fid, qabs_lw_wtp_var,'units', '-')
2873 :
2874 : ! Define 2-dimension (:nrh,:nswbands) optics properties: ext_sw, qext_sw, ssa_sw, asm_sw
2875 0 : dimids(1) = wtpdim
2876 0 : dimids(2) = swdim
2877 0 : call wrap_def_var(fid, 'ext_sw_wtp', NF90_DOUBLE, 2, dimids(1:2), ext_sw_wtp_var)
2878 0 : call wrap_def_var(fid, 'qext_sw_wtp',NF90_DOUBLE, 2, dimids(1:2), qext_sw_wtp_var)
2879 0 : call wrap_def_var(fid, 'ssa_sw_wtp', NF90_DOUBLE, 2, dimids(1:2), ssa_sw_wtp_var)
2880 0 : call wrap_def_var(fid, 'asm_sw_wtp', NF90_DOUBLE, 2, dimids(1:2), asm_sw_wtp_var)
2881 :
2882 0 : call wrap_put_att_text(fid, ssa_sw_wtp_var, 'units', 'fraction')
2883 0 : call wrap_put_att_text(fid, qext_sw_wtp_var,'units', '-')
2884 0 : call wrap_put_att_text(fid, ext_sw_wtp_var, 'units', 'meter^2 kilogram^-1')
2885 0 : call wrap_put_att_text(fid, asm_sw_wtp_var, 'units', '-')
2886 :
2887 : ! Define the variables for the refractive indicies.
2888 0 : dimids(1) = swdim
2889 0 : call wrap_def_var(fid, 'refindex_real_aer_sw', NF90_DOUBLE, 1, dimids(1), sw_r_refidx_var)
2890 0 : call wrap_def_var(fid, 'refindex_im_aer_sw', NF90_DOUBLE, 1, dimids(1), sw_i_refidx_var)
2891 :
2892 0 : dimids(1) = lwdim
2893 0 : call wrap_def_var(fid, 'refindex_real_aer_lw', NF90_DOUBLE, 1, dimids(1), lw_r_refidx_var)
2894 0 : call wrap_def_var(fid, 'refindex_im_aer_lw', NF90_DOUBLE, 1, dimids(1), lw_i_refidx_var)
2895 :
2896 0 : call wrap_put_att_text(fid, sw_r_refidx_var, 'units', '-')
2897 0 : call wrap_put_att_text(fid, sw_i_refidx_var, 'units', '-')
2898 0 : call wrap_put_att_text(fid, lw_r_refidx_var, 'units', '-')
2899 0 : call wrap_put_att_text(fid, lw_i_refidx_var, 'units', '-')
2900 :
2901 0 : call wrap_put_att_text(fid, sw_r_refidx_var, 'long_name', 'real refractive index of aerosol - shortwave')
2902 0 : call wrap_put_att_text(fid, sw_i_refidx_var, 'long_name', 'imaginary refractive index of aerosol - shortwave')
2903 0 : call wrap_put_att_text(fid, lw_r_refidx_var, 'long_name', 'real refractive index of aerosol - longwave')
2904 0 : call wrap_put_att_text(fid, lw_i_refidx_var, 'long_name', 'imaginary refractive index of aerosol - longwave')
2905 :
2906 :
2907 : ! Define fields that define the aerosol properties.
2908 0 : call wrap_def_dim(fid, 'opticsmethod_len', 32, omdim)
2909 0 : dimids(1) = omdim
2910 0 : call wrap_def_var(fid, 'opticsmethod', NF90_CHAR, 1, dimids(1), omvar)
2911 :
2912 0 : call wrap_def_dim(fid, 'namelength', 20, andim)
2913 0 : dimids(1) = andim
2914 0 : call wrap_def_var(fid, 'aername', NF90_CHAR, 1, dimids(1), anvar)
2915 :
2916 0 : call wrap_def_dim(fid, 'name_len', 32, namedim)
2917 0 : dimids(1) = namedim
2918 0 : call wrap_def_var(fid, 'name', NF90_CHAR, 1, dimids, namevar)
2919 :
2920 0 : call wrap_def_var(fid, 'density', NF90_DOUBLE, 0, dimids(1), denvar)
2921 0 : call wrap_def_var(fid, 'sigma_logr', NF90_DOUBLE, 0, dimids(1), slogvar)
2922 0 : call wrap_def_var(fid, 'dryrad', NF90_DOUBLE, 0, dimids(1), dryrvar)
2923 0 : call wrap_def_var(fid, 'radmin_aer', NF90_DOUBLE, 0, dimids(1), rminvar)
2924 0 : call wrap_def_var(fid, 'radmax_aer', NF90_DOUBLE, 0, dimids(1), rmaxvar)
2925 0 : call wrap_def_var(fid, 'hygroscopicity', NF90_DOUBLE, 0, dimids(1), hygrovar)
2926 0 : call wrap_def_var(fid, 'num_to_mass_ratio', NF90_DOUBLE, 0, dimids(1), ntmvar)
2927 :
2928 0 : call wrap_put_att_text(fid, denvar, 'units', 'kg m^-3')
2929 0 : call wrap_put_att_text(fid, slogvar, 'units', '-')
2930 0 : call wrap_put_att_text(fid, dryrvar, 'units', 'm')
2931 0 : call wrap_put_att_text(fid, rminvar, 'units', 'm')
2932 0 : call wrap_put_att_text(fid, rmaxvar, 'units', 'm')
2933 0 : call wrap_put_att_text(fid, hygrovar, 'units', '-')
2934 0 : call wrap_put_att_text(fid, ntmvar, 'units', 'kg^-1')
2935 :
2936 0 : call wrap_put_att_text(fid, denvar, 'long_name', 'aerosol material density')
2937 0 : call wrap_put_att_text(fid, slogvar, 'long_name', 'geometric standard deviation of aerosol')
2938 0 : call wrap_put_att_text(fid, dryrvar, 'long_name', 'dry number mode radius of aerosol')
2939 0 : call wrap_put_att_text(fid, rminvar, 'long_name', 'minimum dry radius of aerosol for bin')
2940 0 : call wrap_put_att_text(fid, rmaxvar, 'long_name', 'maximum dry radius of aerosol for bin')
2941 0 : call wrap_put_att_text(fid, hygrovar, 'long_name', 'hygroscopicity of aerosol')
2942 0 : call wrap_put_att_text(fid, ntmvar, 'long_name', 'ratio of number to mass of aerosol')
2943 :
2944 : ! End the defintion phase of the netcdf file.
2945 0 : call wrap_enddef(fid)
2946 :
2947 : ! Write out the dimensions.
2948 0 : call wrap_put_var_realx(fid, rhvar, mie_rh(:))
2949 0 : call wrap_put_var_realx(fid, lwvar, wave(:nlwbands) * 1e-2_f)
2950 0 : call wrap_put_var_realx(fid, swvar, wave(nlwbands+1:) * 1e-2_f)
2951 :
2952 0 : call wrap_put_var_realx(fid, wtp_var, mie_wtp(:)*100._f)
2953 :
2954 : ! Write out the refractive indicies.
2955 0 : call wrap_put_var_realx(fid, sw_r_refidx_var, real(refidxS(nlwbands+1:, 1)))
2956 0 : call wrap_put_var_realx(fid, sw_i_refidx_var, aimag(refidxS(nlwbands+1:, 1)))
2957 0 : call wrap_put_var_realx(fid, lw_r_refidx_var, real(refidxS(:nlwbands, 1)))
2958 0 : call wrap_put_var_realx(fid, lw_i_refidx_var, aimag(refidxS(:nlwbands, 1)))
2959 :
2960 : ! Pad the names out with spaces.
2961 0 : aer_name = ' '
2962 0 : aer_name(1:len(trim(c_name))) = c_name
2963 :
2964 0 : start_text(1) = 1
2965 0 : count_text(1) = 32
2966 0 : call wrap_put_vara_text(fid, namevar, start_text, count_text, (/ aer_name /))
2967 0 : count_text(1) = 20
2968 0 : call wrap_put_vara_text(fid, anvar, start_text, count_text, (/ aer_name /))
2969 :
2970 0 : count_text(1) = len('hygroscopic_wtp ')
2971 0 : call wrap_put_vara_text(fid, omvar, start_text, count_text, (/ 'hygroscopic_wtp ' /))
2972 :
2973 0 : call wrap_put_var_realx(fid, denvar, (/ rho(ibin) * 1e-3_f / 1e-6_f /))
2974 0 : call wrap_put_var_realx(fid, slogvar, (/ 0._f /))
2975 0 : call wrap_put_var_realx(fid, dryrvar, (/ r(ibin) * 1e-2_f /))
2976 0 : call wrap_put_var_realx(fid, rminvar, (/ rlow(ibin) * 1e-2_f /))
2977 0 : call wrap_put_var_realx(fid, rmaxvar, (/ rup(ibin) * 1e-2_f /))
2978 0 : call wrap_put_var_realx(fid, hygrovar, (/ 0.6_f /))
2979 0 : call wrap_put_var_realx(fid, ntmvar, (/ 1._f / rmass(ibin) / 1e-3_f /))
2980 :
2981 : ! For now, ext_sw(:nrh, :nswbands) and ext_sw_coreshell(:nrh, :nswbands, :ncoreshellratio) both are calculated
2982 : ! Since other aerosols in CAM may use ext_sw rather than ext_sw_coreshell
2983 : ! Modified by Pengfei Yu
2984 : ! April.1, 2012
2985 :
2986 : ! calculate qext and ext for pure sulfate dependent on weight percent
2987 : ! ideally qext is based on (wgt,temp,wave), however Beyer et al. (1996) Figure 5
2988 : ! shows sulfate density is roughly 0.006 g/cm3/k, I negelet temp dimension, assuming temp = 270 K
2989 : ! In code, sulfate density is precisely calculated to determine wet raidus
2990 0 : do iwtp = 1, NMIE_WTP
2991 :
2992 : ! NOTE: Weight percent is normal a result of the getwetr calculation. To build the
2993 : ! table based upon weight percent, we need to pass in the desired value and a
2994 : ! reference temperature. In that case, the RH is ignored.
2995 0 : call getwetr(carma, igroup, mie_rh(1), r(ibin), rwet, rho(ibin), rhopwet, rc, wgtpct=mie_wtp(iwtp)*100._f, temp=270._f)
2996 0 : if (rc < 0) call endrun('carma_CreateOpticsFile::wetr failed.')
2997 :
2998 : ! This is not in Yu (2015), but rather than using the refractive
2999 : ! index of H2SO4 for the shell, do a volume mix of water and H2SO4
3000 : ! for the refractive index of the shell.
3001 0 : volwater = rwet**3._f - r(ibin)**3._f
3002 0 : volsulfate = r(ibin)**3._f
3003 0 : volshell = volwater + volsulfate
3004 0 : if (volshell > 0._f) then
3005 0 : refidx(:) = (volwater / volshell) * refidxW(:) + (volsulfate / volshell) * refidxS(:, 1)
3006 : else
3007 0 : refidx(:) = refidxS(:, 1)
3008 : end if
3009 :
3010 : ! Calculate at each wavelength.
3011 0 : do iwave = 1, NWAVE
3012 :
3013 : ! Using Mie code, calculate the optical properties: extinction coefficient,
3014 : ! single scattering albedo and asymmetry factor.
3015 : ! Assume the particle is homogeneous (no core).
3016 : !
3017 : ! NOTE: The refractive index for sulfate changes with RH/weight percent, which
3018 : ! is not reflected in this code.
3019 : call mie(carma, &
3020 : imiertn, &
3021 : rwet, &
3022 0 : wave(iwave), &
3023 : 0._f, &
3024 : 3.0_f, &
3025 : 0.0_f, &
3026 : 1.0_f, &
3027 : refidx(iwave), &
3028 : 0.0_f, &
3029 : refidx(iwave), &
3030 : Qext, &
3031 : Qsca, &
3032 : asym, &
3033 0 : rc)
3034 0 : if (rc < 0) call endrun('carma_CreateOpticsFile::mie failed.')
3035 :
3036 : ! Calculate the shortwave and longwave properties?
3037 : !
3038 : ! NOTE: miess is in cgs units, but the optics file needs to be in mks
3039 : ! units, so perform the necessary conversions.
3040 0 : if (iwave <= nlwbands) then
3041 :
3042 : ! Longwave just needs absorption: abs_lw.
3043 0 : qabs_lw_wtp(iwtp, iwave) = (Qext - Qsca) ! absorption per particle
3044 0 : abs_lw_wtp (iwtp, iwave) = (Qext - Qsca) * PI * (rwet * 1e-2_f)**2 / (rmass(ibin) * 1e-3_f)
3045 : else
3046 :
3047 : ! Shortwave needs extinction, single scattering albedo and asymmetry factor:
3048 : ! ext_sw, ssa_sw and asm_sw.
3049 0 : qext_sw_wtp(iwtp, iwave - nlwbands) = Qext ! extinction per particle
3050 0 : ext_sw_wtp (iwtp, iwave - nlwbands) = Qext * PI * (rwet * 1e-2_f)**2 / (rmass(ibin) * 1e-3_f)
3051 0 : ssa_sw_wtp (iwtp, iwave - nlwbands) = Qsca / Qext
3052 0 : asm_sw_wtp (iwtp, iwave - nlwbands) = asym
3053 : end if
3054 : end do ! iwave
3055 : end do ! iwtp
3056 :
3057 : ! Write out the longwave fields.
3058 0 : ret = nf90_put_var(fid, abs_lw_wtp_var, abs_lw_wtp (:, :))
3059 0 : if (ret /= NF90_NOERR) then
3060 0 : write(iulog,*)'CARMA_CreateOpticsFile_SulfateYu: error writing varid =', fid, abs_lw_wtp_var
3061 0 : call handle_error(ret)
3062 : end if
3063 :
3064 0 : ret = nf90_put_var(fid, qabs_lw_wtp_var, qabs_lw_wtp(:, :))
3065 0 : if (ret /= NF90_NOERR) then
3066 0 : write(iulog,*)'CARMA_CreateOpticsFile_SulfateYu: error writing varid =', qabs_lw_wtp_var
3067 0 : call handle_error(ret)
3068 : end if
3069 :
3070 : ! Write out the shortwave fields.
3071 0 : ret = nf90_put_var(fid, ext_sw_wtp_var, ext_sw_wtp (:, :))
3072 0 : if (ret /= NF90_NOERR) then
3073 0 : write(iulog,*)'CARMA_CreateOpticsFile_SulfateYu: error writing varid =', ext_sw_wtp_var
3074 0 : call handle_error(ret)
3075 : end if
3076 :
3077 0 : ret = nf90_put_var(fid, qext_sw_wtp_var,qext_sw_wtp(:, :))
3078 0 : if (ret /= NF90_NOERR) then
3079 0 : write(iulog,*)'CARMA_CreateOpticsFile_SulfateYu: error writing varid =', qext_sw_wtp_var
3080 0 : call handle_error(ret)
3081 : end if
3082 :
3083 0 : ret = nf90_put_var(fid, ssa_sw_wtp_var, ssa_sw_wtp (:, :))
3084 0 : if (ret /= NF90_NOERR) then
3085 0 : write(iulog,*)'CARMA_CreateOpticsFile_SulfateYu: error writing varid =', ssa_sw_wtp_var
3086 0 : call handle_error(ret)
3087 : end if
3088 :
3089 0 : ret = nf90_put_var(fid, asm_sw_wtp_var, asm_sw_wtp (:, :))
3090 0 : if (ret /= NF90_NOERR) then
3091 0 : write(iulog,*)'CARMA_CreateOpticsFile_SulfateYu: error writing varid =', asm_sw_wtp_var
3092 0 : call handle_error(ret)
3093 : end if
3094 :
3095 : ! Close the file.
3096 0 : call wrap_close(fid)
3097 : end if
3098 : end do
3099 :
3100 0 : return
3101 : end subroutine CARMA_CreateOpticsFile_Sulfate
3102 :
3103 :
3104 : !! Calculate the aerodynamic resistance for dry deposition.
3105 : !!
3106 : !! This is based upon Seinfeld and Pandis (1998) page 963, and
3107 : !! is similar to the calcram routine in drydep_mod.F90;
3108 : !! however, it enables independent determination of the aerodynamic
3109 : !! resistance each surface type (land, ocean and ice).
3110 : !!
3111 : !! @author Tianyi Fan
3112 : !! @version Aug 2011
3113 852463 : subroutine CARMA_calcram(ustar, z0, pdel, pmid, tmid, obklen, ram)
3114 : use shr_const_mod, only: shr_const_karman
3115 : use physconst, only: rair, gravit
3116 :
3117 : implicit none
3118 :
3119 : ! input and output argument
3120 : real(r8), intent(in) :: ustar ! friction velocity
3121 : real(r8), intent(in) :: z0 ! roughness length
3122 : real(r8), intent(in) :: pdel ! layer depth in pressure [Pa]
3123 : real(r8), intent(in) :: pmid ! layer mid-point pressure [Pa]
3124 : real(r8), intent(in) :: tmid ! layer mid-point temperature [K]
3125 : real(r8), intent(in) :: obklen ! Monin-Obukhov length
3126 : real(r8), intent(out) :: ram ! aerodynamic resistance
3127 :
3128 : ! local varibles
3129 : real(r8) :: z ! half the layer height
3130 : real(r8) :: psi ! stability parameter for z
3131 : real(r8) :: psi0 ! stability parameter for z0
3132 : real(r8) :: nu ! temparory variable
3133 : real(r8) :: nu0 ! temparory variable
3134 : real(r8), parameter :: xkar = shr_const_karman
3135 :
3136 :
3137 : ! Use half the layer height like Ganzefeld and Lelieveld, 1995
3138 852463 : z = pdel * rair * tmid / pmid / gravit / 2._r8
3139 :
3140 852463 : if (obklen .eq. 0._r8) then
3141 : psi = 0._r8
3142 : psi0 = 0._r8
3143 : else
3144 852463 : psi = min(max(z / obklen, -1._r8), 1._r8)
3145 852463 : psi0 = min(max(z0 / obklen, -1._r8), 1._r8)
3146 : endif
3147 :
3148 : ! Stable
3149 852463 : if (psi > 0._r8) then
3150 172628 : ram = 1._r8 / xkar / ustar * (log(z / z0) + 4.7_r8 * (psi - psi0))
3151 :
3152 : ! Unstable
3153 679835 : else if (psi < 0._r8) then
3154 679835 : nu = (1._r8 - 15._r8 *psi)**(.25_r8)
3155 679835 : nu0 = (1._r8 - 15._r8 *psi0)**(.25_r8)
3156 :
3157 679835 : if (ustar /= 0._r8) then
3158 : ram = 1._r8 / xkar / ustar * (log(z / z0) + &
3159 : log(((nu0**2 + 1._r8) * (nu0 + 1._r8)**2) / &
3160 : ((nu**2 + 1._r8) * (nu + 1._r8)**2)) + &
3161 679835 : 2._r8 * (atan(nu) - atan(nu0)))
3162 : else
3163 0 : ram = 0._r8
3164 : end if
3165 :
3166 : ! Neutral
3167 : else
3168 0 : ram = 1._r8 / xkar / ustar * log(z / z0)
3169 : end if
3170 :
3171 852463 : return
3172 : end subroutine CARMA_calcram
3173 :
3174 : !---------------------------------------------------------------------------
3175 : ! define fields for reference profiles in cam restart file
3176 : !---------------------------------------------------------------------------
3177 1536 : subroutine CARMA_restart_init( File )
3178 : use cam_pio_utils, only: cam_pio_def_dim
3179 : use pio, only: file_desc_t, pio_def_var, pio_double
3180 :
3181 : ! arguments
3182 : type(file_desc_t),intent(inout) :: File ! pio File pointer
3183 :
3184 : ! local variables
3185 : integer :: levid, ierr
3186 :
3187 1536 : if (carma_do_fixedinit) then
3188 0 : call cam_pio_def_dim(File, 'lev', pver, levid, existOK=.true.)
3189 0 : ierr = pio_def_var(File, 'CARMA_REF_T', pio_double, (/ levid /), t_ref_desc)
3190 0 : ierr = pio_def_var(File, 'CARMA_REF_H2O', pio_double, (/ levid /), h2o_ref_desc)
3191 0 : ierr = pio_def_var(File, 'CARMA_REF_H2SO4', pio_double, (/ levid /), h2so4_ref_desc)
3192 : endif
3193 :
3194 1536 : end subroutine CARMA_restart_init
3195 :
3196 : !---------------------------------------------------------------------------
3197 : ! write reference profiles to restart file
3198 : !---------------------------------------------------------------------------
3199 1536 : subroutine CARMA_restart_write(File)
3200 1536 : use pio, only: file_desc_t, pio_put_var
3201 :
3202 : ! arguments
3203 : type(file_desc_t), intent(inout) :: File
3204 :
3205 : ! local variables
3206 : integer ::ierr
3207 :
3208 1536 : if (carma_do_fixedinit) then
3209 0 : ierr = pio_put_var(File, t_ref_desc, carma_t_ref)
3210 0 : if (carma%f_igash2o /= 0) then
3211 0 : ierr = pio_put_var(File, h2o_ref_desc, carma_h2o_ref)
3212 : endif
3213 0 : if (carma%f_igash2So4 /= 0) then
3214 0 : ierr = pio_put_var(File, h2so4_ref_desc, carma_h2so4_ref)
3215 : endif
3216 : endif
3217 :
3218 1536 : end subroutine CARMA_restart_write
3219 :
3220 : !---------------------------------------------------------------------------
3221 : ! read reference profiles from restart file
3222 : !---------------------------------------------------------------------------
3223 768 : subroutine CARMA_restart_read(File)
3224 : use pio, only: file_desc_t, pio_inq_varid, pio_get_var
3225 :
3226 : ! arguments
3227 : type(file_desc_t),intent(inout) :: File ! pio File pointer
3228 :
3229 : ! local variables
3230 : integer :: ierr, varid
3231 : character(len=*), parameter :: subname = 'CARMA_restart_read: '
3232 :
3233 768 : if (carma_do_fixedinit) then
3234 0 : ierr = pio_inq_varid(File, 'CARMA_REF_T', varid)
3235 0 : if (varid>0) then
3236 0 : ierr = pio_get_var(File, varid, carma_t_ref)
3237 : else
3238 0 : call endrun(subname//'restart file must include CARMA_REF_T')
3239 : endif
3240 0 : ierr = pio_inq_varid(File, 'CARMA_REF_H2O', varid)
3241 0 : if (varid>0) then
3242 0 : ierr = pio_get_var(File, varid, carma_h2o_ref)
3243 0 : else if (carma%f_igash2o /= 0) then
3244 0 : call endrun(subname//'restart file must include CARMA_REF_H2O')
3245 : endif
3246 0 : ierr = pio_inq_varid(File, 'CARMA_REF_H2SO4', varid)
3247 0 : if (varid>0) then
3248 0 : ierr = pio_get_var(File, varid, carma_h2so4_ref)
3249 0 : else if (carma%f_igash2So4 /= 0) then
3250 0 : call endrun(subname//'restart file must include CARMA_REF_H2SO4')
3251 : endif
3252 : endif
3253 :
3254 768 : end subroutine CARMA_restart_read
3255 :
3256 : !! Get the mixing ratio for the specified element and bin.
3257 : !!
3258 : !! @author Chuck Bardeen
3259 : !! @version Aug 2023
3260 95591986194 : subroutine carma_get_bin(state, ielem, ibin, mmr, rc)
3261 : type(physics_state), intent(in) :: state !! physics state variables
3262 : integer, intent(in) :: ielem !! element index
3263 : integer, intent(in) :: ibin !! bin index
3264 : real(r8), intent(out) :: mmr(pcols,pver) !! mass mixing ratio (kg/kg)
3265 : integer, intent(out) :: rc !! return code
3266 :
3267 : integer :: ncol
3268 :
3269 : ! default return code
3270 95591986194 : rc = RC_OK
3271 :
3272 95591986194 : ncol = state%ncol
3273 :
3274 : ! Check the group and bin ranges
3275 95591986194 : if ((ielem < 1) .or. (ielem .gt. NELEM)) then
3276 0 : write(LUNOPRT, *) 'carma_get_bin:: ERROR - Invalid element id, ', ielem
3277 0 : rc = RC_ERROR
3278 0 : return
3279 : end if
3280 :
3281 95591986194 : if ((ibin < 1) .or. (ibin .gt. NBIN)) then
3282 0 : write(LUNOPRT, *) 'carma_get_bin:: ERROR - Invalid bin id, ', ibin
3283 0 : rc = RC_ERROR
3284 0 : return
3285 : end if
3286 :
3287 : ! Get the element from the physics state
3288 >1032*10^11 : mmr(:ncol, :) = state%q(:ncol, :, icnst4elem(ielem, ibin))
3289 :
3290 : return
3291 : end subroutine
3292 :
3293 : !! Get the mixing ratio for the specified element and bin.
3294 3095857800 : subroutine carma_get_bin_cld(pbuf, ielem, ibin, ncol, nlev, mmr, rc)
3295 : type(physics_buffer_desc), pointer :: pbuf(:) !! physics buffer
3296 : integer, intent(in) :: ielem !! element index
3297 : integer, intent(in) :: ibin !! bin index
3298 : integer, intent(in) :: ncol,nlev !! dimensions
3299 : real(r8), intent(out) :: mmr(:,:) !! mass mixing ratio (kg/kg)
3300 : integer, intent(out) :: rc !! return code
3301 :
3302 3095857800 : real(r8), pointer :: mmr_ptr(:,:)
3303 : character(len=8) :: shortname ! short (CAM) name
3304 : character(len=16) :: c_name
3305 : integer :: idx
3306 :
3307 : ! default return code
3308 3095857800 : rc = RC_OK
3309 :
3310 3095857800 : call CARMAELEMENT_Get(carma, ielem, rc, shortname=shortname)
3311 :
3312 3095857800 : write(c_name, '(A, I2.2)') trim(shortname), ibin
3313 :
3314 3095857800 : idx = pbuf_get_index('CLD'//trim(c_name))
3315 3095857800 : call pbuf_get_field(pbuf, idx, mmr_ptr)
3316 :
3317 >33431*10^8 : mmr(:ncol,:nlev) = mmr_ptr(:ncol,:nlev)
3318 :
3319 3095857800 : end subroutine carma_get_bin_cld
3320 :
3321 : !! Determine the dry radius and dry density for the particular bin.
3322 : !!
3323 : !! @author Chuck Bardeen
3324 : !! @version Aug 2023
3325 160204800 : subroutine carma_get_dry_radius(state, igroup, ibin, rdry, rhopdry, rc)
3326 :
3327 : implicit none
3328 :
3329 : type(physics_state), intent(in) :: state !! physics state variables
3330 : integer, intent(in) :: igroup !! group index
3331 : integer, intent(in) :: ibin !! bin index
3332 : real(r8), intent(out) :: rdry(:,:) !! dry radius (m)
3333 : real(r8), intent(out) :: rhopdry(:,:) !! dry density (kg/m3)
3334 : integer, intent(out) :: rc !! return code
3335 :
3336 : real(r8) :: rhoelem(NBIN) ! element density (g/cm3)
3337 : real(r8) :: totvol(pcols,pver) ! total volume (m3/kg)
3338 : real(r8) :: totmmr(pcols,pver) ! total mmr (kg/kg)
3339 : real(r8) :: mmr(pcols, pver) ! mass mixing ratio (kg/kg)
3340 : real(r8) :: nmr(pcols, pver) ! number mixing ratio (#/kg)
3341 : integer :: nelems ! number of elements in group
3342 : integer :: ielems(NELEM) ! element indexes for group
3343 : integer :: ncol
3344 : integer :: i
3345 : integer :: ielem
3346 :
3347 : ! default return code
3348 160204800 : rc = RC_OK
3349 :
3350 160204800 : ncol = state%ncol
3351 :
3352 : ! Check the group and bin ranges
3353 160204800 : if ((igroup < 1) .or. (igroup .gt. NGROUP)) then
3354 0 : write(LUNOPRT, *) 'carma_get_dry_radius:: ERROR - Invalid group id, ', igroup
3355 0 : rc = RC_ERROR
3356 0 : return
3357 : end if
3358 :
3359 160204800 : if ((ibin < 1) .or. (ibin .gt. NBIN)) then
3360 0 : write(LUNOPRT, *) 'carma_get_dry_radius:: ERROR - Invalid bin id, ', ibin
3361 0 : rc = RC_ERROR
3362 0 : return
3363 : end if
3364 :
3365 : ! Iterate over all of the composition and determine the dry volume and dry radius.
3366 160204800 : call carma_get_elem_for_group(igroup, nelems, ielems, rc)
3367 160204800 : if (rc < 0) return
3368 :
3369 >17286*10^7 : totvol(:ncol, :) = 0._r8
3370 >17286*10^7 : totmmr(:ncol, :) = 0._r8
3371 >17286*10^7 : rhopdry(:ncol, :)= 0._r8
3372 >17286*10^7 : rdry(:ncol, :) = 0._r8
3373 :
3374 962841600 : do i = 1, nelems
3375 802636800 : ielem = ielems(i)
3376 :
3377 802636800 : call CARMAELEMENT_Get(carma, ielem, rc, rho=rhoelem)
3378 802636800 : if (rc < 0) return
3379 :
3380 802636800 : call carma_get_bin(state, ielem, ibin, mmr, rc)
3381 802636800 : if (rc < 0) return
3382 :
3383 >86604*10^7 : totmmr(:ncol, :) = totmmr(:ncol, :) + mmr(:ncol, :)
3384 >86620*10^7 : totvol(:ncol, :) = totvol(:ncol, :) + mmr(:ncol, :) / (rhoelem(ibin) / 1.e3_r8 * 1.e6_r8)
3385 : end do
3386 :
3387 : ! Add checks for totvol = 0 and nmr = 0
3388 >17286*10^7 : where(totvol(:ncol, :)>0._r8)
3389 160204800 : rhopdry(:ncol, :) = totmmr(:ncol, :) / totvol(:ncol, :)
3390 : end where
3391 :
3392 160204800 : call carma_get_number(state, igroup, ibin, nmr, rc)
3393 160204800 : if (rc < 0) return
3394 :
3395 >17286*10^7 : where(nmr(:ncol, :)>0._r8)
3396 160204800 : rdry(:ncol, :) = ((3._r8 * totvol(:ncol, :) / nmr(:ncol, :)) / (4._r8 * PI)) ** (1._r8 / 3._r8)
3397 : !rdry(:ncol, :) = ((three_o_fourpi* totvol(:ncol, :) / nmr(:ncol, :))) ** onethird
3398 : end where
3399 :
3400 : return
3401 : end subroutine carma_get_dry_radius
3402 :
3403 :
3404 : !! Get the number of elements and list of element ids for a group. This includes
3405 : !! the concentration elements and the core masses.
3406 : !!
3407 : !! @author Chuck Bardeen
3408 : !! @version Aug 2023
3409 34477941998 : subroutine carma_get_elem_for_group(igroup, nelems, ielems, rc)
3410 : integer, intent(in) :: igroup !! group index
3411 : integer, intent(out) :: nelems !! number of elements in group
3412 : integer, intent(out) :: ielems(NELEM) !! indexes of elements in group
3413 : integer, intent(out) :: rc !! return code
3414 :
3415 : integer :: ienconc
3416 : integer :: ncore
3417 : integer :: icorelem(NELEM)
3418 :
3419 : ! default return code
3420 17238970999 : rc = RC_OK
3421 :
3422 : ! Check the group range.
3423 17238970999 : if ((igroup < 1) .or. (igroup .gt. NGROUP)) then
3424 0 : write(LUNOPRT, *) 'carma_get_elem_for_group:: ERROR - Invalid group id, ', igroup
3425 0 : rc = RC_ERROR
3426 0 : return
3427 : end if
3428 :
3429 17238970999 : call CARMAGROUP_Get(carma, igroup, rc, ienconc=ienconc, ncore=ncore, icorelem=icorelem)
3430 :
3431 17238970999 : nelems = ncore + 1
3432 17238970999 : ielems(1) = ienconc
3433 :
3434 17238970999 : if (ncore .gt. 0) then
3435 97738647594 : ielems(2:ncore+1) = icorelem(1:ncore)
3436 : end if
3437 :
3438 : return
3439 : end subroutine
3440 :
3441 :
3442 : !! Get the CARMA group id a group name.
3443 : !!
3444 : !! @author Chuck Bardeen
3445 : !! @version Aug 2023
3446 2298266873 : subroutine carma_get_group_by_name(shortname, igroup, rc)
3447 : character(len=*), intent(in) :: shortname !! the group short name
3448 : integer, intent(out) :: igroup !! group index
3449 : integer, intent(out) :: rc !! return code
3450 :
3451 : integer :: i
3452 : character(len=32) :: name
3453 :
3454 : ! default return code
3455 2298266873 : rc = RC_OK
3456 :
3457 2298266873 : igroup = -1
3458 :
3459 : ! Check the short names of each group for one that matches
3460 3535465627 : do i = 1, NGROUP
3461 3535465627 : call CARMAGROUP_Get(carma, i, rc, shortname=name)
3462 :
3463 3535465627 : if (trim(shortname) .eq. trim(name)) then
3464 2298266873 : igroup = i
3465 2298266873 : exit
3466 : end if
3467 : end do
3468 :
3469 2298266873 : if (igroup .eq. -1) then
3470 0 : write(LUNOPRT, *) 'carma_get_group_by_name:: ERROR - group not found, ', shortname
3471 0 : rc = RC_ERROR
3472 0 : return
3473 : end if
3474 :
3475 : return
3476 : end subroutine
3477 :
3478 :
3479 : !! Get the CARMA group id and bin id from a compound name xxxxxxnn, where xxxxxx is the
3480 : !! name of the group and nn is the two digit bin number.
3481 : !!
3482 : !! @author Chuck Bardeen
3483 : !! @version Aug 2023
3484 : subroutine carma_get_group_and_bin_by_name(shortname, igroup, ibin, rc)
3485 : character(len=*), intent(out) :: shortname !! the group short name
3486 : integer, intent(out) :: igroup !! group index
3487 : integer, intent(out) :: ibin !! bin index
3488 : integer, intent(out) :: rc !! return code
3489 :
3490 : integer :: i
3491 : character(len=32) :: name
3492 : character(len=32) :: groupname
3493 : character(len=32) :: binname
3494 :
3495 : ! default return code
3496 : rc = RC_OK
3497 :
3498 : igroup = -1
3499 : ibin = -1
3500 :
3501 : if (len(shortname) <= 2) then
3502 : write(LUNOPRT, *) 'carma_get_group_and_bin_by_name:: ERROR - Illegal shortname, ' // shortname
3503 : rc = RC_ERROR
3504 : return
3505 : end if
3506 :
3507 : ! Check the short names of each group for one that matches
3508 : groupname = shortname(:len(shortname)-2)
3509 : binname = shortname(len(shortname)-2:)
3510 :
3511 : call carma_get_group_by_name(groupname, igroup, rc)
3512 : if (rc < 0) return
3513 :
3514 : read(binname, *) ibin
3515 :
3516 : return
3517 : end subroutine
3518 :
3519 :
3520 : !! Determine a mass weighted kappa for the entire particle.
3521 : !!
3522 : !! @author Chuck Bardeen
3523 : !! @version Aug 2023
3524 15097198999 : subroutine carma_get_kappa(state, igroup, ibin, kappa, rc)
3525 :
3526 : implicit none
3527 :
3528 : type(physics_state), intent(in) :: state !! physics state variables
3529 : integer, intent(in) :: igroup !! group index
3530 : integer, intent(in) :: ibin !! bin index
3531 : real(r8), intent(out) :: kappa(:,:) !! kappa value for the entire particle
3532 : integer, intent(out) :: rc !! return code
3533 :
3534 : real(r8) :: totmmr(pcols,pver) ! total mmr (kg/kg)
3535 : real(r8) :: mmr(pcols,pver) ! element mmr (kg/kg)
3536 : real(r8) :: kappaelem ! element kappa
3537 : integer :: ncol
3538 : integer :: nelems
3539 : integer :: ielems(NELEM)
3540 : integer :: i
3541 : integer :: ielem
3542 :
3543 : ! default return code
3544 15097198999 : rc = RC_OK
3545 :
3546 15097198999 : ncol = state%ncol
3547 :
3548 : ! Check the group and bin ranges
3549 15097198999 : if ((igroup < 1) .or. (igroup .gt. NGROUP)) then
3550 0 : write(LUNOPRT, *) 'carma_get_kappa:: ERROR - Invalid group id, ', igroup
3551 0 : rc = RC_ERROR
3552 0 : return
3553 : end if
3554 :
3555 15097198999 : if ((ibin < 1) .or. (igroup .gt. NBIN)) then
3556 0 : write(LUNOPRT, *) 'carma_get_kappa:: ERROR - Invalid bin id, ', ibin
3557 0 : rc = RC_ERROR
3558 0 : return
3559 : end if
3560 :
3561 : ! Iterate over all of the composition and determine the total mass.
3562 15097198999 : call carma_get_elem_for_group(igroup, nelems, ielems, rc)
3563 15097198999 : if (rc < 0) return
3564 :
3565 >16307*10^9 : totmmr(:ncol, :) = 0._r8
3566 >16307*10^9 : kappa(:ncol, :) = 0._r8
3567 :
3568 >10568*10^7 : do i = 1, nelems
3569 90583193994 : ielem = ielems(i)
3570 :
3571 90583193994 : call carma_get_bin(state, ielem, ibin, mmr, rc)
3572 90583193994 : if (rc < 0) return
3573 :
3574 90583193994 : call CARMAELEMENT_Get(carma, ielem, rc, kappa=kappaelem)
3575 :
3576 >97844*10^9 : kappa(:ncol, :) = kappa(:ncol, :) + mmr(:ncol, :) * kappaelem
3577 >97950*10^9 : totmmr(:ncol, :) = totmmr(:ncol, :) + mmr(:ncol, :)
3578 : end do
3579 :
3580 : ! Figure out the average kappa.q
3581 >16307*10^9 : where (totmmr(:ncol,:) .gt. 0._r8)
3582 15097198999 : kappa(:ncol,:) = kappa(:ncol,:) / totmmr(:ncol,:)
3583 : end where
3584 :
3585 : return
3586 : end subroutine
3587 :
3588 :
3589 : !! Get the number mixing ratio for the group. This is the number of particles per
3590 : !! density of air.
3591 : !!
3592 : !! @author Chuck Bardeen
3593 : !! @version Aug 2023
3594 1103948400 : subroutine carma_get_number(state, igroup, ibin, nmr, rc)
3595 :
3596 : implicit none
3597 :
3598 : type(physics_state), intent(in) :: state !! physics state variables
3599 : integer, intent(in) :: igroup !! group index
3600 : integer, intent(in) :: ibin !! bin index
3601 : real(r8), intent(out) :: nmr(pcols,pver) !! number mixing ratio (#/kg)
3602 : integer, intent(out) :: rc !! return code
3603 :
3604 2207896800 : real(r8) :: rmass(carma%f_NBIN) ! the bin mass (g)
3605 : real(r8) :: totmmr(pcols,pver) ! total mmr (kg/kg)
3606 : integer :: ncol
3607 :
3608 : ! default return code
3609 1103948400 : rc = RC_OK
3610 :
3611 1103948400 : ncol = state%ncol
3612 :
3613 : ! Check the group and bin ranges
3614 1103948400 : if ((igroup < 1) .or. (igroup .gt. NGROUP)) then
3615 0 : write(LUNOPRT, *) 'carma_get_number:: ERROR - Invalid group id, ', igroup
3616 0 : rc = RC_ERROR
3617 0 : return
3618 : end if
3619 :
3620 1103948400 : if ((ibin < 1) .or. (igroup .gt. NBIN)) then
3621 0 : write(LUNOPRT, *) 'carma_get_number:: ERROR - Invalid bin id, ', ibin
3622 0 : rc = RC_ERROR
3623 0 : return
3624 : end if
3625 :
3626 : ! Get the mass in each bin
3627 1103948400 : call CARMAGROUP_Get(carma, igroup, rc, rmass=rmass)
3628 1103948400 : if (rc < 0) return
3629 :
3630 : ! Get the total mmr in the bin
3631 1103948400 : call carma_get_total_mmr(state, igroup, ibin, totmmr, rc)
3632 1103948400 : if (rc < 0) return
3633 :
3634 : ! Get the mmr is the total mass divided by rmass, but need to convert rmass
3635 : ! to kg.
3636 >11919*10^8 : nmr(:ncol, :) = totmmr(:ncol, :) / (rmass(ibin) / 1.e3_r8)
3637 :
3638 : return
3639 : end subroutine carma_get_number
3640 :
3641 877618800 : subroutine carma_get_number_cld(pbuf, igroup, ibin, ncol, nlev, nmr, rc)
3642 :
3643 : implicit none
3644 :
3645 : type(physics_buffer_desc),pointer :: pbuf(:) !! physics buffer
3646 : integer, intent(in) :: igroup !! group index
3647 : integer, intent(in) :: ibin !! bin index
3648 : integer, intent(in) :: ncol,nlev !! dimensions
3649 : real(r8), intent(out) :: nmr(pcols,pver) !! number mixing ratio (#/kg)
3650 : integer, intent(out) :: rc !! return code
3651 :
3652 1755237600 : real(r8) :: rmass(carma%f_NBIN) ! the bin mass (g)
3653 : real(r8) :: totmmr(pcols,pver) ! total mmr (kg/kg)
3654 :
3655 : ! default return code
3656 877618800 : rc = RC_OK
3657 :
3658 : ! Check the group and bin ranges
3659 877618800 : if ((igroup < 1) .or. (igroup .gt. NGROUP)) then
3660 0 : write(LUNOPRT, *) 'carma_get_number:: ERROR - Invalid group id, ', igroup
3661 0 : rc = RC_ERROR
3662 0 : return
3663 : end if
3664 :
3665 877618800 : if ((ibin < 1) .or. (igroup .gt. NBIN)) then
3666 0 : write(LUNOPRT, *) 'carma_get_number:: ERROR - Invalid bin id, ', ibin
3667 0 : rc = RC_ERROR
3668 0 : return
3669 : end if
3670 :
3671 : ! Get the mass in each bin
3672 877618800 : call CARMAGROUP_Get(carma, igroup, rc, rmass=rmass)
3673 877618800 : if (rc < 0) return
3674 :
3675 : ! Get the total mmr in the bin
3676 877618800 : call carma_get_total_mmr_cld(pbuf, igroup, ibin, ncol, nlev, totmmr, rc)
3677 877618800 : if (rc < 0) return
3678 :
3679 : ! Get the mmr is the total mass divided by rmass, but need to convert rmass
3680 : ! to kg.
3681 >94771*10^7 : nmr(:ncol, :) = totmmr(:ncol, :) / (rmass(ibin) / 1.e3_r8)
3682 :
3683 : return
3684 : end subroutine carma_get_number_cld
3685 :
3686 :
3687 : !! Get the mixing ratio for the group. This is the total of all the elements that
3688 : !! make up the group.
3689 : !!
3690 : !! @author Chuck Bardeen
3691 : !! @version Aug 2023
3692 2207896800 : subroutine carma_get_total_mmr(state, igroup, ibin, totmmr, rc)
3693 :
3694 : implicit none
3695 :
3696 : type(physics_state), intent(in) :: state !! physics state variables
3697 : integer, intent(in) :: igroup !! group index
3698 : integer, intent(in) :: ibin !! bin index
3699 : real(r8), intent(out) :: totmmr(pcols,pver) !! total mmr (kg/kg)
3700 : integer, intent(out) :: rc !! return code
3701 :
3702 : real(r8) :: mmr(pcols, pver) ! mmr (kg/kg)
3703 : integer :: i
3704 : integer :: nelems
3705 : integer :: ielems(NELEM)
3706 : integer :: ielem
3707 : integer :: ncol
3708 :
3709 : ! default return code
3710 1103948400 : rc = RC_OK
3711 :
3712 1103948400 : ncol = state%ncol
3713 :
3714 : ! Check the group and bin ranges
3715 1103948400 : if ((igroup < 1) .or. (igroup .gt. NGROUP)) then
3716 0 : write(LUNOPRT, *) 'carma_get_total_mmr:: ERROR - Invalid group id, ', igroup
3717 0 : rc = RC_ERROR
3718 0 : return
3719 : end if
3720 :
3721 1103948400 : if ((ibin < 1) .or. (ibin .gt. NBIN)) then
3722 0 : write(LUNOPRT, *) 'carma_get_total_mmr:: ERROR - Invalid bin id, ', ibin
3723 0 : rc = RC_ERROR
3724 0 : return
3725 : end if
3726 :
3727 : ! Iterate over all of the composition and determine the total mass.
3728 1103948400 : call carma_get_elem_for_group(igroup, nelems, ielems, rc)
3729 1103948400 : if (rc < 0) return
3730 :
3731 >11919*10^8 : totmmr(:ncol, :) = 0._r8
3732 :
3733 5310103800 : do i = 1, nelems
3734 4206155400 : ielem = ielems(i)
3735 :
3736 4206155400 : call carma_get_bin(state, ielem, ibin, mmr, rc)
3737 4206155400 : if (rc < 0) return
3738 :
3739 >45422*10^8 : totmmr(:ncol, :) = totmmr(:ncol, :) + mmr(:ncol, :)
3740 : end do
3741 :
3742 : return
3743 : end subroutine carma_get_total_mmr
3744 :
3745 1755237600 : subroutine carma_get_total_mmr_cld(pbuf, igroup, ibin, ncol, nlev, totmmr, rc)
3746 :
3747 : type(physics_buffer_desc),pointer :: pbuf(:) !! physics buffer
3748 : integer, intent(in) :: igroup !! group index
3749 : integer, intent(in) :: ibin !! bin index
3750 : integer, intent(in) :: ncol,nlev !! dimensions
3751 : real(r8), intent(out) :: totmmr(pcols,pver) !! total mmr (kg/kg)
3752 : integer, intent(out) :: rc !! return code
3753 :
3754 : real(r8) :: mmr(pcols, pver) ! mmr (kg/kg)
3755 : integer :: i
3756 : integer :: nelems
3757 : integer :: ielems(NELEM)
3758 : integer :: ielem
3759 :
3760 : ! default return code
3761 877618800 : rc = RC_OK
3762 :
3763 :
3764 : ! Check the group and bin ranges
3765 877618800 : if ((igroup < 1) .or. (igroup .gt. NGROUP)) then
3766 0 : write(LUNOPRT, *) 'carma_get_total_mmr:: ERROR - Invalid group id, ', igroup
3767 0 : rc = RC_ERROR
3768 0 : return
3769 : end if
3770 :
3771 877618800 : if ((ibin < 1) .or. (ibin .gt. NBIN)) then
3772 0 : write(LUNOPRT, *) 'carma_get_total_mmr:: ERROR - Invalid bin id, ', ibin
3773 0 : rc = RC_ERROR
3774 0 : return
3775 : end if
3776 :
3777 : ! Iterate over all of the composition and determine the total mass.
3778 877618800 : call carma_get_elem_for_group(igroup, nelems, ielems, rc)
3779 877618800 : if (rc < 0) return
3780 :
3781 >94771*10^7 : totmmr(:ncol, :) = 0._r8
3782 :
3783 3973476600 : do i = 1, nelems
3784 3095857800 : ielem = ielems(i)
3785 :
3786 3095857800 : call carma_get_bin_cld(pbuf, ielem, ibin, ncol, nlev, mmr, rc)
3787 3095857800 : if (rc < 0) return
3788 :
3789 >33439*10^8 : totmmr(:ncol, :) = totmmr(:ncol, :) + mmr(:ncol, :)
3790 : end do
3791 :
3792 : end subroutine carma_get_total_mmr_cld
3793 :
3794 5836800 : subroutine carma_get_sad(state, igroup, ibin, sad, rc)
3795 : type(physics_state), intent(in) :: state !! physics state variables
3796 : integer, intent(in) :: igroup !! group index
3797 : integer, intent(in) :: ibin !! bin index
3798 : real(r8), intent(out) :: sad(pcols,pver) !! surface area dens (cm2/cm3)
3799 : integer, intent(out) :: rc !! return code
3800 :
3801 : real(r8) :: nmr(pcols,pver) !! number mixing ratio (#/kg)
3802 : real(r8) :: rwet(pcols,pver) !! wet radius (m)
3803 : real(r8) :: rhopwet(pcols,pver) !! wet density (kg/m3)
3804 : real(r8) :: rhoa(pcols,pver) !! air density (kg/m3)
3805 : real(r8) :: ndens(pcols,pver) !! number density (#/m3)
3806 :
3807 : integer :: ncol
3808 :
3809 5836800 : rc = RC_OK
3810 :
3811 5836800 : call carma_get_wet_radius(state, igroup, ibin, rwet, rhopwet, rc)
3812 5836800 : call carma_get_number(state, igroup, ibin, nmr, rc)
3813 :
3814 5836800 : ncol = state%ncol
3815 :
3816 6297907200 : rhoa(:ncol,:) = (state%pmid(:ncol,:) * 10._r8) / (R_AIR * state%t(:ncol,:)) / 1.e3_r8 * 1.e6_r8 ! air density (kg/m3)
3817 :
3818 6297907200 : ndens(:ncol,:) = nmr(:ncol,:) * rhoa(:ncol,:) ! #/m3
3819 :
3820 6297907200 : sad(:ncol,:) = 4.0_r8 * PI * ndens(:ncol,:) * (rwet(:ncol,:)**2) * 1.e-2_r8 ! cm2/cm3
3821 :
3822 5836800 : end subroutine carma_get_sad
3823 :
3824 :
3825 : !! Find the wet radius and wet density for the group and bin specified.
3826 : !!
3827 : !! NOTE: Groups can be configured with different methods to determine the wet
3828 : !! radius, so multiple methods need to be supported and code from rhopart and
3829 : !! wetr need to be included in this routine.
3830 : !!
3831 : !! @author Chuck Bardeen
3832 : !! @version Aug 2023
3833 89856000 : subroutine carma_get_wet_radius(state, igroup, ibin, rwet, rhopwet, rc)
3834 : use wetr, only: getwetr
3835 :
3836 : type(physics_state), intent(in) :: state !! physics state variables
3837 : integer, intent(in) :: igroup !! group index
3838 : integer, intent(in) :: ibin !! bin index
3839 : real(r8), intent(out) :: rwet(pcols,pver) !! wet radius (m)
3840 : real(r8), intent(out) :: rhopwet(pcols,pver) !! wet density (kg/m3)
3841 : integer, intent(inout) :: rc !! return code
3842 :
3843 : real(r8) :: rdry(pcols,pver) !! dry radius (m)
3844 : real(r8) :: rhopdry(pcols,pver) !! dry density (kg/m3)
3845 : real(r8) :: rhoa(pcols,pver) !! air density (kg/m3)
3846 : real(r8) :: kappa(pcols,pver) !! dry radius (m)
3847 : real(r8) :: es !! saturation vapor pressure
3848 : real(r8) :: qs !! saturation specific humidity
3849 : real(r8) :: relhum !! relative humidity
3850 : real(r8) :: wvpres !! water eq. vaper pressure (dynes/cm2)
3851 : real(r8) :: watcon !! water concentration (g/cm3)
3852 : real(r8) :: dryden !! dry density (g/cm3)
3853 : real(r8) :: dryrad !! dry radius (cm)
3854 : integer :: icol
3855 : integer :: iz
3856 : integer :: ncol
3857 : integer :: iq
3858 : integer :: irhswell
3859 :
3860 : ! default return code
3861 29952000 : rc = RC_OK
3862 :
3863 29952000 : ncol = state%ncol
3864 :
3865 : ! Check the group and bin ranges
3866 29952000 : if ((igroup < 1) .or. (igroup .gt. NGROUP)) then
3867 0 : write(LUNOPRT, *) 'carma_get_total_mmr:: ERROR - Invalid group id, ', igroup
3868 0 : rc = RC_ERROR
3869 : !return
3870 : end if
3871 29952000 : if (rc/=RC_OK) then
3872 0 : call endrun('carma_get_wet_radius ERROR1: rc = ',rc)
3873 : end if
3874 :
3875 29952000 : if ((ibin < 1) .or. (ibin .gt. NBIN)) then
3876 0 : write(LUNOPRT, *) 'carma_get_total_mmr:: ERROR - Invalid bin id, ', ibin
3877 0 : rc = RC_ERROR
3878 : !return
3879 : end if
3880 29952000 : if (rc/=RC_OK) then
3881 0 : call endrun('carma_get_wet_radius ERROR2: rc = ',rc)
3882 : end if
3883 :
3884 : ! Get the constiuent index for water vapor (Q)
3885 29952000 : call cnst_get_ind("Q", iq)
3886 :
3887 : ! The wet radius can be configured differently for each group, so we
3888 : ! need to use getwetr to handle those differences. This requires repeating
3889 : ! some code that is in rhopart to use getwetr properly. There may be a
3890 : ! better way to do this, but for now we will duplicate the code.
3891 29952000 : call carma_get_dry_radius(state, igroup, ibin, rdry, rhopdry, rc)
3892 : !if (rc < 0) return
3893 29952000 : if (rc/=RC_OK) then
3894 0 : call endrun('carma_get_wet_radius ERROR3: rc = ',rc)
3895 : end if
3896 :
3897 : ! Calculate the dry air density at each level, using the ideal gas law.
3898 32318208000 : rhoa(:ncol, :) = (state%pmid(:ncol, :) * 10._r8) / (R_AIR * state%t(:ncol, :)) / 1.e3_r8 * 1.e6_r8
3899 :
3900 29952000 : call CARMAGROUP_Get(carma, igroup, rc, irhswell=irhswell)
3901 : !if (rc < 0) return
3902 29952000 : if (rc/=RC_OK) then
3903 0 : call endrun('carma_get_wet_radius ERROR4: rc = ',rc)
3904 : end if
3905 :
3906 461260800 : do icol = 1, ncol
3907 30652876800 : do iz = 1, pver
3908 30622924800 : if (rdry(icol, iz)>0._r8) then
3909 : ! Get relative humidity and vapor pressure
3910 29310079993 : call wv_sat_qsat_water(state%t(icol,iz), state%pmid(icol,iz), es, qs)
3911 :
3912 : ! NOTE: getwetr is in cgs units, so some conversions are needed from the
3913 : ! mks values
3914 29310079993 : wvpres = es * 10._r8 ! dynes/cm2
3915 29310079993 : relhum = state%q(icol,iz,iq) / qs
3916 29310079993 : watcon = state%q(icol,iz,iq) * rhoa(icol, iz) * 1.e-3_r8 ! g/cm3
3917 29310079993 : dryden = rhopdry(icol,iz) * 1.e-3_r8 ! g/cm3
3918 29310079993 : dryrad = rdry(icol,iz) * 1.e2_r8 ! cm
3919 :
3920 : ! If humidity affects the particle, then determine the equilbirium
3921 : ! radius and density based upon the relative humidity.
3922 : !
3923 29310079993 : if (irhswell == I_WTPCT_H2SO4) then
3924 :
3925 : call getwetr(carma, igroup, relhum, dryrad, rwet(icol, iz), dryden, rhopwet(icol,iz), rc, &
3926 : h2o_mass=watcon, h2o_vp=wvpres, temp=state%t(icol,iz))
3927 14214416994 : if (rc/=RC_OK) then
3928 0 : call endrun('carma_get_wet_radius ERROR5: rc = ',rc) ! <======
3929 : end if
3930 :
3931 30191325998 : else if (irhswell == I_PETTERS) then
3932 :
3933 15095662999 : call carma_get_kappa(state, igroup, ibin, kappa, rc)
3934 15095662999 : if (rc/=RC_OK) then
3935 0 : call endrun('carma_get_wet_radius carma_get_kappa ERROR: rc = ',rc)
3936 : end if
3937 :
3938 : call getwetr(carma, igroup, relhum, dryrad, rwet(icol, iz), dryden, rhopwet(icol,iz), rc, &
3939 : h2o_mass=watcon, h2o_vp=wvpres, temp=state%t(icol,iz), kappa=kappa(icol,iz))
3940 15095662999 : if (rc/=RC_OK) then
3941 0 : call endrun('carma_get_wet_radius ERROR6: rc = ',rc)
3942 : end if
3943 :
3944 : else ! I_GERBER and I_FITZGERALD
3945 :
3946 : call getwetr(carma, igroup, relhum, dryrad, rwet(icol, iz), dryden, rhopwet(icol,iz), rc )
3947 0 : if (rc/=RC_OK) then
3948 0 : call endrun('carma_get_wet_radius ERROR7: rc = ',rc)
3949 : end if
3950 :
3951 : end if
3952 : else
3953 881536007 : rhopwet(icol,iz) = 0._r8
3954 881536007 : rwet(icol, iz) = 0._r8
3955 : end if
3956 : end do
3957 : end do
3958 :
3959 : ! Convert rwet and rhopwet to mks units
3960 32318208000 : rwet(:ncol,:) = rwet(:ncol,:) * 1.e-2_r8 ! cm --> m
3961 32318208000 : rhopwet(:ncol,:) = rhopwet(:ncol,:) * 1.e3_r8 ! g/cm3 --> kg/m3
3962 :
3963 29952000 : if (rc/=RC_OK) then
3964 0 : call endrun('carma_get_wet_radius ERROR8: rc = ',rc)
3965 : end if
3966 :
3967 29952000 : return
3968 : end subroutine
3969 :
3970 :
3971 : !! Provides the tendency (in kg/kg/s) required to change the element and bin from
3972 : !! the current state to the desired mmr.
3973 : !!
3974 : !! NOTE: The caller needs to make sure that the lq flags are set in ptend for the
3975 : !! particular tracer. Perhaps we need a routine that will set lq to true for all
3976 : !! the fields that could be set by CARMA to be used by the caller of this routine.
3977 : !!
3978 : !! @author Chuck Bardeen
3979 : !! @version Aug 2023
3980 0 : subroutine carma_set_bin(state, ielem, ibin, mmr, dt, ptend, rc)
3981 :
3982 : implicit none
3983 :
3984 : type(physics_state), intent(in) :: state !! physics state variables
3985 : integer, intent(in) :: ielem !! element index
3986 : integer, intent(in) :: ibin !! bin index
3987 : real(r8), intent(in) :: mmr(pcols,pver) !! mass mixing ratio (kg/kg)
3988 : integer :: dt !! timestep size (sec)
3989 : type(physics_ptend), intent(inout) :: ptend !! constituent tendencies
3990 : integer, intent(out) :: rc !! return code
3991 :
3992 : integer :: ncol
3993 : integer :: icnst
3994 :
3995 : ! default return code
3996 0 : rc = RC_OK
3997 :
3998 0 : ncol = state%ncol
3999 :
4000 : ! Check the element and bin ranges
4001 0 : if ((ielem < 1) .or. (ielem .gt. NELEM)) then
4002 0 : write(LUNOPRT, *) 'carma_set_bin:: ERROR - Invalid element id, ', ielem
4003 0 : rc = RC_ERROR
4004 0 : return
4005 : end if
4006 :
4007 0 : if ((ibin < 1) .or. (ibin .gt. NBIN)) then
4008 0 : write(LUNOPRT, *) 'carma_set_binr:: ERROR - Invalid bin id, ', ibin
4009 0 : rc = RC_ERROR
4010 0 : return
4011 : end if
4012 :
4013 : ! Determine the tendency needed to make state into mmr for this tracer.
4014 0 : icnst = icnst4elem(ielem, ibin)
4015 0 : ptend%q(:ncol, :, icnst) = (mmr(:ncol, :) - state%q(:ncol, :, icnst)) / dt
4016 :
4017 : return
4018 : end subroutine
4019 :
4020 346037273 : subroutine carma_get_bin_rmass(igroup, ibin, mass, rc)
4021 :
4022 : integer, intent(in) :: igroup !! group index
4023 : integer, intent(in) :: ibin !! bin index
4024 : real(r8),intent(out) :: mass ! grams ???
4025 : integer, intent(out) :: rc !! return code
4026 :
4027 0 : real(r8) :: rmass(carma%f_NBIN) ! the bin mass (g)
4028 :
4029 : ! default return code
4030 346037273 : rc = RC_OK
4031 7266782733 : rmass = rmass
4032 :
4033 346037273 : call CARMAGROUP_Get(carma, igroup, rc, rmass=rmass) ! rmass in g
4034 346037273 : if (rc /= RC_OK) return
4035 :
4036 346037273 : mass = rmass(ibin)*1.e-03_r8 ! convert to kg
4037 :
4038 346037273 : end subroutine carma_get_bin_rmass
4039 :
4040 76800 : function carma_get_wght_pct(ncol,nlev,state) result(wtpct)
4041 : use sulfate_utils, only: wtpct_tabaz
4042 :
4043 : integer, intent(in) :: ncol,nlev
4044 : type(physics_state), intent(in) :: state !! Physics state variables - before CARMA
4045 :
4046 : real(r8) :: wtpct(ncol,nlev)
4047 :
4048 : integer :: rc !! return code
4049 : real(r8) :: pvapl, es, qs, gc_cgs, rhoa
4050 : integer :: icol, ilev
4051 :
4052 76800 : rc = RC_OK
4053 :
4054 5452800 : do ilev = 1,nlev
4055 82867200 : do icol = 1,ncol
4056 : ! Get relative humidity and vapor pressure
4057 :
4058 77414400 : call wv_sat_qsat_water(state%t(icol,ilev), state%pmid(icol,ilev), es, qs) ! es = Saturation vapor pressure in Pa
4059 :
4060 77414400 : pvapl = es * 10._r8 ! Pa -> dynes/cm2
4061 :
4062 77414400 : rhoa = (state%pmid(icol,ilev) * 10._r8) / (R_AIR * state%t(icol,ilev)) ! grams/cm3
4063 :
4064 77414400 : gc_cgs = state%q(icol,ilev,icnst4gas(carma%f_igash2o)) * rhoa ! h2o grams/cm3
4065 :
4066 77414400 : wtpct(icol,ilev) = wtpct_tabaz(carma, state%t(icol,ilev), gc_cgs, pvapl, rc)
4067 :
4068 160204800 : if (rc/=RC_OK) then
4069 0 : call endrun('carma_get_wght_pct: rc = ',rc)
4070 : end if
4071 : end do
4072 : end do
4073 :
4074 153600 : end function carma_get_wght_pct
4075 :
4076 145920 : function carma_effecitive_radius(state) result(rad)
4077 :
4078 : type(physics_state), intent(in) :: state !! physics state variables
4079 : real(r8) :: rad(pcols,pver) ! effective radius (cm)
4080 :
4081 : integer :: igroup, ibin, rc, ncol
4082 : real(r8) :: rwet(pcols,pver) !! wet radius (m)
4083 : real(r8) :: rho(pcols,pver) !! density (kg/m3)
4084 : real(r8) :: nmr(pcols,pver) !! num/kg
4085 : real(r8) :: rtmp3(pcols,pver)
4086 : real(r8) :: rtmp2(pcols,pver)
4087 :
4088 145920 : rc = RC_OK
4089 :
4090 145920 : rtmp2(:,:) = 0.0_r8
4091 145920 : rtmp3(:,:) = 0.0_r8
4092 :
4093 145920 : ncol = state%ncol
4094 :
4095 437760 : do igroup = 1, NGROUP
4096 6274560 : do ibin = 1, NBIN
4097 :
4098 5836800 : call carma_get_number(state, igroup, ibin, nmr, rc)
4099 5836800 : call carma_get_wet_radius(state, igroup, ibin, rwet, rho, rc)
4100 5836800 : if (rc/=RC_OK) then
4101 0 : call endrun('carma_effecitive_radius -- carma_get_wet_radius ERROR: rc = ',rc)
4102 : end if
4103 :
4104 6297907200 : rtmp3(:ncol,:) = rtmp3(:ncol,:) + nmr(:ncol,:)*(rwet(:ncol,:)**3)
4105 6304035840 : rtmp2(:ncol,:) = rtmp2(:ncol,:) + nmr(:ncol,:)*(rwet(:ncol,:)**2)
4106 :
4107 : end do
4108 : end do
4109 :
4110 157447680 : rad(:ncol,:) = (rtmp3(:ncol,:)/rtmp2(:ncol,:))*100._r8 ! cm
4111 :
4112 222720 : end function carma_effecitive_radius
4113 :
4114 : end module carma_intr
|