CAMP 1.0.0
Chemistry Across Multiple Phases
rxn_condensed_phase_arrhenius.F90
Go to the documentation of this file.
1! Copyright (C) 2021 Barcelona Supercomputing Center and University of
2! Illinois at Urbana-Champaign
3! SPDX-License-Identifier: MIT
4
5!> \file
6!> The camp_rxn_condensed_phase_arrhenius module.
7
8!> \page camp_rxn_condensed_phase_arrhenius CAMP: Condensed-Phase Arrhenius Reaction
9!!
10!! Condensed-phase Arrhenius reactions are calculated using an Arrhenius-like
11!! rate constant that takes the form:
12!!
13!! \f[
14!! Ae^{(\frac{-E_a}{k_bT})}(\frac{T}{D})^B(1.0+E*P)
15!! \f]
16!!
17!! where \f$A\f$ is the pre-exponential factor
18!! (\f$[\mbox{U}]^{-(n-1)} s^{-1}\f$), \f$U\f$ is the unit of the reactants
19!! and products, which can be \f$M\f$ for aqueous-phase reactions or
20!! mol \f$\mbox{m}^{-3}\f$ for all other condensed-phase
21!! reactions, \f$n\f$ is the number of reactants, \f$E_a\f$ is the activation
22!! energy (J), \f$k_b\f$ is the Boltzmann constant (J/K), \f$D\f$ (K), \f$B\f$
23!! (unitless) and \f$E\f$ (\f$Pa^{-1}\f$) are reaction parameters, \f$T\f$ is
24!! the temperature (K), and \f$P\f$ is the pressure (Pa). The first two terms
25!! are described in Finlayson-Pitts and Pitts (2000) \cite Finlayson-Pitts2000
26!! . The final term is included to accomodate CMAQ EBI solver type 7 rate
27!! constants \cite Gipson.
28!!
29!! Input data for condensed-phase Arrhenius reactions have the following
30!! format:
31!! \code{.json}
32!! {
33!! "type" : "CONDENSED_PHASE_ARRHENIUS",
34!! "A" : 123.45,
35!! "Ea" : 123.45,
36!! "B" : 1.3,
37!! "D" : 300.0,
38!! "E" : 0.6E-5,
39!! "units" : "M",
40!! "time unit" : "MIN",
41!! "aerosol phase" : "my aqueous phase",
42!! "aerosol-phase water" : "H2O_aq",
43!! "reactants" : {
44!! "spec1" : {},
45!! "spec2" : { "qty" : 2 },
46!! ...
47!! },
48!! "products" : {
49!! "spec3" : {},
50!! "spec4" : { "yield" : 0.65 },
51!! ...
52!! }
53!! }
54!! \endcode
55!! The key-value pairs \b reactants, and \b products are required. Reactants
56!! without a \b qty value are assumed to appear once in the reaction equation.
57!! Products without a specified \b yield are assumed to have a \b yield of
58!! 1.0.
59!!
60!! Units for the reactants and products must be specified using the key
61!! \b units and can be either \b M or \b mol \b m-3. If units of \b M are
62!! specified, a key-value pair \b aerosol-phase \b water must also be included
63!! whose value is a string specifying the name for water in the aerosol phase.
64!!
65!! The unit for time is assumed to be s, but inclusion of the optional
66!! key-value pair \b time \b unit = \b MIN can be used to indicate a rate
67!! with min as the time unit.
68!!
69!! The key-value pair \b aerosol \b phase is required and must specify the name
70!! of the aerosol-phase in which the reaction occurs.
71!!
72!! Optionally, a parameter \b C may be included, and is taken to equal
73!! \f$\frac{-E_a}{k_b}\f$. Note that either \b Ea or \b C may be included, but
74!! not both. When neither \b Ea or \b C are included, they are assumed to be
75!! 0.0. When \b A is not included, it is assumed to be 1.0, when \b D is not
76!! included, it is assumed to be 300.0 K, when \b B is not included, it is
77!! assumed to be 0.0, and when \b E is not included, it is assumed to be 0.0.
78
79!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
80
81!> The rxn_condensed_phase_arrhenius_t type and associated functions.
83
87 use camp_constants, only: const
91 use camp_util, only: i_kind, dp, to_string, &
94
95 implicit none
96 private
97
98#define NUM_REACT_ this%condensed_data_int(1)
99#define NUM_PROD_ this%condensed_data_int(2)
100#define NUM_AERO_PHASE_ this%condensed_data_int(3)
101#define A_ this%condensed_data_real(1)
102#define B_ this%condensed_data_real(2)
103#define C_ this%condensed_data_real(3)
104#define D_ this%condensed_data_real(4)
105#define E_ this%condensed_data_real(5)
106#define NUM_INT_PROP_ 3
107#define NUM_REAL_PROP_ 5
108#define NUM_ENV_PARAM_ 1
109#define REACT_(x) this%condensed_data_int(NUM_INT_PROP_+x)
110#define PROD_(x) this%condensed_data_int(NUM_INT_PROP_+NUM_REACT_*NUM_AERO_PHASE_+x)
111#define WATER_(x) this%condensed_data_int(NUM_INT_PROP_+(NUM_REACT_+NUM_PROD_)*NUM_AERO_PHASE_+x)
112#define DERIV_ID_(x) this%condensed_data_int(NUM_INT_PROP_+(NUM_REACT_+NUM_PROD_+1)*NUM_AERO_PHASE_+x)
113#define JAC_ID_(x) this%condensed_data_int(NUM_INT_PROP_+(2*(NUM_REACT_+NUM_PROD_)+1)*NUM_AERO_PHASE_+x)
114#define YIELD_(x) this%condensed_data_real(NUM_REAL_PROP_+x)
115#define KGM3_TO_MOLM3_(x) this%condensed_data_real(NUM_REAL_PROP_+NUM_PROD_+x)
116
118
119 !> Generic test reaction data type
121 contains
122 !> Reaction initialization
123 procedure :: initialize
124 !> Finalize the reaction
125 final :: finalize
127
128 !> Constructor for rxn_condensed_phase_arrhenius_t
130 procedure :: constructor
132
133contains
134
135!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
136
137 !> Constructor for Condensed-phase Arrhenius reaction
138 function constructor() result(new_obj)
139
140 !> A new reaction instance
141 type(rxn_condensed_phase_arrhenius_t), pointer :: new_obj
142
143 allocate(new_obj)
144 new_obj%rxn_phase = aero_rxn
145
146 end function constructor
147
148!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
149
150 !> Initialize the reaction data, validating component data and loading
151 !! any required information into the condensed data arrays for use during
152 !! solving
153 subroutine initialize(this, chem_spec_data, aero_rep, n_cells)
154
155 !> Reaction data
156 class(rxn_condensed_phase_arrhenius_t), intent(inout) :: this
157 !> Chemical species data
158 type(chem_spec_data_t), intent(in) :: chem_spec_data
159 !> Aerosol representations
160 type(aero_rep_data_ptr), pointer, intent(in) :: aero_rep(:)
161 !> Number of grid cells to solve simultaneously
162 integer(kind=i_kind), intent(in) :: n_cells
163
164 type(property_t), pointer :: spec_props, reactants, products
165 character(len=:), allocatable :: key_name, spec_name, water_name, &
166 phase_name, temp_string
167 integer(kind=i_kind) :: i_spec, i_phase_inst, i_qty, i_aero_rep, &
168 i_aero_phase, num_spec_per_phase, num_phase, num_react, num_prod
169 type(string_t), allocatable :: unique_names(:)
170 type(string_t), allocatable :: react_names(:), prod_names(:)
171
172 integer(kind=i_kind) :: temp_int
173 real(kind=dp) :: temp_real
174 logical :: is_aqueous
175
176 is_aqueous = .false.
177
178 ! Get the property set
179 call assert_msg(852163263, associated(this%property_set), &
180 "Missing property set needed to initialize reaction")
181
182 ! Get the aerosol phase name
183 key_name = "aerosol phase"
184 call assert_msg(845445717, &
185 this%property_set%get_string(key_name, phase_name), &
186 "Missing aerosol phase in condensed-phase Arrhenius reaction")
187
188 ! Get reactant species
189 key_name = "reactants"
190 call assert_msg(501773136, &
191 this%property_set%get_property_t(key_name, reactants), &
192 "Missing reactant species in condensed-phase Arrhenius reaction")
193
194 ! Get product species
195 key_name = "products"
196 call assert_msg(556252922, &
197 this%property_set%get_property_t(key_name, products), &
198 "Missing product species in condensed-phase Arrhenius reaction")
199
200 ! Count the number of species per phase instance (including reactants
201 ! with a "qty" specified)
202 call reactants%iter_reset()
203 num_react = 0
204 do while (reactants%get_key(spec_name))
205 ! Get properties included with this reactant in the reaction data
206 call assert(428593951, reactants%get_property_t(val=spec_props))
207 key_name = "qty"
208 if (spec_props%get_int(key_name, temp_int)) &
209 num_react = num_react + temp_int - 1
210 call reactants%iter_next()
211 num_react = num_react + 1
212 end do
213 num_prod = products%size()
214 num_spec_per_phase = num_prod + num_react
215
216 ! Check for aerosol representations
217 call assert_msg(370755392, associated(aero_rep), &
218 "Missing aerosol representation for condensed-phase "// &
219 "Arrhenius reaction")
220 call assert_msg(483073737, size(aero_rep).gt.0, &
221 "Missing aerosol representation for condensed-phase "// &
222 "Arrhenius reaction")
223
224 ! Count the instances of the specified aerosol phase
225 num_phase = 0
226 do i_aero_rep = 1, size(aero_rep)
227 num_phase = num_phase + &
228 aero_rep(i_aero_rep)%val%num_phase_instances(phase_name)
229 end do
230
231 ! Allocate space in the condensed data arrays
232 allocate(this%condensed_data_int(num_int_prop_ + &
233 num_phase * (num_spec_per_phase * (num_react + 3) + 1)))
234 allocate(this%condensed_data_real(num_real_prop_ + &
235 num_spec_per_phase + num_prod))
236 this%condensed_data_int(:) = int(0, kind=i_kind)
237 this%condensed_data_real(:) = real(0.0, kind=dp)
238
239 ! Save space for the environment-dependent parameters
240 this%num_env_params = num_env_param_
241
242 ! Set the number of products, reactants and aerosol phase instances
243 num_react_ = num_react
244 num_prod_ = num_prod
245 num_aero_phase_ = num_phase
246
247 ! Get the rate constant parameters
248 key_name = "A"
249 if (.not. this%property_set%get_real(key_name, a_)) then
250 a_ = 1.0
251 end if
252 key_name = "time unit"
253 if (this%property_set%get_string(key_name, temp_string)) then
254 if (trim(temp_string).eq."MIN") then
255 a_ = a_ / 60.0
256 else
257 call assert_msg(390870843, trim(temp_string).eq."s", &
258 "Received invalid time unit: '"//temp_string//"' in "// &
259 "condnesed-phase Arrhenius reaction. Valid units are "// &
260 "'MIN' and 's'.")
261 end if
262 end if
263 key_name = "Ea"
264 if (this%property_set%get_real(key_name, temp_real)) then
265 c_ = -temp_real/const%boltzmann
266 key_name = "C"
267 call assert_msg(827485736, &
268 .not.this%property_set%get_real(key_name, temp_real), &
269 "Received both Ea and C parameter for condensed-phase "// &
270 "Arrhenius equation.")
271 else
272 key_name = "C"
273 if (.not. this%property_set%get_real(key_name, c_)) then
274 c_ = 0.0
275 end if
276 end if
277 key_name = "D"
278 if (.not. this%property_set%get_real(key_name, d_)) then
279 d_ = 300.0
280 end if
281 key_name = "B"
282 if (.not. this%property_set%get_real(key_name, b_)) then
283 b_ = 0.0
284 end if
285 key_name = "E"
286 if (.not. this%property_set%get_real(key_name, e_)) then
287 e_ = 0.0
288 end if
289
290 ! Set up an array to the reactant and product names
291 allocate(react_names(num_react_))
292 allocate(prod_names(num_prod_))
293
294 ! Get the chemical properties for the reactants
295 call reactants%iter_reset()
296 i_spec = 0
297 do while (reactants%get_key(spec_name))
298
299 ! Get the reactant species properties
300 call assert_msg(365229559, &
301 chem_spec_data%get_property_set(spec_name, spec_props), &
302 "Missing properties required for condensed-phase Arrhenius "// &
303 "reaction involving species '"//trim(spec_name)//"'")
304
305 ! Get the molecular weight
306 key_name = "molecular weight [kg mol-1]"
307 call assert_msg(409180731, spec_props%get_real(key_name, temp_real), &
308 "Missing 'molecular weight' for species '"//trim(spec_name)// &
309 "' in condensed-phase Arrhenius reaction.")
310
311 ! Set properties for each occurance of a reactant in the rxn equation
312 call assert(186449575, reactants%get_property_t(val=spec_props))
313 key_name = "qty"
314 if (.not.spec_props%get_int(key_name, temp_int)) temp_int = 1
315 do i_qty = 1, temp_int
316 i_spec = i_spec + 1
317
318 ! Add the reactant name to the list
319 react_names(i_spec)%string = spec_name
320
321 ! Use the MW to calculate the kg/m3 -> mol/m3 conversion
322 kgm3_to_molm3_(i_spec) = 1.0/temp_real
323
324 end do
325
326 ! Go to the next reactant
327 call reactants%iter_next()
328
329 end do
330
331 ! Get the chemical properties for the products
332 call products%iter_reset()
333 i_spec = 0
334 do while (products%get_key(spec_name))
335
336 ! Get the product species properties
337 call assert_msg(450225425, &
338 chem_spec_data%get_property_set(spec_name, spec_props), &
339 "Missing properties required for condensed-phase Arrhenius "// &
340 "reaction involving species '"//trim(spec_name)//"'")
341
342 ! Increment the product counter
343 i_spec = i_spec + 1
344
345 ! Get the molecular weight
346 key_name = "molecular weight [kg mol-1]"
347 call assert_msg(504705211, spec_props%get_real(key_name, temp_real), &
348 "Missing 'molecular weight' for species '"//trim(spec_name)// &
349 "' in condensed phase Arrhenius reaction.")
350
351 ! Use the MW to calculate the kg/m3 -> mol/m3 conversion
352 kgm3_to_molm3_(num_react_+i_spec) = 1.0/temp_real
353
354 ! Set properties for each occurance of a reactant in the rxn equation
355 call assert(846924553, products%get_property_t(val=spec_props))
356 key_name = "yield"
357 if (spec_props%get_real(key_name, temp_real)) then
358 yield_(i_spec) = temp_real
359 else
360 yield_(i_spec) = 1.0d0
361 end if
362
363 ! Add the product name to the list
364 prod_names(i_spec)%string = spec_name
365
366 ! Go to the next product
367 call products%iter_next()
368
369 end do
370
371 ! Get the units for the reactants and products and name for water
372 ! if this is an aqueous reaction
373 key_name = "units"
374 call assert_msg(348722817, &
375 this%property_set%get_string(key_name, temp_string), &
376 "Missing units for condensed-phase Arrhenius reaction.")
377 if (trim(temp_string).eq."mol m-3") then
378 is_aqueous = .false.
379 key_name = "aerosol-phase water"
380 call assert_msg(767767240, &
381 .not.this%property_set%get_string(key_name, temp_string), &
382 "Aerosol-phase water specified for non-aqueous condensed-"// &
383 "phase Arrhenius reaction. Change units to 'M' or remove "// &
384 "aerosol-phase water")
385 else if (trim(temp_string).eq."M") then
386 is_aqueous = .true.
387 key_name = "aerosol-phase water"
388 call assert_msg(199910264, &
389 this%property_set%get_string(key_name, water_name), &
390 "Missing aerosol-phase water for aqeuous condensed-phase "// &
391 "Arrhenius reaction.")
392 else
393 call die_msg(161772048, "Received invalid units for condensed-"// &
394 "phase Arrhenius reaction: '"//temp_string//"'. Valid "// &
395 "units are 'mol m-3' or 'M'.")
396 end if
397
398 ! Set the state array indices for the reactants, products and water
399 i_aero_phase = 0
400 do i_aero_rep = 1, size(aero_rep)
401
402 ! Check for the specified phase in this aero rep
403 num_phase = aero_rep(i_aero_rep)%val%num_phase_instances(phase_name)
404 if (num_phase.eq.0) cycle
405
406 ! Save the state ids for aerosol-phase water for aqueous reactions.
407 ! For non-aqueous reactions, set the water id to -1
408 if (is_aqueous) then
409
410 ! Get the unique names for aerosol-phase water
411 unique_names = aero_rep(i_aero_rep)%val%unique_names( &
412 phase_name = phase_name, spec_name = water_name)
413
414 ! Make sure water is present
415 call assert_msg(196838614, size(unique_names).eq.num_phase, &
416 "Missing aerosol-phase water species '"//water_name// &
417 "' in phase '"//phase_name//"' in aqueous condensed-"// &
418 "phase Arrhenius reacion.")
419
420 ! Save the ids for water in this phase
421 do i_phase_inst = 1, num_phase
422 water_(i_aero_phase + i_phase_inst) = &
423 aero_rep(i_aero_rep)%val%spec_state_id( &
424 unique_names(i_phase_inst)%string)
425 end do
426
427 deallocate(unique_names)
428
429 else
430
431 ! Set the water ids to -1 for non-aqueous condensed-phase reactions
432 do i_phase_inst = 1, num_phase
433 water_(i_aero_phase + i_phase_inst) = -1
434 end do
435
436 end if
437
438 ! Loop through the reactants
439 do i_spec = 1, num_react_
440
441 ! Get the unique names for the reactants
442 unique_names = aero_rep(i_aero_rep)%val%unique_names( &
443 phase_name = phase_name, spec_name = react_names(i_spec)%string)
444
445 ! Make sure the right number of instances are present
446 call assert_msg(360730267, size(unique_names).eq.num_phase, &
447 "Incorrect instances of reactant '"// &
448 react_names(i_spec)%string//"' in phase '"//phase_name// &
449 "' in a condensed-phase Arrhenius reaction")
450
451 ! Save the state ids for the reactant concentration
452 ! IDs are grouped by phase instance:
453 ! R1(phase1), R2(phase1), ..., R1(phase2)...
454 do i_phase_inst = 1, num_phase
455 react_((i_aero_phase+i_phase_inst-1)*num_react_ + i_spec) = &
456 aero_rep(i_aero_rep)%val%spec_state_id( &
457 unique_names(i_phase_inst)%string)
458 end do
459
460 deallocate(unique_names)
461
462 end do
463
464 ! Loop through the products
465 do i_spec = 1, num_prod_
466
467 ! Get the unique names for the products
468 unique_names = aero_rep(i_aero_rep)%val%unique_names( &
469 phase_name = phase_name, spec_name = prod_names(i_spec)%string)
470
471 ! Make sure the right number of instances are present
472 call assert_msg(399869427, size(unique_names).eq.num_phase, &
473 "Incorrect instances of product '"// &
474 prod_names(i_spec)%string//"' in phase '"//phase_name// &
475 "' in a condensed-phase Arrhenius reaction")
476
477 ! Save the state ids for the product concentration
478 ! IDs are grouped by phase instance:
479 ! P1(phase1), P2(phase1), ..., P1(phase2)...
480 do i_phase_inst = 1, num_phase
481 prod_((i_aero_phase+i_phase_inst-1)*num_prod_ + i_spec) = &
482 aero_rep(i_aero_rep)%val%spec_state_id( &
483 unique_names(i_phase_inst)%string)
484 end do
485
486 deallocate(unique_names)
487
488 end do
489
490 ! Increment the index offset for the next aerosol representation
491 i_aero_phase = i_aero_phase + num_phase
492
493 end do
494
495 end subroutine initialize
496
497!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
498
499 !> Finalize the reaction
500 elemental subroutine finalize(this)
501
502 !> Reaction data
503 type(rxn_condensed_phase_arrhenius_t), intent(inout) :: this
504
505 if (associated(this%property_set)) &
506 deallocate(this%property_set)
507 if (allocated(this%condensed_data_real)) &
508 deallocate(this%condensed_data_real)
509 if (allocated(this%condensed_data_int)) &
510 deallocate(this%condensed_data_int)
511
512 end subroutine finalize
513
514!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
515
Initialize the aerosol representation data, validating component data and loading any required inform...
Get the non-unique name of a chemical species by its unique name.
Get a list of unique names for each element on the camp_camp_state::camp_state_t::state_var array for...
Interface for to_string functions.
Definition util.F90:32
The abstract aero_phase_data_t structure and associated subroutines.
elemental subroutine finalize(this)
Finalize the aerosol phase data.
type(aero_phase_data_t) function, pointer constructor(phase_name, init_size)
Constructor for aero_phase_data_t.
The abstract aero_rep_data_t structure and associated subroutines.
The camp_state_t structure and associated subroutines.
Definition camp_state.F90:9
The chem_spec_data_t structure and associated subroutines.
Physical constants.
Definition constants.F90:9
integer, parameter dp
Kind of a double precision real number.
Definition constants.F90:16
type(const_t), save const
Fixed variable for accessing the constant's values.
Definition constants.F90:77
integer, parameter i_kind
Kind of an integer.
Definition constants.F90:21
The property_t structure and associated subroutines.
Definition property.F90:9
The rxn_condensed_phase_arrhenius_t type and associated functions.
The rxn_data_t structure and associated subroutines.
Definition rxn_data.F90:60
integer(kind=i_kind), parameter, public aero_rxn
Aerosol-phase reaction.
Definition rxn_data.F90:88
Common utility subroutines.
Definition util.F90:9
subroutine assert(code, condition_ok)
Errors unless condition_ok is true.
Definition util.F90:165
subroutine die_msg(code, error_msg)
Error immediately.
Definition util.F90:196
subroutine assert_msg(code, condition_ok, error_msg)
Errors unless condition_ok is true.
Definition util.F90:130
Pointer to aero_rep_data_t extending types.
Abstract reaction data type.
Definition rxn_data.F90:98
String type for building arrays of string of various size.
Definition util.F90:38