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
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_phase, 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 phase data
160 type(aero_phase_data_ptr), intent(in) :: aero_phase(:)
161 !> Aerosol representations
162 type(aero_rep_data_ptr), pointer, intent(in) :: aero_rep(:)
163 !> Number of grid cells to solve simultaneously
164 integer(kind=i_kind), intent(in) :: n_cells
165
166 type(property_t), pointer :: spec_props, reactants, products
167 character(len=:), allocatable :: key_name, spec_name, water_name, &
168 phase_name, temp_string
169 integer(kind=i_kind) :: i_spec, i_phase_inst, i_qty, i_aero_rep, &
170 i_aero_phase, num_spec_per_phase, num_phase, num_react, num_prod
171 type(string_t), allocatable :: unique_names(:)
172 type(string_t), allocatable :: react_names(:), prod_names(:)
173
174 integer(kind=i_kind) :: temp_int
175 real(kind=dp) :: temp_real
176 logical :: is_aqueous
177
178 is_aqueous = .false.
179
180 ! Get the property set
181 call assert_msg(852163263, associated(this%property_set), &
182 "Missing property set needed to initialize reaction")
183
184 ! Get the aerosol phase name
185 key_name = "aerosol phase"
186 call assert_msg(845445717, &
187 this%property_set%get_string(key_name, phase_name), &
188 "Missing aerosol phase in condensed-phase Arrhenius reaction")
189
190 ! Get reactant species
191 key_name = "reactants"
192 call assert_msg(501773136, &
193 this%property_set%get_property_t(key_name, reactants), &
194 "Missing reactant species in condensed-phase Arrhenius reaction")
195
196 ! Get product species
197 key_name = "products"
198 call assert_msg(556252922, &
199 this%property_set%get_property_t(key_name, products), &
200 "Missing product species in condensed-phase Arrhenius reaction")
201
202 ! Count the number of species per phase instance (including reactants
203 ! with a "qty" specified)
204 call reactants%iter_reset()
205 num_react = 0
206 do while (reactants%get_key(spec_name))
207 ! Get properties included with this reactant in the reaction data
208 call assert(428593951, reactants%get_property_t(val=spec_props))
209 key_name = "qty"
210 if (spec_props%get_int(key_name, temp_int)) &
211 num_react = num_react + temp_int - 1
212 call reactants%iter_next()
213 num_react = num_react + 1
214 end do
215 num_prod = products%size()
216 num_spec_per_phase = num_prod + num_react
217
218 ! Check for aerosol representations
219 call assert_msg(370755392, associated(aero_rep), &
220 "Missing aerosol representation for condensed-phase "// &
221 "Arrhenius reaction")
222 call assert_msg(483073737, size(aero_rep).gt.0, &
223 "Missing aerosol representation for condensed-phase "// &
224 "Arrhenius reaction")
225
226 ! Count the instances of the specified aerosol phase
227 num_phase = 0
228 do i_aero_rep = 1, size(aero_rep)
229 num_phase = num_phase + &
230 aero_rep(i_aero_rep)%val%num_phase_instances(phase_name)
231 end do
232
233 ! Allocate space in the condensed data arrays
234 allocate(this%condensed_data_int(num_int_prop_ + &
235 num_phase * (num_spec_per_phase * (num_react + 3) + 1)))
236 allocate(this%condensed_data_real(num_real_prop_ + &
237 num_spec_per_phase + num_prod))
238 this%condensed_data_int(:) = int(0, kind=i_kind)
239 this%condensed_data_real(:) = real(0.0, kind=dp)
240
241 ! Save space for the environment-dependent parameters
242 this%num_env_params = num_env_param_
243
244 ! Set the number of products, reactants and aerosol phase instances
245 num_react_ = num_react
246 num_prod_ = num_prod
247 num_aero_phase_ = num_phase
248
249 ! Get the rate constant parameters
250 key_name = "A"
251 if (.not. this%property_set%get_real(key_name, a_)) then
252 a_ = 1.0
253 end if
254 key_name = "time unit"
255 if (this%property_set%get_string(key_name, temp_string)) then
256 if (trim(temp_string).eq."MIN") then
257 a_ = a_ / 60.0
258 else
259 call assert_msg(390870843, trim(temp_string).eq."s", &
260 "Received invalid time unit: '"//temp_string//"' in "// &
261 "condnesed-phase Arrhenius reaction. Valid units are "// &
262 "'MIN' and 's'.")
263 end if
264 end if
265 key_name = "Ea"
266 if (this%property_set%get_real(key_name, temp_real)) then
267 c_ = -temp_real/const%boltzmann
268 key_name = "C"
269 call assert_msg(827485736, &
270 .not.this%property_set%get_real(key_name, temp_real), &
271 "Received both Ea and C parameter for condensed-phase "// &
272 "Arrhenius equation.")
273 else
274 key_name = "C"
275 if (.not. this%property_set%get_real(key_name, c_)) then
276 c_ = 0.0
277 end if
278 end if
279 key_name = "D"
280 if (.not. this%property_set%get_real(key_name, d_)) then
281 d_ = 300.0
282 end if
283 key_name = "B"
284 if (.not. this%property_set%get_real(key_name, b_)) then
285 b_ = 0.0
286 end if
287 key_name = "E"
288 if (.not. this%property_set%get_real(key_name, e_)) then
289 e_ = 0.0
290 end if
291
292 ! Set up an array to the reactant and product names
293 allocate(react_names(num_react_))
294 allocate(prod_names(num_prod_))
295
296 ! Get the chemical properties for the reactants
297 call reactants%iter_reset()
298 i_spec = 0
299 do while (reactants%get_key(spec_name))
300
301 ! Get the reactant species properties
302 call assert_msg(365229559, &
303 chem_spec_data%get_property_set(spec_name, spec_props), &
304 "Missing properties required for condensed-phase Arrhenius "// &
305 "reaction involving species '"//trim(spec_name)//"'")
306
307 ! Get the molecular weight
308 key_name = "molecular weight [kg mol-1]"
309 call assert_msg(409180731, spec_props%get_real(key_name, temp_real), &
310 "Missing 'molecular weight' for species '"//trim(spec_name)// &
311 "' in condensed-phase Arrhenius reaction.")
312
313 ! Set properties for each occurance of a reactant in the rxn equation
314 call assert(186449575, reactants%get_property_t(val=spec_props))
315 key_name = "qty"
316 if (.not.spec_props%get_int(key_name, temp_int)) temp_int = 1
317 do i_qty = 1, temp_int
318 i_spec = i_spec + 1
319
320 ! Add the reactant name to the list
321 react_names(i_spec)%string = spec_name
322
323 ! Use the MW to calculate the kg/m3 -> mol/m3 conversion
324 kgm3_to_molm3_(i_spec) = 1.0/temp_real
325
326 end do
327
328 ! Go to the next reactant
329 call reactants%iter_next()
330
331 end do
332
333 ! Get the chemical properties for the products
334 call products%iter_reset()
335 i_spec = 0
336 do while (products%get_key(spec_name))
337
338 ! Get the product species properties
339 call assert_msg(450225425, &
340 chem_spec_data%get_property_set(spec_name, spec_props), &
341 "Missing properties required for condensed-phase Arrhenius "// &
342 "reaction involving species '"//trim(spec_name)//"'")
343
344 ! Increment the product counter
345 i_spec = i_spec + 1
346
347 ! Get the molecular weight
348 key_name = "molecular weight [kg mol-1]"
349 call assert_msg(504705211, spec_props%get_real(key_name, temp_real), &
350 "Missing 'molecular weight' for species '"//trim(spec_name)// &
351 "' in condensed phase Arrhenius reaction.")
352
353 ! Use the MW to calculate the kg/m3 -> mol/m3 conversion
354 kgm3_to_molm3_(num_react_+i_spec) = 1.0/temp_real
355
356 ! Set properties for each occurance of a reactant in the rxn equation
357 call assert(846924553, products%get_property_t(val=spec_props))
358 key_name = "yield"
359 if (spec_props%get_real(key_name, temp_real)) then
360 yield_(i_spec) = temp_real
361 else
362 yield_(i_spec) = 1.0d0
363 end if
364
365 ! Add the product name to the list
366 prod_names(i_spec)%string = spec_name
367
368 ! Go to the next product
369 call products%iter_next()
370
371 end do
372
373 ! Get the units for the reactants and products and name for water
374 ! if this is an aqueous reaction
375 key_name = "units"
376 call assert_msg(348722817, &
377 this%property_set%get_string(key_name, temp_string), &
378 "Missing units for condensed-phase Arrhenius reaction.")
379 if (trim(temp_string).eq."mol m-3") then
380 is_aqueous = .false.
381 key_name = "aerosol-phase water"
382 call assert_msg(767767240, &
383 .not.this%property_set%get_string(key_name, temp_string), &
384 "Aerosol-phase water specified for non-aqueous condensed-"// &
385 "phase Arrhenius reaction. Change units to 'M' or remove "// &
386 "aerosol-phase water")
387 else if (trim(temp_string).eq."M") then
388 is_aqueous = .true.
389 key_name = "aerosol-phase water"
390 call assert_msg(199910264, &
391 this%property_set%get_string(key_name, water_name), &
392 "Missing aerosol-phase water for aqeuous condensed-phase "// &
393 "Arrhenius reaction.")
394 else
395 call die_msg(161772048, "Received invalid units for condensed-"// &
396 "phase Arrhenius reaction: '"//temp_string//"'. Valid "// &
397 "units are 'mol m-3' or 'M'.")
398 end if
399
400 ! Set the state array indices for the reactants, products and water
401 i_aero_phase = 0
402 do i_aero_rep = 1, size(aero_rep)
403
404 ! Check for the specified phase in this aero rep
405 num_phase = aero_rep(i_aero_rep)%val%num_phase_instances(phase_name)
406 if (num_phase.eq.0) cycle
407
408 ! Save the state ids for aerosol-phase water for aqueous reactions.
409 ! For non-aqueous reactions, set the water id to -1
410 if (is_aqueous) then
411
412 ! Get the unique names for aerosol-phase water
413 unique_names = aero_rep(i_aero_rep)%val%unique_names( &
414 phase_name = phase_name, spec_name = water_name)
415
416 ! Make sure water is present
417 call assert_msg(196838614, size(unique_names).eq.num_phase, &
418 "Missing aerosol-phase water species '"//water_name// &
419 "' in phase '"//phase_name//"' in aqueous condensed-"// &
420 "phase Arrhenius reacion.")
421
422 ! Save the ids for water in this phase
423 do i_phase_inst = 1, num_phase
424 water_(i_aero_phase + i_phase_inst) = &
425 aero_rep(i_aero_rep)%val%spec_state_id( &
426 unique_names(i_phase_inst)%string)
427 end do
428
429 deallocate(unique_names)
430
431 else
432
433 ! Set the water ids to -1 for non-aqueous condensed-phase reactions
434 do i_phase_inst = 1, num_phase
435 water_(i_aero_phase + i_phase_inst) = -1
436 end do
437
438 end if
439
440 ! Loop through the reactants
441 do i_spec = 1, num_react_
442
443 ! Get the unique names for the reactants
444 unique_names = aero_rep(i_aero_rep)%val%unique_names( &
445 phase_name = phase_name, spec_name = react_names(i_spec)%string)
446
447 ! Make sure the right number of instances are present
448 call assert_msg(360730267, size(unique_names).eq.num_phase, &
449 "Incorrect instances of reactant '"// &
450 react_names(i_spec)%string//"' in phase '"//phase_name// &
451 "' in a condensed-phase Arrhenius reaction")
452
453 ! Save the state ids for the reactant concentration
454 ! IDs are grouped by phase instance:
455 ! R1(phase1), R2(phase1), ..., R1(phase2)...
456 do i_phase_inst = 1, num_phase
457 react_((i_aero_phase+i_phase_inst-1)*num_react_ + i_spec) = &
458 aero_rep(i_aero_rep)%val%spec_state_id( &
459 unique_names(i_phase_inst)%string)
460 end do
461
462 deallocate(unique_names)
463
464 end do
465
466 ! Loop through the products
467 do i_spec = 1, num_prod_
468
469 ! Get the unique names for the products
470 unique_names = aero_rep(i_aero_rep)%val%unique_names( &
471 phase_name = phase_name, spec_name = prod_names(i_spec)%string)
472
473 ! Make sure the right number of instances are present
474 call assert_msg(399869427, size(unique_names).eq.num_phase, &
475 "Incorrect instances of product '"// &
476 prod_names(i_spec)%string//"' in phase '"//phase_name// &
477 "' in a condensed-phase Arrhenius reaction")
478
479 ! Save the state ids for the product concentration
480 ! IDs are grouped by phase instance:
481 ! P1(phase1), P2(phase1), ..., P1(phase2)...
482 do i_phase_inst = 1, num_phase
483 prod_((i_aero_phase+i_phase_inst-1)*num_prod_ + i_spec) = &
484 aero_rep(i_aero_rep)%val%spec_state_id( &
485 unique_names(i_phase_inst)%string)
486 end do
487
488 deallocate(unique_names)
489
490 end do
491
492 ! Increment the index offset for the next aerosol representation
493 i_aero_phase = i_aero_phase + num_phase
494
495 end do
496
497 end subroutine initialize
498
499!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
500
501 !> Finalize the reaction
502 subroutine finalize(this)
503
504 !> Reaction data
505 type(rxn_condensed_phase_arrhenius_t), intent(inout) :: this
506
507 if (associated(this%property_set)) &
508 deallocate(this%property_set)
509 if (allocated(this%condensed_data_real)) &
510 deallocate(this%condensed_data_real)
511 if (allocated(this%condensed_data_int)) &
512 deallocate(this%condensed_data_int)
513
514 end subroutine finalize
515
516!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
517
518 !> Finalize an array of reactions
519 subroutine finalize_array(this)
520
521 !> Array of reaction data
522 type(rxn_condensed_phase_arrhenius_t), intent(inout) :: this(:)
523
524 integer(kind=i_kind) :: i_rxn
525
526 do i_rxn = 1, size(this)
527 call finalize(this(i_rxn))
528 end do
529
530 end subroutine finalize_array
531
532!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
533
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.
subroutine finalize_array(this)
Finalize the aerosol phase data.
type(aero_phase_data_t) function, pointer constructor(phase_name, init_size)
Constructor for aero_phase_data_t.
subroutine finalize(this)
Finalize the aerosol phase data.
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:89
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 type for building arrays.
Pointer to aero_rep_data_t extending types.
Abstract reaction data type.
Definition rxn_data.F90:99
String type for building arrays of string of various size.
Definition util.F90:53