CAMP 1.0.0
Chemistry Across Multiple Phases
rxn_wennberg_no_ro2.F90
Go to the documentation of this file.
1! Copyright (C) 2021 Barcelona Supercomputing Center,
2! University of Illinois at Urbana-Champaign, and
3! National Center for Atmospheric Research
4! SPDX-License-Identifier: MIT
5
6!> \file
7!> The camp_rxn_wennberg_no_ro2 module.
8
9!> \page camp_rxn_wennberg_no_ro2 CAMP: Wennberg NO + RO2 Reaction
10!!
11!! Wennberg NO + RO2 reactions are branched reactions with one branch forming
12!! alkoxy radicals plus \f$\ce{NO2}\f$ and the other forming organic nitrates
13!! \cite Wennberg2018 . The rate constants for each branch are based on an
14!! Arrhenius rate constant and a temperature- and structure-dependent
15!! branching ratio calculated as:
16!!
17!! \f{align}{
18!! k_{nitrate} & = \left(X e^{-Y/T}\right) \left(\frac{A(T, \mbox{[M]}, n)}{A(T, \mbox{[M]}, n) + Z}\right) \\
19!! k_{alkoxy} & = \left(X e^{-Y/T}\right)\left(\frac{Z}{Z + A(T, \mbox{[M]}, n)}\right) \\
20!! A(T, \mbox{[M]}, n) & = \frac{2 \times 10^{-22} e^n \mbox{[M]}}{1 + \frac{2 \times 10^{-22} e^n \mbox{[M]}}{0.43(T/298)^{-8}}} 0.41^{(1+[log( \frac{2 \times 10^{-22} e^n \mbox{[M]}}{0.43(T/298)^{-8}})]^2)^{-1}}
21!! \f}
22!!
23!! where \f$T\f$ is temperature (K), [M] is the number density of air
24!! (molecules \f$\mbox{cm}^{-3}\f$), \f$X\f$ and \f$Y\f$ are Arrhenius
25!! parameters for the overall reaction, \f$n\f$ is the number of heavy atoms
26!! in the \f$\ce{RO2}\f$ reacting species (excluding the peroxy moiety), and
27!! \f$Z\f$ is defined as a function of two parameters (\f$\alpha_0, n\f$):
28!!
29!! \f[
30!! Z( \alpha_0, n) = A(T = 293 \mbox{K}, \mbox{[M]} = 2.45 \times 10^{19} \frac{\mbox{molec}}{\mbox{cm}^3}, n) \frac{(1-\alpha_0)}{\alpha_0}
31!! \f]
32!!
33!! More details can be found in Wennberg et al. (2018) \cite Wennberg2018 .
34!!
35!! Input data for Wennberg NO + RO2 equations has the following format:
36!! \code{.json}
37!! {
38!! "type" : "WENNBERG_NO_RO2",
39!! "X" : 123.45,
40!! "Y" : 1200.0,
41!! "a0" : 1.0e8,
42!! "n" : 6,
43!! "time unit" : "MIN",
44!! "reactants" : {
45!! "spec1" : {},
46!! "spec2" : { "qty" : 2 },
47!! ...
48!! },
49!! "alkoxy products" : {
50!! "spec3" : {},
51!! "spec4" : { "yield" : 0.65 },
52!! ...
53!! },
54!! "nitrate products" : {
55!! "spec5" : {},
56!! "spec6" : { "yield" : 0.32 },
57!! ...
58!! }
59!! }
60!! \endcode
61!! The key-value pairs \b reactants, and both sets of \b products are
62!! required. Reactants without a \b qty value are assumed to appear once in
63!! the reaction equation. Products without a specified \b yield are assumed
64!! to have a \b yield of 1.0.
65!!
66!! When \b X is not included, it is assumed to be 1.0, when \b Y is not
67!! included, it is assumed to be 0.0 K, when \b a0 is not included, it is
68!! assumed to be 1.0, and when \b n is not included, it is assumed to be 0.
69!! The unit for time is assumed to be s, but inclusion of the optional
70!! key-value pair \b time \b unit = \b MIN can be used to indicate a rate
71!! with min as the time unit.
72
73!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
74
75!> The rxn_wennberg_no_ro2_t type and associated functions.
77
81 use camp_constants, only: const
85 use camp_util, only: i_kind, dp, to_string, &
87
88 implicit none
89 private
90
91#define NUM_REACT_ this%condensed_data_int(1)
92#define NUM_ALKOXY_PROD_ this%condensed_data_int(2)
93#define NUM_NITRATE_PROD_ this%condensed_data_int(3)
94#define X_ this%condensed_data_real(1)
95#define Y_ this%condensed_data_real(2)
96#define a0_ this%condensed_data_real(3)
97#define n_ this%condensed_data_real(4)
98#define CONV_ this%condensed_data_real(5)
99#define NUM_INT_PROP_ 3
100#define NUM_REAL_PROP_ 5
101#define NUM_ENV_PARAM_ 2
102#define REACT_(x) this%condensed_data_int(NUM_INT_PROP_ + x)
103#define PROD_(x) this%condensed_data_int(NUM_INT_PROP_ + NUM_REACT_ + x)
104#define DERIV_ID_(x) this%condensed_data_int(NUM_INT_PROP_ + NUM_REACT_ + NUM_ALKOXY_PROD_ + NUM_NITRATE_PROD_ + x)
105#define JAC_ID_(x) this%condensed_data_int(NUM_INT_PROP_ + 2*(NUM_REACT_ + NUM_ALKOXY_PROD_ + NUM_NITRATE_PROD_) + x)
106#define YIELD_(x) this%condensed_data_real(NUM_REAL_PROP_ + x)
107
108 public :: rxn_wennberg_no_ro2_t
109
110 !> Generic test reaction data type
111 type, extends(rxn_data_t) :: rxn_wennberg_no_ro2_t
112 contains
113 !> Reaction initialization
114 procedure :: initialize
115 !> Finalize the reaction
117 end type rxn_wennberg_no_ro2_t
118
119 !> Constructor for rxn_wennberg_no_ro2_t
121 procedure :: constructor
122 end interface rxn_wennberg_no_ro2_t
123
124contains
125
126!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
127
128 !> Constructor for Wennberg no_ro2 reaction
129 function constructor() result(new_obj)
130
131 !> A new reaction instance
132 type(rxn_wennberg_no_ro2_t), pointer :: new_obj
133
134 allocate(new_obj)
135 new_obj%rxn_phase = gas_rxn
136
137 end function constructor
138
139!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
140
141 !> Initialize the reaction data, validating component data and loading
142 !! any required information into the condensed data arrays for use during
143 !! solving
144 subroutine initialize(this, chem_spec_data, aero_phase, aero_rep, n_cells)
145
146 !> Reaction data
147 class(rxn_wennberg_no_ro2_t), intent(inout) :: this
148 !> Chemical species data
149 type(chem_spec_data_t), intent(in) :: chem_spec_data
150 !> Aerosol phase data
151 type(aero_phase_data_ptr), intent(in) :: aero_phase(:)
152 !> Aerosol representations
153 type(aero_rep_data_ptr), pointer, intent(in) :: aero_rep(:)
154 !> Number of grid cells being solved simultaneously
155 integer(kind=i_kind), intent(in) :: n_cells
156
157 type(property_t), pointer :: spec_props, reactants, alkoxy_products, &
158 nitrate_products
159 character(len=:), allocatable :: key_name, spec_name, string_val
160 integer(kind=i_kind) :: i_spec, i_qty
161
162 integer(kind=i_kind) :: temp_int
163 real(kind=dp) :: temp_real
164
165 ! Get the species involved
166 if (.not. associated(this%property_set)) call die_msg(573144252, &
167 "Missing property set needed to initialize reaction")
168 key_name = "reactants"
169 call assert_msg(350413096, &
170 this%property_set%get_property_t(key_name, reactants), &
171 "Wennberg NO + RO2 reaction is missing reactants")
172 key_name = "alkoxy products"
173 call assert_msg(575049786, &
174 this%property_set%get_property_t(key_name, alkoxy_products), &
175 "Wennberg NO + RO2 reaction is missing alkoxy products")
176 key_name = "nitrate products"
177 call assert_msg(794422169, &
178 this%property_set%get_property_t(key_name, nitrate_products), &
179 "Wennberg NO + RO2 reaction is missing nitrate products")
180
181 ! Count the number of reactants (including those with a qty specified)
182 call reactants%iter_reset()
183 i_spec = 0
184 do while (reactants%get_key(spec_name))
185 ! Get properties included with this reactant in the reaction data
186 call assert(626170799, reactants%get_property_t(val=spec_props))
187 key_name = "qty"
188 if (spec_props%get_int(key_name, temp_int)) i_spec = i_spec+temp_int-1
189 call reactants%iter_next()
190 i_spec = i_spec + 1
191 end do
192
193 ! Allocate space in the condensed data arrays
194 allocate(this%condensed_data_int(num_int_prop_ + &
195 (i_spec + 2) * (i_spec + alkoxy_products%size() + &
196 nitrate_products%size())))
197 allocate(this%condensed_data_real(num_real_prop_ + &
198 alkoxy_products%size() + nitrate_products%size()))
199 this%condensed_data_int(:) = int(0, kind=i_kind)
200 this%condensed_data_real(:) = real(0.0, kind=dp)
201
202 ! Save space for the environment dependent parameters
203 this%num_env_params = num_env_param_
204
205 ! Save the size of the reactant and product arrays (for reactions where
206 ! these can vary)
207 num_react_ = i_spec
208 num_alkoxy_prod_ = alkoxy_products%size()
209 num_nitrate_prod_ = nitrate_products%size()
210
211 ! Set the #/cc -> ppm conversion prefactor
212 conv_ = const%avagadro / const%univ_gas_const * 10.0d0**(-12.0d0)
213
214 ! Get reaction parameters (it might be easiest to keep these at the
215 ! beginning of the condensed data array, so they can be accessed using
216 ! compliler flags)
217 key_name = "X"
218 if (.not. this%property_set%get_real(key_name, x_)) then
219 x_ = 1.0
220 end if
221 key_name = "time unit"
222 if (this%property_set%get_string(key_name, string_val)) then
223 if (trim(string_val).eq."MIN") then
224 x_ = x_ / 60.0
225 end if
226 endif
227 key_name = "Y"
228 if (.not. this%property_set%get_real(key_name, y_)) then
229 y_ = 0.0
230 end if
231 key_name = "a0"
232 if (.not. this%property_set%get_real(key_name, a0_)) then
233 a0_ = 1.0
234 end if
235 key_name = "n"
236 if (.not. this%property_set%get_real(key_name, n_)) then
237 n_ = 0.0
238 end if
239
240 ! Get the indices and chemical properties for the reactants
241 call reactants%iter_reset()
242 i_spec = 1
243 do while (reactants%get_key(spec_name))
244
245 ! Save the index of this species in the state variable array
246 react_(i_spec) = chem_spec_data%gas_state_id(spec_name)
247
248 ! Make sure the species exists
249 call assert_msg(716430972, react_(i_spec).gt.0, &
250 "Missing Wennberg NO + RO2 reactant: "//spec_name)
251
252 ! Get properties included with this reactant in the reaction data
253 call assert(493699816, reactants%get_property_t(val=spec_props))
254 key_name = "qty"
255 if (spec_props%get_int(key_name, temp_int)) then
256 do i_qty = 1, temp_int - 1
257 react_(i_spec + i_qty) = react_(i_spec)
258 end do
259 i_spec = i_spec + temp_int - 1
260 end if
261
262 call reactants%iter_next()
263 i_spec = i_spec + 1
264 end do
265
266 ! Get the indices and chemical properties for the products
267 call alkoxy_products%iter_reset()
268 i_spec = 1
269 do while (alkoxy_products%get_key(spec_name))
270
271 ! Save the index of this species in the state variable array
272 prod_(i_spec) = chem_spec_data%gas_state_id(spec_name)
273
274 ! Make sure the species exists
275 call assert_msg(825390544, prod_(i_spec).gt.0, &
276 "Missing Wennberg NO + RO2 alkoxy product: "//spec_name)
277
278 ! Get properties included with this product in the reaction data
279 call assert(879870330, alkoxy_products%get_property_t(val=spec_props))
280 key_name = "yield"
281 if (spec_props%get_real(key_name, temp_real)) then
282 yield_(i_spec) = temp_real
283 else
284 yield_(i_spec) = 1.0
285 end if
286
287 call alkoxy_products%iter_next()
288 i_spec = i_spec + 1
289 end do
290 call nitrate_products%iter_reset()
291 do while (nitrate_products%get_key(spec_name))
292
293 ! Save the index of this species in the state variable array
294 prod_(i_spec) = chem_spec_data%gas_state_id(spec_name)
295
296 ! Make sure the species exists
297 call assert_msg(590677535, prod_(i_spec).gt.0, &
298 "Missing Wennberg NO + RO2 nitrate product: "//spec_name)
299
300 ! Get properties included with this product in the reaction data
301 call assert(815314225, nitrate_products%get_property_t(val=spec_props))
302 key_name = "yield"
303 if (spec_props%get_real(key_name, temp_real)) then
304 yield_(i_spec) = temp_real
305 else
306 yield_(i_spec) = 1.0
307 end if
308
309 call nitrate_products%iter_next()
310 i_spec = i_spec + 1
311 end do
312 call assert(699637107, i_spec - 1 .eq. &
313 num_alkoxy_prod_ + num_nitrate_prod_ )
314
315 end subroutine initialize
316
317!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
318
319 !> Finalize the reaction
320 subroutine finalize(this)
321
322 !> Reaction data
323 type(rxn_wennberg_no_ro2_t), intent(inout) :: this
324
325 if (associated(this%property_set)) &
326 deallocate(this%property_set)
327 if (allocated(this%condensed_data_real)) &
328 deallocate(this%condensed_data_real)
329 if (allocated(this%condensed_data_int)) &
330 deallocate(this%condensed_data_int)
331
332 end subroutine finalize
333
334!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
335
336 !> Finalize an array of reactions
337 subroutine finalize_array(this)
338
339 !> Array of reaction data
340 type(rxn_wennberg_no_ro2_t), intent(inout) :: this(:)
341
342 integer(kind=i_kind) :: i_rxn
343
344 do i_rxn = 1, size(this)
345 call finalize(this(i_rxn))
346 end do
347
348 end subroutine finalize_array
349
350!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
351
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.
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_data_t structure and associated subroutines.
Definition rxn_data.F90:60
integer(kind=i_kind), parameter, public gas_rxn
Gas-phase reaction.
Definition rxn_data.F90:85
The rxn_wennberg_no_ro2_t type and associated functions.
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
#define a0_
#define n_
Pointer type for building arrays.
Pointer to aero_rep_data_t extending types.
Abstract reaction data type.
Definition rxn_data.F90:99