CAMP 1.0.0
Chemistry Across Multiple Phases
aero_rep_modal_binned_mass.c
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 * Modal mass aerosol representation functions
6 *
7 */
8/** \file
9 * \brief Modal mass aerosol representation functions
10 */
11#include <math.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <camp/aero_phase_solver.h>
15#include <camp/aero_reps.h>
16#include <camp/camp_solver.h>
17
18// TODO Lookup environmental indicies during initialization
19#define TEMPERATURE_K_ env_data[0]
20#define PRESSURE_PA_ env_data[1]
21
22#define UPDATE_GMD 0
23#define UPDATE_GSD 1
24
25#define BINNED 1
26#define MODAL 2
27
28#define NUM_SECTION_ (int_data[0])
29#define INT_DATA_SIZE_ (int_data[1])
30#define FLOAT_DATA_SIZE_ (int_data[2])
31#define AERO_REP_ID_ (int_data[3])
32#define NUM_INT_PROP_ 4
33#define NUM_FLOAT_PROP_ 0
34#define NUM_ENV_PARAM_ 0
35#define MODE_INT_PROP_LOC_(x) (int_data[NUM_INT_PROP_ + x] - 1)
36#define MODE_FLOAT_PROP_LOC_(x) (int_data[NUM_INT_PROP_ + NUM_SECTION_ + x] - 1)
37#define SECTION_TYPE_(x) (int_data[MODE_INT_PROP_LOC_(x)])
38
39// For modes, NUM_BINS_ = 1
40#define NUM_BINS_(x) (int_data[MODE_INT_PROP_LOC_(x) + 1])
41
42// Number of aerosol phases in this mode/bin set
43#define NUM_PHASE_(x) (int_data[MODE_INT_PROP_LOC_(x) + 2])
44
45// Phase state and model data ids
46#define PHASE_STATE_ID_(x, y, b) \
47 (int_data[MODE_INT_PROP_LOC_(x) + 3 + b * NUM_PHASE_(x) + y] - 1)
48#define PHASE_MODEL_DATA_ID_(x, y, b) \
49 (int_data[MODE_INT_PROP_LOC_(x) + 3 + NUM_BINS_(x) * NUM_PHASE_(x) + \
50 b * NUM_PHASE_(x) + y] - \
51 1)
52
53// Number of Jacobian elements in a phase
54#define PHASE_NUM_JAC_ELEM_(x, y, b) \
55 int_data[MODE_INT_PROP_LOC_(x) + 3 + 2 * NUM_BINS_(x) * NUM_PHASE_(x) + \
56 b * NUM_PHASE_(x) + y]
57
58// Bin diameter (for bins)
59#define BIN_DP_(x, b) (float_data[MODE_FLOAT_PROP_LOC_(x) + b * 3])
60
61// GMD and GSD - only used for modes
62#define GMD_(x) (aero_rep_env_data[x])
63#define GSD_(x) (aero_rep_env_data[NUM_SECTION_ + x])
64
65// Real-time number concentration - used for modes and bins - for modes, b=0
66#define NUMBER_CONC_(x, b) (float_data[MODE_FLOAT_PROP_LOC_(x) + b * 3 + 1])
67
68// Real-time effective radius - for modes, b=0
69#define EFFECTIVE_RADIUS_(x, b) \
70 (float_data[MODE_FLOAT_PROP_LOC_(x) + b * 3 + 2])
71
72// Real-time phase mass (kg/m^3) - used for modes and bins - for modes, b=0
73#define PHASE_MASS_(x, y, b) \
74 (float_data[MODE_FLOAT_PROP_LOC_(x) + 3 * NUM_BINS_(x) + b * NUM_PHASE_(x) + \
75 y])
76
77// Real-time phase average MW (kg/mol) - used for modes and bins - for modes,
78// b=0
79#define PHASE_AVG_MW_(x, y, b) \
80 (float_data[MODE_FLOAT_PROP_LOC_(x) + (3 + NUM_PHASE_(x)) * NUM_BINS_(x) + \
81 b * NUM_PHASE_(x) + y])
82
83/** \brief Flag Jacobian elements used in calcualtions of mass and volume
84 *
85 * \param model_data Pointer to the model data
86 * \param aero_rep_int_data Pointer to the aerosol representation integer data
87 * \param aero_rep_float_data Pointer to the aerosol representation
88 * floating-point data
89 * \param aero_phase_idx Index of the aerosol phase to find elements for
90 * \param jac_struct 1D array of flags indicating potentially non-zero
91 * Jacobian elements. (The dependent variable should have
92 * been chosen by the calling function.)
93 * \return Number of Jacobian elements flagged
94 */
96 int aero_phase_idx,
97 int *aero_rep_int_data,
98 double *aero_rep_float_data,
99 bool *jac_struct) {
100 int *int_data = aero_rep_int_data;
101 double *float_data = aero_rep_float_data;
102
103 int num_flagged_elem = 0;
104
105 // Loop through the modes/bins flagging Jacobian elements used by each
106 // aerosol phase
107 for (int i_section = 0; i_section < NUM_SECTION_ && aero_phase_idx >= 0;
108 i_section++) {
109 for (int i_bin = 0; i_bin < NUM_BINS_(i_section) && aero_phase_idx >= 0;
110 i_bin++) {
111 for (int i_phase = 0;
112 i_phase < NUM_PHASE_(i_section) && aero_phase_idx >= 0; i_phase++) {
113 if (aero_phase_idx == 0) {
114 for (int j_phase = 0; j_phase < NUM_PHASE_(i_section); j_phase++) {
115 PHASE_NUM_JAC_ELEM_(i_section, j_phase, i_bin) =
117 model_data, PHASE_MODEL_DATA_ID_(i_section, j_phase, i_bin),
118 PHASE_STATE_ID_(i_section, j_phase, i_bin), jac_struct);
119 num_flagged_elem += PHASE_NUM_JAC_ELEM_(i_section, j_phase, i_bin);
120 }
121 }
122 aero_phase_idx -= 1;
123 }
124 }
125 }
126
127 return num_flagged_elem;
128}
129
130/** \brief Flag elements on the state array used by this aerosol representation
131 *
132 * The modal mass aerosol representation functions do not use state array values
133 *
134 * \param aero_rep_int_data Pointer to the aerosol representation integer data
135 * \param aero_rep_float_data Pointer to the aerosol representation
136 * floating-point data
137 * \param state_flags Array of flags indicating state array elements used
138 */
140 double *aero_rep_float_data,
141 bool *state_flags) {
142 int *int_data = aero_rep_int_data;
143 double *float_data = aero_rep_float_data;
144
145 return;
146}
147
148/** \brief Update aerosol representation data for new environmental conditions
149 *
150 * The modal mass aerosol representation is not updated for new environmental
151 * conditions
152 *
153 * \param model_data Pointer to the model data
154 * \param aero_rep_int_data Pointer to the aerosol representation integer data
155 * \param aero_rep_float_data Pointer to the aerosol representation
156 * floating-point data
157 * \param aero_rep_env_data Pointer to the aerosol representation
158 * environment-dependent parameters
159 */
161 int *aero_rep_int_data,
162 double *aero_rep_float_data,
163 double *aero_rep_env_data) {
164 int *int_data = aero_rep_int_data;
165 double *float_data = aero_rep_float_data;
166 double *env_data = model_data->grid_cell_env;
167
168 return;
169}
170
171/** \brief Update aerosol representation data for a new state
172 *
173 * The modal mass aerosol representation recalculates effective radius and
174 * number concentration for each new state.
175 *
176 * \param model_data Pointer to the model data, including the state array
177 * \param aero_rep_int_data Pointer to the aerosol representation integer data
178 * \param aero_rep_float_data Pointer to the aerosol representation
179 * floating-point data
180 * \param aero_rep_env_data Pointer to the aerosol representation
181 * environment-dependent parameters
182 */
184 int *aero_rep_int_data,
185 double *aero_rep_float_data,
186 double *aero_rep_env_data) {
187 int *int_data = aero_rep_int_data;
188 double *float_data = aero_rep_float_data;
189
190 // Loop through the modes and calculate effective radius and number
191 // concentration
192 for (int i_section = 0; i_section < NUM_SECTION_; i_section++) {
193 double volume, mass;
194 switch (SECTION_TYPE_(i_section)) {
195 // Mode
196 case (MODAL):
197
198 // Sum the volumes of each species in the mode [m3 m-3]
199 volume = 0.0;
200 for (int i_phase = 0; i_phase < NUM_PHASE_(i_section); i_phase++) {
201 // Get a pointer to the phase on the state array
202 double *state = (double *)(model_data->grid_cell_state);
203 state += PHASE_STATE_ID_(i_section, i_phase, 0);
204
205 // Set the aerosol-phase mass [kg m-3] and average MW [kg mol-1]
207 model_data, PHASE_MODEL_DATA_ID_(i_section, i_phase, 0), state,
208 &(PHASE_MASS_(i_section, i_phase, 0)),
209 &(PHASE_AVG_MW_(i_section, i_phase, 0)), NULL, NULL);
210
211 // Get the phase volume [m3 m-3]
212 double phase_volume = 0.0;
214 model_data, PHASE_MODEL_DATA_ID_(i_section, i_phase, 0), state,
215 &phase_volume, NULL);
216 volume += phase_volume;
217 }
218
219 // Calculate the number concentration [# m-3] based on the total mode
220 // volume (see aero_rep_modal_binned_mass_get_number_conc for details)
221 NUMBER_CONC_(i_section, 0) =
222 volume * 6.0 /
223 (M_PI * pow(GMD_(i_section), 3) *
224 exp(9.0 / 2.0 * pow(log(GSD_(i_section)), 2)));
225
226 break;
227
228 // Bins
229 case (BINNED):
230
231 // Loop through the bins
232 for (int i_bin = 0; i_bin < NUM_BINS_(i_section); i_bin++) {
233 // Sum the volumes of each species in the bin [m3 m-3]
234 volume = 0.0;
235 for (int i_phase = 0; i_phase < NUM_PHASE_(i_section); i_phase++) {
236 // Get a pointer to the phase on the state array
237 double *state = (double *)(model_data->grid_cell_state);
238 state += PHASE_STATE_ID_(i_section, i_phase, i_bin);
239
240 // Set the aerosol-phase mass [kg m-3] and average MW [kg mol-1]
242 model_data, PHASE_MODEL_DATA_ID_(i_section, i_phase, i_bin),
243 state, &(PHASE_MASS_(i_section, i_phase, i_bin)),
244 &(PHASE_AVG_MW_(i_section, i_phase, i_bin)), NULL, NULL);
245
246 // Get the phase volume [m3 m-3]
247 double phase_volume = 0.0;
249 model_data, PHASE_MODEL_DATA_ID_(i_section, i_phase, i_bin),
250 state, &phase_volume, NULL);
251 volume += phase_volume;
252 }
253
254 // Calculate the number concentration [# m-3] based on the total bin
255 // volume (see aero_rep_modal_binned_mass_get_number_conc for details)
256 NUMBER_CONC_(i_section, i_bin) =
257 volume * 3.0 / (4.0 * M_PI) /
258 pow(BIN_DP_(i_section, i_bin) / 2.0, 3);
259 }
260
261 break;
262 }
263 }
264
265 return;
266}
267
268/** \brief Get the effective radius of a specified layer \f$r_{layer}\f$ (m)
269 *
270 * The modal mass aerosol representation does not assume any internal
271 * structure for modes or bins, so the layer radius function always returns
272 * the radius of the particle.
273 *
274 * \param model_data Pointer to the model data, including the state array
275 * \param aero_phase_idx Index of the aerosol phase within the representation
276 * \param layer_radius Effective layer radius (m)
277 * \param partial_deriv \f$\frac{\partial r_{layer}}{\partial y}\f$ where \f$y\f$
278 * are species on the state array
279 * \param aero_rep_int_data Pointer to the aerosol representation integer data
280 * \param aero_rep_float_data Pointer to the aerosol representation
281 * floating-point data
282 * \param aero_rep_env_data Pointer to the aerosol representation
283 * environment-dependent parameters
284 */
286 ModelData *model_data, int aero_phase_idx, double *layer_radius,
287 double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data,
288 double *aero_rep_env_data) {
289 int *int_data = aero_rep_int_data;
290 double *float_data = aero_rep_float_data;
291
293 model_data,
294 aero_phase_idx,
295 layer_radius,
296 partial_deriv,
297 int_data,
298 float_data,
299 aero_rep_env_data);
300
301 return;
302}
303
304/** \brief Get the effective particle radius \f$r_{eff}\f$ (m)
305 *
306 * The modal mass effective radius is calculated for a log-normal distribution
307 * where the geometric mean diameter (\f$\tilde{D}_n\f$) and geometric standard
308 * deviation (\f$\sigma_g\f$) are set by the aerosol model prior to
309 * solving the chemistry. Thus, all \f$\frac{\partial r_{eff}}{\partial y}\f$
310 * are zero. The effective radius is calculated according to the equation given
311 * in Table 1 of Zender \cite Zender2002 :
312 *
313 * \f[
314 * \tilde{\sigma_g} \equiv ln( \sigma_g )
315 * \f]
316 * \f[
317 * D_s = D_{eff} = \tilde{D_n} e^{5 \tilde{\sigma}_g^2 / 2}
318 * \f]
319 * \f[
320 * r_{eff} = \frac{D_{eff}}{2}
321 * \f]
322 *
323 * For bins, \f$r_{eff}\f$ is assumed to be the bin radius.
324 *
325 * \param model_data Pointer to the model data, including the state array
326 * \param aero_phase_idx Index of the aerosol phase within the representation
327 * \param radius Effective particle radius (m)
328 * \param partial_deriv \f$\frac{\partial r_{eff}}{\partial y}\f$ where \f$y\f$
329 * are species on the state array
330 * \param aero_rep_int_data Pointer to the aerosol representation integer data
331 * \param aero_rep_float_data Pointer to the aerosol representation
332 * floating-point data
333 * \param aero_rep_env_data Pointer to the aerosol representation
334 * environment-dependent parameters
335 */
337 ModelData *model_data, int aero_phase_idx, double *radius,
338 double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data,
339 double *aero_rep_env_data) {
340 int *int_data = aero_rep_int_data;
341 double *float_data = aero_rep_float_data;
342
343 for (int i_section = 0; i_section < NUM_SECTION_; i_section++) {
344 for (int i_bin = 0; i_bin < NUM_BINS_(i_section); i_bin++) {
345 aero_phase_idx -= NUM_PHASE_(i_section);
346 if (aero_phase_idx < 0) {
347 *radius = EFFECTIVE_RADIUS_(i_section, i_bin);
348 // Effective radii are constant for bins and modes
349 if (partial_deriv) {
350 for (int i_phase = 0; i_phase < NUM_PHASE_(i_section); ++i_phase) {
351 for (int i_elem = 0;
352 i_elem < PHASE_NUM_JAC_ELEM_(i_section, i_phase, i_bin);
353 ++i_elem) {
354 *(partial_deriv++) = ZERO;
355 }
356 }
357 }
358 i_section = NUM_SECTION_;
359 break;
360 }
361 }
362 }
363
364 return;
365}
366
367/** \brief Get the phase volume \f$V_{phase}\f$ (m³/m³)
368 *
369 * The phase volume is calculated as the sum of the volumes of each species in
370 * the phase, which are calculated by dividing the mass of each species by its
371 * density. The mass of each species is calculated from the state array and
372 * model data using the aero_phase_get_mass function.
373 *
374 * \param model_data Pointer to the model data, including the state array
375 * \param aero_phase_idx Index of the aerosol phase within the representation
376 * \param phase_volume Phase volume (m³/m³)
377 * \param partial_deriv \f$\frac{\partial V_{phase}}{\partial y}\f$ where \f$y\f$
378 * are species on the state array
379 * \param aero_rep_int_data Pointer to the aerosol representation integer data
380 * \param aero_rep_float_data Pointer to the aerosol representation
381 * floating-point data
382 * \param aero_rep_env_data Pointer to the aerosol representation
383 * environment-dependent parameters
384 */
386 ModelData *model_data, int aero_phase_idx, double *phase_volume,
387 double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data,
388 double *aero_rep_env_data) {
389 int *int_data = aero_rep_int_data;
390 double *float_data = aero_rep_float_data;
391
392 for (int i_section = 0; i_section < NUM_SECTION_ && aero_phase_idx >= 0;
393 ++i_section) {
394 for (int i_bin = 0; i_bin < NUM_BINS_(i_section) && aero_phase_idx >= 0;
395 ++i_bin) {
396 if (aero_phase_idx < 0 || aero_phase_idx >= NUM_PHASE_(i_section)) {
397 aero_phase_idx -= NUM_PHASE_(i_section);
398 continue;
399 }
400 for (int i_phase = 0; i_phase < NUM_PHASE_(i_section); ++i_phase) {
401 if (aero_phase_idx == 0) {
402 double *state = (double *)(model_data->grid_cell_state);
403 state += PHASE_STATE_ID_(i_section, i_phase, i_bin);
404 aero_phase_get_volume__m3_m3(model_data, PHASE_MODEL_DATA_ID_(i_section, i_phase, i_bin),
405 state, phase_volume, partial_deriv);
406 if (partial_deriv)
407 partial_deriv +=
408 PHASE_NUM_JAC_ELEM_(i_section, i_phase, i_bin);
409
410 } else if (partial_deriv) {
411
412 for (int i_elem = 0;
413 i_elem < PHASE_NUM_JAC_ELEM_(i_section, i_phase, i_bin);
414 ++i_elem) {
415 *(partial_deriv++) = ZERO;
416 }
417 }
418
419 aero_phase_idx -= 1;
420 }
421 }
422 }
423
424 return;
425}
426
427/** \brief Get the effective particle surface area \f$r_{eff}\f$ (m)
428 *
429 * The surface area interface that exists between two specified phases within
430 * the modal/binned aerosol representation always returns an error, because no
431 * specific internal structure is assumed for phases within modes or bins.
432 *
433 * \param model_data Pointer to the model data, including the state array
434 * \param aero_phase_idx_first Index of the first aerosol phase within the representation
435 * \param aero_phase_idx_second Index of the second aerosol phase within the representation
436 * \param surface_area Effective surface area of interface between phases (m^2)
437 * \param partial_deriv \f$\frac{\partial sa_{eff}}{\partial y}\f$ where \f$y\f$
438 * are species on the state array
439 * \param aero_rep_int_data Pointer to the aerosol representation integer data
440 * \param aero_rep_float_data Pointer to the aerosol representation
441 * floating-point data
442 * \param aero_rep_env_data Pointer to the aerosol representation
443 * environment-dependent parameters
444 */
446 ModelData *model_data, int aero_phase_idx_first, int aero_phase_idx_second,
447 double *surface_area, double *partial_deriv, int *aero_rep_int_data,
448 double *aero_rep_float_data, double *aero_rep_env_data) {
449 int *int_data = aero_rep_int_data;
450 double *float_data = aero_rep_float_data;
451
452
453 printf("\n\nERROR There are no adjacent pairs in the modal/binned representation.\n\n");
454 exit(1);
455}
456
457/** \brief Get the thickness of a particle layer (m)
458 *
459 * The thickness of a particle layer within the modal/binned aerosol
460 * representation always returns the radius.
461 *
462 * \param model_data Pointer to the model data, including the state array
463 * \param aero_phase_idx Index of the aerosol phase within the representation
464 * \param layer_thickness Layer thickness which is equivalent to radius (m)
465 * \param partial_deriv \f$\frac{\partial r_{eff}}{\partial y}\f$ where \f$y\f$
466 * are species on the state array
467 * \param aero_rep_int_data Pointer to the aerosol representation integer data
468 * \param aero_rep_float_data Pointer to the aerosol representation
469 * floating-point data
470 * \param aero_rep_env_data Pointer to the aerosol representation
471 * environment-dependent parameters
472 */
474 ModelData *model_data, int aero_phase_idx, double *layer_thickness,
475 double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data,
476 double *aero_rep_env_data) {
477 int *int_data = aero_rep_int_data;
478 double *float_data = aero_rep_float_data;
479
480 for (int i_section = 0; i_section < NUM_SECTION_; i_section++) {
481 for (int i_bin = 0; i_bin < NUM_BINS_(i_section); i_bin++) {
482 aero_phase_idx -= NUM_PHASE_(i_section);
483 if (aero_phase_idx < 0) {
484 *layer_thickness = EFFECTIVE_RADIUS_(i_section, i_bin);
485 // Effective radii are constant for bins and modes
486 if (partial_deriv) {
487 for (int i_phase = 0; i_phase < NUM_PHASE_(i_section); ++i_phase) {
488 for (int i_elem = 0;
489 i_elem < PHASE_NUM_JAC_ELEM_(i_section, i_phase, i_bin);
490 ++i_elem) {
491 *(partial_deriv++) = ZERO;
492 }
493 }
494 }
495 i_section = NUM_SECTION_;
496 break;
497 }
498 }
499 }
500
501 return;
502}
503
504/** \brief Get the particle number concentration \f$n\f$
505 * (\f$\mbox{\si{\#\per\cubic\metre}}\f$)
506 *
507 * The modal mass number concentration is calculated for a log-normal
508 * distribution where the geometric mean diameter (\f$\tilde{D}_n\f$) and
509 * geometric standard deviation (\f$\tilde{\sigma}_g\f$) are set by the aerosol
510 * model prior to solving the chemistry. The number concentration is
511 * calculated according to the equation given in Table 1 of Zender
512 * \cite Zender2002 :
513 * \f[
514 * n = N_0 = \frac{6V_0}{\pi}\tilde{D}_n^{-3}e^{-9
515 * ln(\tilde{\sigma}_g)^2/2} \f] \f[ V_0 = \sum_i{\frac{m_i}{\rho_i}} \f] where
516 * \f$\rho_i\f$ and \f$m_i\f$ are the density and total mass of species \f$i\f$
517 * in the specified mode.
518 *
519 * The binned number concentration is calculated according to:
520 * \f[
521 * n = V_0 / V_p
522 * \f]
523 * \f[
524 * V_p = \frac{4}{3}\pi r^{3}
525 * \f]
526 * where \f$r\f$ is the radius of the size bin and \f$V_0\f$ is defined as
527 * above.
528 *
529 * \param model_data Pointer to the model data, including the state array
530 * \param aero_phase_idx Index of the aerosol phase within the representation
531 * \param number_conc Particle number concentration, \f$n\f$
532 * (\f$\mbox{\si{\#\per\cubic\centi\metre}}\f$)
533 * \param partial_deriv \f$\frac{\partial n}{\partial y}\f$ where \f$y\f$ are
534 * the species on the state array
535 * \param aero_rep_int_data Pointer to the aerosol representation integer data
536 * \param aero_rep_float_data Pointer to the aerosol representation
537 * floating-point data
538 * \param aero_rep_env_data Pointer to the aerosol representation
539 * environment-dependent parameters
540 */
542 ModelData *model_data, int aero_phase_idx, double *number_conc,
543 double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data,
544 double *aero_rep_env_data) {
545 int *int_data = aero_rep_int_data;
546 double *float_data = aero_rep_float_data;
547
548 for (int i_section = 0; i_section < NUM_SECTION_ && aero_phase_idx >= 0;
549 i_section++) {
550 for (int i_bin = 0; i_bin < NUM_BINS_(i_section) && aero_phase_idx >= 0;
551 i_bin++) {
552 aero_phase_idx -= NUM_PHASE_(i_section);
553 if (aero_phase_idx < 0) {
554 *number_conc = NUMBER_CONC_(i_section, i_bin);
555 if (partial_deriv) {
556 for (int i_phase = 0; i_phase < NUM_PHASE_(i_section); ++i_phase) {
557 // Get a pointer to the phase on the state array
558 double *state = (double *)(model_data->grid_cell_state);
559 state += PHASE_STATE_ID_(i_section, i_phase, i_bin);
560
561 // Get the aerosol phase volume [m3 m-3]
562 double phase_volume = 0.0;
564 model_data, PHASE_MODEL_DATA_ID_(i_section, i_phase, i_bin),
565 state, &phase_volume, partial_deriv);
566
567 // Convert d_vol/d_conc to d_number/d_conc
568 for (int i_elem = 0;
569 i_elem < PHASE_NUM_JAC_ELEM_(i_section, i_phase, i_bin);
570 ++i_elem) {
571 switch (SECTION_TYPE_(i_section)) {
572 case (MODAL):
573 *(partial_deriv++) *=
574 6.0 / (M_PI * pow(GMD_(i_section), 3) *
575 exp(9.0 / 2.0 * pow(log(GSD_(i_section)), 2)));
576 break;
577 case (BINNED):
578 *(partial_deriv++) *= 3.0 / (4.0 * M_PI) /
579 pow(BIN_DP_(i_section, i_bin) / 2.0, 3);
580 break;
581 }
582 }
583 }
584 }
585 i_section = NUM_SECTION_;
586 break;
587 }
588 }
589 }
590
591 return;
592}
593
594/** \brief Get the type of aerosol concentration used.
595 *
596 * Modal mass concentrations are per-mode or per-bin.
597 *
598 * \param aero_phase_idx Index of the aerosol phase within the representation
599 * \param aero_conc_type Pointer to int that will hold the concentration type
600 * code
601 * \param aero_rep_int_data Pointer to the aerosol representation integer data
602 * \param aero_rep_float_data Pointer to the aerosol representation
603 * floating-point data
604 * \param aero_rep_env_data Pointer to the aerosol representation
605 * environment-dependent parameters
606 */
608 int *aero_conc_type,
609 int *aero_rep_int_data,
610 double *aero_rep_float_data,
611 double *aero_rep_env_data) {
612 int *int_data = aero_rep_int_data;
613 double *float_data = aero_rep_float_data;
614
615 *aero_conc_type = 1;
616
617 return;
618}
619
620/** \brief Get the total mass in an aerosol phase \f$m\f$
621 * (\f$\mbox{\si{\kilogram\per\cubic\metre}}\f$)
622 *
623 * \param model_data Pointer to the model data, including the state array
624 * \param aero_phase_idx Index of the aerosol phase within the representation
625 * \param aero_phase_mass Total mass in the aerosol phase, \f$m\f$
626 * (\f$\mbox{\si{\kilogram\per\cubic\metre}}\f$)
627 * \param partial_deriv \f$\frac{\partial m}{\partial y}\f$ where \f$y\f$ are
628 * the species on the state array
629 * \param aero_rep_int_data Pointer to the aerosol representation integer data
630 * \param aero_rep_float_data Pointer to the aerosol representation
631 * floating-point data
632 * \param aero_rep_env_data Pointer to the aerosol representation
633 * environment-dependent parameters
634 */
636 ModelData *model_data, int aero_phase_idx, double *aero_phase_mass,
637 double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data,
638 double *aero_rep_env_data) {
639 int *int_data = aero_rep_int_data;
640 double *float_data = aero_rep_float_data;
641
642 for (int i_section = 0; i_section < NUM_SECTION_ && aero_phase_idx >= 0;
643 ++i_section) {
644 for (int i_bin = 0; i_bin < NUM_BINS_(i_section) && aero_phase_idx >= 0;
645 ++i_bin) {
646 if (aero_phase_idx < 0 || aero_phase_idx >= NUM_PHASE_(i_section)) {
647 aero_phase_idx -= NUM_PHASE_(i_section);
648 continue;
649 }
650 for (int i_phase = 0; i_phase < NUM_PHASE_(i_section); ++i_phase) {
651 if (aero_phase_idx == 0) {
652 *aero_phase_mass = PHASE_MASS_(i_section, i_phase, i_bin);
653 if (partial_deriv) {
654 // Get a pointer to the phase on the state array
655 double *state = (double *)(model_data->grid_cell_state);
656 state += PHASE_STATE_ID_(i_section, i_phase, i_bin);
657
658 // Get d_mass / d_conc
659 double mass, mw;
661 model_data, PHASE_MODEL_DATA_ID_(i_section, i_phase, i_bin),
662 state, &mass, &mw, partial_deriv, NULL);
663 partial_deriv += PHASE_NUM_JAC_ELEM_(i_section, i_phase, i_bin);
664 }
665
666 // Other phases present in the bin or mode do not contribute to
667 // the aerosol phase mass
668 } else if (partial_deriv) {
669 for (int i_elem = 0;
670 i_elem < PHASE_NUM_JAC_ELEM_(i_section, i_phase, i_bin);
671 ++i_elem) {
672 *(partial_deriv++) = ZERO;
673 }
674 }
675 aero_phase_idx -= 1;
676 }
677 }
678 }
679
680 return;
681}
682
683/** \brief Get the average molecular weight in an aerosol phase
684 ** \f$m\f$ (\f$\mbox{\si{\kilogram\per\mole}}\f$)
685 *
686 * \param model_data Pointer to the model data, including the state array
687 * \param aero_phase_idx Index of the aerosol phase within the representation
688 * \param aero_phase_avg_MW Average molecular weight in the aerosol phase
689 * (\f$\mbox{\si{\kilogram\per\mole}}\f$)
690 * \param partial_deriv \f$\frac{\partial m}{\partial y}\f$ where \f$y\f$ are
691 * the species on the state array
692 * \param aero_rep_int_data Pointer to the aerosol representation integer data
693 * \param aero_rep_float_data Pointer to the aerosol representation
694 * floating-point data
695 * \param aero_rep_env_data Pointer to the aerosol representation
696 * environment-dependent parameters
697 */
699 ModelData *model_data, int aero_phase_idx, double *aero_phase_avg_MW,
700 double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data,
701 double *aero_rep_env_data) {
702 int *int_data = aero_rep_int_data;
703 double *float_data = aero_rep_float_data;
704
705 for (int i_section = 0; i_section < NUM_SECTION_ && aero_phase_idx >= 0;
706 ++i_section) {
707 for (int i_bin = 0; i_bin < NUM_BINS_(i_section) && aero_phase_idx >= 0;
708 ++i_bin) {
709 if (aero_phase_idx < 0 || aero_phase_idx >= NUM_PHASE_(i_section)) {
710 aero_phase_idx -= NUM_PHASE_(i_section);
711 continue;
712 }
713 for (int i_phase = 0; i_phase < NUM_PHASE_(i_section); ++i_phase) {
714 if (aero_phase_idx == 0) {
715 *aero_phase_avg_MW = PHASE_AVG_MW_(i_section, i_phase, i_bin);
716 if (partial_deriv) {
717 // Get a pointer to the phase on the state array
718 double *state = (double *)(model_data->grid_cell_state);
719 state += PHASE_STATE_ID_(i_section, i_phase, i_bin);
720
721 // Get d_MW / d_conc
722 double mass, mw;
724 model_data, PHASE_MODEL_DATA_ID_(i_section, i_phase, i_bin),
725 state, &mass, &mw, NULL, partial_deriv);
726 partial_deriv += PHASE_NUM_JAC_ELEM_(i_section, i_phase, i_bin);
727 }
728
729 // Other phases present in the bin/mode do not contribute to the
730 // average MW of the aerosol phase
731 } else if (partial_deriv) {
732 for (int i_elem = 0;
733 i_elem < PHASE_NUM_JAC_ELEM_(i_section, i_phase, i_bin);
734 ++i_elem) {
735 *(partial_deriv++) = ZERO;
736 }
737 }
738 aero_phase_idx -= 1;
739 }
740 }
741 }
742
743 return;
744}
745
746/** \brief Update the aerosol representation data
747 *
748 * The model mass aerosol representation update data is structured as follows:
749 *
750 * - \b int aero_rep_id (Id of one or more aerosol representations set by the
751 * host model using the
752 * camp_aero_rep_modal_binned_mass::aero_rep_modal_binned_mass_t::set_id
753 * function prior to initializing the solver.)
754 * - \b int update_type (Type of update to perform. Can be UPDATE_GMD or
755 * UPDATE_GSD.)
756 * - \b int section_id (Index of the mode to update.)
757 * - \b double new_value (Either the new GMD (m) or the new GSD (unitless).)
758 *
759 * \param update_data Pointer to the updated aerosol representation data
760 * \param aero_rep_int_data Pointer to the aerosol representation integer data
761 * \param aero_rep_float_data Pointer to the aerosol representation
762 * floating-point data
763 * \param aero_rep_env_data Pointer to the aerosol representation
764 * environment-dependent parameters
765 * \return Flag indicating whether this is the aerosol representation to update
766 */
768 int *aero_rep_int_data,
769 double *aero_rep_float_data,
770 double *aero_rep_env_data) {
771 int *int_data = aero_rep_int_data;
772 double *float_data = aero_rep_float_data;
773
774 int *aero_rep_id = (int *)update_data;
775 int *update_type = (int *)&(aero_rep_id[1]);
776 int *section_id = (int *)&(update_type[1]);
777 double *new_value = (double *)&(section_id[1]);
778 bool ret_val = false;
779
780 // Set the new GMD or GSD for matching aerosol representations
781 if (*aero_rep_id == AERO_REP_ID_ && AERO_REP_ID_ != 0) {
782 if (*update_type == UPDATE_GMD) {
783 if (SECTION_TYPE_(*section_id) != MODAL) {
784 printf(
785 "\n\nERROR Trying to set geometric mean diameter for non-modal"
786 " aerosol section.");
787 exit(1);
788 }
789 GMD_(*section_id) = (double)*new_value; // [m]
790 ret_val = true;
791 } else if (*update_type == UPDATE_GSD) {
792 if (SECTION_TYPE_(*section_id) != MODAL) {
793 printf(
794 "\n\nERROR Trying to set geometric standard deviation for non-modal"
795 " aerosol section.");
796 exit(1);
797 }
798 GSD_(*section_id) = (double)*new_value;
799 ret_val = true;
800 }
801 }
802
803 if (ret_val == true) {
804 /// Recalculate the effective radius [m]
805 ///
806 /// Equation based on \cite Zender2002
807 /// Table 1 effective diameter \f$(D_s, D_{eff}\f$) equations:
808 /// \f[
809 /// \tilde{\sigma_g} \equiv ln( \sigma_g )
810 /// \f]
811 /// \f[
812 /// D_s = D_{eff} = \tilde{D_n} e^{5 \tilde{\sigma}_g^2 / 2}
813 /// \f]
814 /// \f[
815 /// r_{eff} = \frac{D_{eff}}{2}
816 /// \f]
817 /// where \f$\tilde{D_n}\f$ is the geometric mean diameter [m],
818 /// \f$\sigma_g\f$
819 /// is the geometric standard deviation [unitless], and \f$r_{eff}\f$
820 /// is the effective radius [m].
821 ///
822 double ln_gsd = log(GSD_(*section_id));
823 EFFECTIVE_RADIUS_(*section_id, 0) =
824 GMD_(*section_id) / 2.0 * exp(5.0 * ln_gsd * ln_gsd / 2.0);
825 }
826
827 return ret_val;
828}
829
830/** \brief Print the mass-only modal/binned reaction parameters
831 *
832 * \param aero_rep_int_data Pointer to the aerosol representation integer data
833 * \param aero_rep_float_data Pointer to the aerosol representation
834 * floating-point data
835 */
836void aero_rep_modal_binned_mass_print(int *aero_rep_int_data,
837 double *aero_rep_float_data) {
838 int *int_data = aero_rep_int_data;
839 double *float_data = aero_rep_float_data;
840
841 printf("\n\nModal/binned mass-only aerosol representation\n");
842
843 return;
844}
845
846/** \brief Create update data for new GMD
847 *
848 * \return Pointer to a new GMD update data object
849 */
851 int *update_data = (int *)malloc(3 * sizeof(int) + sizeof(double));
852 if (update_data == NULL) {
853 printf("\n\nERROR allocating space for GMD update data.\n\n");
854 exit(1);
855 }
856 return (void *)update_data;
857}
858
859/** \brief Set GMD update data
860 *
861 * \param update_data Pointer to an allocated GMD update data object
862 * \param aero_rep_id Id of the aerosol representation(s) to update
863 * \param section_id Id of the mode to update
864 * \param gmd New mode GMD (m)
865 */
867 int aero_rep_id,
868 int section_id,
869 double gmd) {
870 int *new_aero_rep_id = (int *)update_data;
871 int *update_type = (int *)&(new_aero_rep_id[1]);
872 int *new_section_id = (int *)&(update_type[1]);
873 double *new_GMD = (double *)&(new_section_id[1]);
874 *new_aero_rep_id = aero_rep_id;
875 *update_type = UPDATE_GMD;
876 *new_section_id = section_id;
877 *new_GMD = gmd;
878}
879
880/** \brief Create update data for new GSD
881 *
882 * \return Pointer to a new GSD update data object
883 */
885 int *update_data = (int *)malloc(3 * sizeof(int) + sizeof(double));
886 if (update_data == NULL) {
887 printf("\n\nERROR allocating space for GSD update data.\n\n");
888 exit(1);
889 }
890 return (void *)update_data;
891}
892
893/** \brief Set GSD update data
894 *
895 * \param update_data Pointer to an allocated GSD update data object
896 * \param aero_rep_id Id of the aerosol representation(s) to update
897 * \param section_id Id of the mode to update
898 * \param gsd New mode GSD (unitless)
899 */
901 int aero_rep_id,
902 int section_id,
903 double gsd) {
904 int *new_aero_rep_id = (int *)update_data;
905 int *update_type = (int *)&(new_aero_rep_id[1]);
906 int *new_section_id = (int *)&(update_type[1]);
907 double *new_GSD = (double *)&(new_section_id[1]);
908 *new_aero_rep_id = aero_rep_id;
909 *update_type = UPDATE_GSD;
910 *new_section_id = section_id;
911 *new_GSD = gsd;
912}
void aero_phase_get_volume__m3_m3(ModelData *model_data, int aero_phase_idx, double *state_var, double *volume, double *jac_elem)
Get the volume of an aerosol phase.
int aero_phase_get_used_jac_elem(ModelData *model_data, int aero_phase_idx, int state_var_id, bool *jac_struct)
Flag Jacobian elements used in calculations of mass and volume.
void aero_phase_get_mass__kg_m3(ModelData *model_data, int aero_phase_idx, double *state_var, double *mass, double *MW, double *jac_elem_mass, double *jac_elem_MW)
Get the mass and average MW in an aerosol phase.
void aero_rep_modal_binned_mass_set_gsd_update_data(void *update_data, int aero_rep_id, int section_id, double gsd)
Set GSD update data.
bool aero_rep_modal_binned_mass_update_data(void *update_data, int *aero_rep_int_data, double *aero_rep_float_data, double *aero_rep_env_data)
Update the aerosol representation data.
#define BINNED
#define PHASE_STATE_ID_(x, y, b)
void aero_rep_modal_binned_mass_get_layer_thickness__m(ModelData *model_data, int aero_phase_idx, double *layer_thickness, double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data, double *aero_rep_env_data)
Get the thickness of a particle layer (m)
#define NUM_SECTION_
void aero_rep_modal_binned_mass_get_aero_phase_avg_MW__kg_mol(ModelData *model_data, int aero_phase_idx, double *aero_phase_avg_MW, double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data, double *aero_rep_env_data)
Get the average molecular weight in an aerosol phase ( )
void aero_rep_modal_binned_mass_get_number_conc__n_m3(ModelData *model_data, int aero_phase_idx, double *number_conc, double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data, double *aero_rep_env_data)
Get the particle number concentration ( )
void aero_rep_modal_binned_mass_set_gmd_update_data(void *update_data, int aero_rep_id, int section_id, double gmd)
Set GMD update data.
#define PHASE_MODEL_DATA_ID_(x, y, b)
void aero_rep_modal_binned_mass_update_state(ModelData *model_data, int *aero_rep_int_data, double *aero_rep_float_data, double *aero_rep_env_data)
Update aerosol representation data for a new state.
void aero_rep_modal_binned_mass_get_aero_conc_type(int aero_phase_idx, int *aero_conc_type, int *aero_rep_int_data, double *aero_rep_float_data, double *aero_rep_env_data)
Get the type of aerosol concentration used.
#define SECTION_TYPE_(x)
void aero_rep_modal_binned_mass_update_env_state(ModelData *model_data, int *aero_rep_int_data, double *aero_rep_float_data, double *aero_rep_env_data)
Update aerosol representation data for new environmental conditions.
void aero_rep_modal_binned_mass_print(int *aero_rep_int_data, double *aero_rep_float_data)
Print the mass-only modal/binned reaction parameters.
#define AERO_REP_ID_
#define UPDATE_GMD
void aero_rep_modal_binned_mass_get_effective_radius__m(ModelData *model_data, int aero_phase_idx, double *radius, double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data, double *aero_rep_env_data)
Get the effective particle radius (m)
void aero_rep_modal_binned_mass_get_dependencies(int *aero_rep_int_data, double *aero_rep_float_data, bool *state_flags)
Flag elements on the state array used by this aerosol representation.
#define NUM_PHASE_(x)
#define NUMBER_CONC_(x, b)
#define GMD_(x)
void aero_rep_modal_binned_mass_get_aero_phase_mass__kg_m3(ModelData *model_data, int aero_phase_idx, double *aero_phase_mass, double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data, double *aero_rep_env_data)
Get the total mass in an aerosol phase ( )
void * aero_rep_modal_binned_mass_create_gmd_update_data()
Create update data for new GMD.
void aero_rep_modal_binned_mass_get_interface_surface_area__m2(ModelData *model_data, int aero_phase_idx_first, int aero_phase_idx_second, double *surface_area, double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data, double *aero_rep_env_data)
Get the effective particle surface area (m)
void aero_rep_modal_binned_mass_get_effective_layer_radius__m(ModelData *model_data, int aero_phase_idx, double *layer_radius, double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data, double *aero_rep_env_data)
Get the effective radius of a specified layer (m)
int aero_rep_modal_binned_mass_get_used_jac_elem(ModelData *model_data, int aero_phase_idx, int *aero_rep_int_data, double *aero_rep_float_data, bool *jac_struct)
Flag Jacobian elements used in calcualtions of mass and volume.
#define MODAL
#define BIN_DP_(x, b)
#define PHASE_AVG_MW_(x, y, b)
void aero_rep_modal_binned_mass_get_phase_volume__m3_m3(ModelData *model_data, int aero_phase_idx, double *phase_volume, double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data, double *aero_rep_env_data)
Get the phase volume (m³/m³)
#define UPDATE_GSD
#define NUM_BINS_(x)
#define PHASE_NUM_JAC_ELEM_(x, y, b)
#define PHASE_MASS_(x, y, b)
void * aero_rep_modal_binned_mass_create_gsd_update_data()
Create update data for new GSD.
#define EFFECTIVE_RADIUS_(x, b)
#define GSD_(x)