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 effective particle surface area \f$r_{eff}\f$ (m)
368 *
369 * The surface area interface that exists between two specified phases within
370 * the modal/binned aerosol representation always returns an error, because no
371 * specific internal structure is assumed for phases within modes or bins.
372 *
373 * \param model_data Pointer to the model data, including the state array
374 * \param aero_phase_idx_first Index of the first aerosol phase within the representation
375 * \param aero_phase_idx_second Index of the second aerosol phase within the representation
376 * \param surface_area Effective surface area of interface between phases (m^2)
377 * \param partial_deriv \f$\frac{\partial sa_{eff}}{\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_first, int aero_phase_idx_second,
387 double *surface_area, double *partial_deriv, int *aero_rep_int_data,
388 double *aero_rep_float_data, double *aero_rep_env_data) {
389 int *int_data = aero_rep_int_data;
390 double *float_data = aero_rep_float_data;
391
392
393 printf("\n\nERROR There are no adjacent pairs in the modal/binned representation.\n\n");
394 exit(1);
395}
396
397/** \brief Get the thickness of a particle layer (m)
398 *
399 * The thickness of a particle layer within the modal/binned aerosol
400 * representation always returns the radius.
401 *
402 * \param model_data Pointer to the model data, including the state array
403 * \param aero_phase_idx Index of the aerosol phase within the representation
404 * \param layer_thickness Layer thickness which is equivalent to radius (m)
405 * \param partial_deriv \f$\frac{\partial r_{eff}}{\partial y}\f$ where \f$y\f$
406 * are species on the state array
407 * \param aero_rep_int_data Pointer to the aerosol representation integer data
408 * \param aero_rep_float_data Pointer to the aerosol representation
409 * floating-point data
410 * \param aero_rep_env_data Pointer to the aerosol representation
411 * environment-dependent parameters
412 */
414 ModelData *model_data, int aero_phase_idx, double *layer_thickness,
415 double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data,
416 double *aero_rep_env_data) {
417 int *int_data = aero_rep_int_data;
418 double *float_data = aero_rep_float_data;
419
420 for (int i_section = 0; i_section < NUM_SECTION_; i_section++) {
421 for (int i_bin = 0; i_bin < NUM_BINS_(i_section); i_bin++) {
422 aero_phase_idx -= NUM_PHASE_(i_section);
423 if (aero_phase_idx < 0) {
424 *layer_thickness = EFFECTIVE_RADIUS_(i_section, i_bin);
425 // Effective radii are constant for bins and modes
426 if (partial_deriv) {
427 for (int i_phase = 0; i_phase < NUM_PHASE_(i_section); ++i_phase) {
428 for (int i_elem = 0;
429 i_elem < PHASE_NUM_JAC_ELEM_(i_section, i_phase, i_bin);
430 ++i_elem) {
431 *(partial_deriv++) = ZERO;
432 }
433 }
434 }
435 i_section = NUM_SECTION_;
436 break;
437 }
438 }
439 }
440
441 return;
442}
443
444/** \brief Get the particle number concentration \f$n\f$
445 * (\f$\mbox{\si{\#\per\cubic\metre}}\f$)
446 *
447 * The modal mass number concentration is calculated for a log-normal
448 * distribution where the geometric mean diameter (\f$\tilde{D}_n\f$) and
449 * geometric standard deviation (\f$\tilde{\sigma}_g\f$) are set by the aerosol
450 * model prior to solving the chemistry. The number concentration is
451 * calculated according to the equation given in Table 1 of Zender
452 * \cite Zender2002 :
453 * \f[
454 * n = N_0 = \frac{6V_0}{\pi}\tilde{D}_n^{-3}e^{-9
455 * ln(\tilde{\sigma}_g)^2/2} \f] \f[ V_0 = \sum_i{\frac{m_i}{\rho_i}} \f] where
456 * \f$\rho_i\f$ and \f$m_i\f$ are the density and total mass of species \f$i\f$
457 * in the specified mode.
458 *
459 * The binned number concentration is calculated according to:
460 * \f[
461 * n = V_0 / V_p
462 * \f]
463 * \f[
464 * V_p = \frac{4}{3}\pi r^{3}
465 * \f]
466 * where \f$r\f$ is the radius of the size bin and \f$V_0\f$ is defined as
467 * above.
468 *
469 * \param model_data Pointer to the model data, including the state array
470 * \param aero_phase_idx Index of the aerosol phase within the representation
471 * \param number_conc Particle number concentration, \f$n\f$
472 * (\f$\mbox{\si{\#\per\cubic\centi\metre}}\f$)
473 * \param partial_deriv \f$\frac{\partial n}{\partial y}\f$ where \f$y\f$ are
474 * the species on the state array
475 * \param aero_rep_int_data Pointer to the aerosol representation integer data
476 * \param aero_rep_float_data Pointer to the aerosol representation
477 * floating-point data
478 * \param aero_rep_env_data Pointer to the aerosol representation
479 * environment-dependent parameters
480 */
482 ModelData *model_data, int aero_phase_idx, double *number_conc,
483 double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data,
484 double *aero_rep_env_data) {
485 int *int_data = aero_rep_int_data;
486 double *float_data = aero_rep_float_data;
487
488 for (int i_section = 0; i_section < NUM_SECTION_ && aero_phase_idx >= 0;
489 i_section++) {
490 for (int i_bin = 0; i_bin < NUM_BINS_(i_section) && aero_phase_idx >= 0;
491 i_bin++) {
492 aero_phase_idx -= NUM_PHASE_(i_section);
493 if (aero_phase_idx < 0) {
494 *number_conc = NUMBER_CONC_(i_section, i_bin);
495 if (partial_deriv) {
496 for (int i_phase = 0; i_phase < NUM_PHASE_(i_section); ++i_phase) {
497 // Get a pointer to the phase on the state array
498 double *state = (double *)(model_data->grid_cell_state);
499 state += PHASE_STATE_ID_(i_section, i_phase, i_bin);
500
501 // Get the aerosol phase volume [m3 m-3]
502 double phase_volume = 0.0;
504 model_data, PHASE_MODEL_DATA_ID_(i_section, i_phase, i_bin),
505 state, &phase_volume, partial_deriv);
506
507 // Convert d_vol/d_conc to d_number/d_conc
508 for (int i_elem = 0;
509 i_elem < PHASE_NUM_JAC_ELEM_(i_section, i_phase, i_bin);
510 ++i_elem) {
511 switch (SECTION_TYPE_(i_section)) {
512 case (MODAL):
513 *(partial_deriv++) *=
514 6.0 / (M_PI * pow(GMD_(i_section), 3) *
515 exp(9.0 / 2.0 * pow(log(GSD_(i_section)), 2)));
516 break;
517 case (BINNED):
518 *(partial_deriv++) *= 3.0 / (4.0 * M_PI) /
519 pow(BIN_DP_(i_section, i_bin) / 2.0, 3);
520 break;
521 }
522 }
523 }
524 }
525 i_section = NUM_SECTION_;
526 break;
527 }
528 }
529 }
530
531 return;
532}
533
534/** \brief Get the type of aerosol concentration used.
535 *
536 * Modal mass concentrations are per-mode or per-bin.
537 *
538 * \param aero_phase_idx Index of the aerosol phase within the representation
539 * \param aero_conc_type Pointer to int that will hold the concentration type
540 * code
541 * \param aero_rep_int_data Pointer to the aerosol representation integer data
542 * \param aero_rep_float_data Pointer to the aerosol representation
543 * floating-point data
544 * \param aero_rep_env_data Pointer to the aerosol representation
545 * environment-dependent parameters
546 */
548 int *aero_conc_type,
549 int *aero_rep_int_data,
550 double *aero_rep_float_data,
551 double *aero_rep_env_data) {
552 int *int_data = aero_rep_int_data;
553 double *float_data = aero_rep_float_data;
554
555 *aero_conc_type = 1;
556
557 return;
558}
559
560/** \brief Get the total mass in an aerosol phase \f$m\f$
561 * (\f$\mbox{\si{\kilogram\per\cubic\metre}}\f$)
562 *
563 * \param model_data Pointer to the model data, including the state array
564 * \param aero_phase_idx Index of the aerosol phase within the representation
565 * \param aero_phase_mass Total mass in the aerosol phase, \f$m\f$
566 * (\f$\mbox{\si{\kilogram\per\cubic\metre}}\f$)
567 * \param partial_deriv \f$\frac{\partial m}{\partial y}\f$ where \f$y\f$ are
568 * the species on the state array
569 * \param aero_rep_int_data Pointer to the aerosol representation integer data
570 * \param aero_rep_float_data Pointer to the aerosol representation
571 * floating-point data
572 * \param aero_rep_env_data Pointer to the aerosol representation
573 * environment-dependent parameters
574 */
576 ModelData *model_data, int aero_phase_idx, double *aero_phase_mass,
577 double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data,
578 double *aero_rep_env_data) {
579 int *int_data = aero_rep_int_data;
580 double *float_data = aero_rep_float_data;
581
582 for (int i_section = 0; i_section < NUM_SECTION_ && aero_phase_idx >= 0;
583 ++i_section) {
584 for (int i_bin = 0; i_bin < NUM_BINS_(i_section) && aero_phase_idx >= 0;
585 ++i_bin) {
586 if (aero_phase_idx < 0 || aero_phase_idx >= NUM_PHASE_(i_section)) {
587 aero_phase_idx -= NUM_PHASE_(i_section);
588 continue;
589 }
590 for (int i_phase = 0; i_phase < NUM_PHASE_(i_section); ++i_phase) {
591 if (aero_phase_idx == 0) {
592 *aero_phase_mass = PHASE_MASS_(i_section, i_phase, i_bin);
593 if (partial_deriv) {
594 // Get a pointer to the phase on the state array
595 double *state = (double *)(model_data->grid_cell_state);
596 state += PHASE_STATE_ID_(i_section, i_phase, i_bin);
597
598 // Get d_mass / d_conc
599 double mass, mw;
601 model_data, PHASE_MODEL_DATA_ID_(i_section, i_phase, i_bin),
602 state, &mass, &mw, partial_deriv, NULL);
603 partial_deriv += PHASE_NUM_JAC_ELEM_(i_section, i_phase, i_bin);
604 }
605
606 // Other phases present in the bin or mode do not contribute to
607 // the aerosol phase mass
608 } else if (partial_deriv) {
609 for (int i_elem = 0;
610 i_elem < PHASE_NUM_JAC_ELEM_(i_section, i_phase, i_bin);
611 ++i_elem) {
612 *(partial_deriv++) = ZERO;
613 }
614 }
615 aero_phase_idx -= 1;
616 }
617 }
618 }
619
620 return;
621}
622
623/** \brief Get the average molecular weight in an aerosol phase
624 ** \f$m\f$ (\f$\mbox{\si{\kilogram\per\mole}}\f$)
625 *
626 * \param model_data Pointer to the model data, including the state array
627 * \param aero_phase_idx Index of the aerosol phase within the representation
628 * \param aero_phase_avg_MW Average molecular weight in the aerosol phase
629 * (\f$\mbox{\si{\kilogram\per\mole}}\f$)
630 * \param partial_deriv \f$\frac{\partial m}{\partial y}\f$ where \f$y\f$ are
631 * the species on the state array
632 * \param aero_rep_int_data Pointer to the aerosol representation integer data
633 * \param aero_rep_float_data Pointer to the aerosol representation
634 * floating-point data
635 * \param aero_rep_env_data Pointer to the aerosol representation
636 * environment-dependent parameters
637 */
639 ModelData *model_data, int aero_phase_idx, double *aero_phase_avg_MW,
640 double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data,
641 double *aero_rep_env_data) {
642 int *int_data = aero_rep_int_data;
643 double *float_data = aero_rep_float_data;
644
645 for (int i_section = 0; i_section < NUM_SECTION_ && aero_phase_idx >= 0;
646 ++i_section) {
647 for (int i_bin = 0; i_bin < NUM_BINS_(i_section) && aero_phase_idx >= 0;
648 ++i_bin) {
649 if (aero_phase_idx < 0 || aero_phase_idx >= NUM_PHASE_(i_section)) {
650 aero_phase_idx -= NUM_PHASE_(i_section);
651 continue;
652 }
653 for (int i_phase = 0; i_phase < NUM_PHASE_(i_section); ++i_phase) {
654 if (aero_phase_idx == 0) {
655 *aero_phase_avg_MW = PHASE_AVG_MW_(i_section, i_phase, i_bin);
656 if (partial_deriv) {
657 // Get a pointer to the phase on the state array
658 double *state = (double *)(model_data->grid_cell_state);
659 state += PHASE_STATE_ID_(i_section, i_phase, i_bin);
660
661 // Get d_MW / d_conc
662 double mass, mw;
664 model_data, PHASE_MODEL_DATA_ID_(i_section, i_phase, i_bin),
665 state, &mass, &mw, NULL, partial_deriv);
666 partial_deriv += PHASE_NUM_JAC_ELEM_(i_section, i_phase, i_bin);
667 }
668
669 // Other phases present in the bin/mode do not contribute to the
670 // average MW of the aerosol phase
671 } else if (partial_deriv) {
672 for (int i_elem = 0;
673 i_elem < PHASE_NUM_JAC_ELEM_(i_section, i_phase, i_bin);
674 ++i_elem) {
675 *(partial_deriv++) = ZERO;
676 }
677 }
678 aero_phase_idx -= 1;
679 }
680 }
681 }
682
683 return;
684}
685
686/** \brief Update the aerosol representation data
687 *
688 * The model mass aerosol representation update data is structured as follows:
689 *
690 * - \b int aero_rep_id (Id of one or more aerosol representations set by the
691 * host model using the
692 * camp_aero_rep_modal_binned_mass::aero_rep_modal_binned_mass_t::set_id
693 * function prior to initializing the solver.)
694 * - \b int update_type (Type of update to perform. Can be UPDATE_GMD or
695 * UPDATE_GSD.)
696 * - \b int section_id (Index of the mode to update.)
697 * - \b double new_value (Either the new GMD (m) or the new GSD (unitless).)
698 *
699 * \param update_data Pointer to the updated aerosol representation data
700 * \param aero_rep_int_data Pointer to the aerosol representation integer data
701 * \param aero_rep_float_data Pointer to the aerosol representation
702 * floating-point data
703 * \param aero_rep_env_data Pointer to the aerosol representation
704 * environment-dependent parameters
705 * \return Flag indicating whether this is the aerosol representation to update
706 */
708 int *aero_rep_int_data,
709 double *aero_rep_float_data,
710 double *aero_rep_env_data) {
711 int *int_data = aero_rep_int_data;
712 double *float_data = aero_rep_float_data;
713
714 int *aero_rep_id = (int *)update_data;
715 int *update_type = (int *)&(aero_rep_id[1]);
716 int *section_id = (int *)&(update_type[1]);
717 double *new_value = (double *)&(section_id[1]);
718 bool ret_val = false;
719
720 // Set the new GMD or GSD for matching aerosol representations
721 if (*aero_rep_id == AERO_REP_ID_ && AERO_REP_ID_ != 0) {
722 if (*update_type == UPDATE_GMD) {
723 if (SECTION_TYPE_(*section_id) != MODAL) {
724 printf(
725 "\n\nERROR Trying to set geometric mean diameter for non-modal"
726 " aerosol section.");
727 exit(1);
728 }
729 GMD_(*section_id) = (double)*new_value; // [m]
730 ret_val = true;
731 } else if (*update_type == UPDATE_GSD) {
732 if (SECTION_TYPE_(*section_id) != MODAL) {
733 printf(
734 "\n\nERROR Trying to set geometric standard deviation for non-modal"
735 " aerosol section.");
736 exit(1);
737 }
738 GSD_(*section_id) = (double)*new_value;
739 ret_val = true;
740 }
741 }
742
743 if (ret_val == true) {
744 /// Recalculate the effective radius [m]
745 ///
746 /// Equation based on \cite Zender2002
747 /// Table 1 effective diameter \f$(D_s, D_{eff}\f$) equations:
748 /// \f[
749 /// \tilde{\sigma_g} \equiv ln( \sigma_g )
750 /// \f]
751 /// \f[
752 /// D_s = D_{eff} = \tilde{D_n} e^{5 \tilde{\sigma}_g^2 / 2}
753 /// \f]
754 /// \f[
755 /// r_{eff} = \frac{D_{eff}}{2}
756 /// \f]
757 /// where \f$\tilde{D_n}\f$ is the geometric mean diameter [m],
758 /// \f$\sigma_g\f$
759 /// is the geometric standard deviation [unitless], and \f$r_{eff}\f$
760 /// is the effective radius [m].
761 ///
762 double ln_gsd = log(GSD_(*section_id));
763 EFFECTIVE_RADIUS_(*section_id, 0) =
764 GMD_(*section_id) / 2.0 * exp(5.0 * ln_gsd * ln_gsd / 2.0);
765 }
766
767 return ret_val;
768}
769
770/** \brief Print the mass-only modal/binned reaction parameters
771 *
772 * \param aero_rep_int_data Pointer to the aerosol representation integer data
773 * \param aero_rep_float_data Pointer to the aerosol representation
774 * floating-point data
775 */
776void aero_rep_modal_binned_mass_print(int *aero_rep_int_data,
777 double *aero_rep_float_data) {
778 int *int_data = aero_rep_int_data;
779 double *float_data = aero_rep_float_data;
780
781 printf("\n\nModal/binned mass-only aerosol representation\n");
782
783 return;
784}
785
786/** \brief Create update data for new GMD
787 *
788 * \return Pointer to a new GMD update data object
789 */
791 int *update_data = (int *)malloc(3 * sizeof(int) + sizeof(double));
792 if (update_data == NULL) {
793 printf("\n\nERROR allocating space for GMD update data.\n\n");
794 exit(1);
795 }
796 return (void *)update_data;
797}
798
799/** \brief Set GMD update data
800 *
801 * \param update_data Pointer to an allocated GMD update data object
802 * \param aero_rep_id Id of the aerosol representation(s) to update
803 * \param section_id Id of the mode to update
804 * \param gmd New mode GMD (m)
805 */
807 int aero_rep_id,
808 int section_id,
809 double gmd) {
810 int *new_aero_rep_id = (int *)update_data;
811 int *update_type = (int *)&(new_aero_rep_id[1]);
812 int *new_section_id = (int *)&(update_type[1]);
813 double *new_GMD = (double *)&(new_section_id[1]);
814 *new_aero_rep_id = aero_rep_id;
815 *update_type = UPDATE_GMD;
816 *new_section_id = section_id;
817 *new_GMD = gmd;
818}
819
820/** \brief Create update data for new GSD
821 *
822 * \return Pointer to a new GSD update data object
823 */
825 int *update_data = (int *)malloc(3 * sizeof(int) + sizeof(double));
826 if (update_data == NULL) {
827 printf("\n\nERROR allocating space for GSD update data.\n\n");
828 exit(1);
829 }
830 return (void *)update_data;
831}
832
833/** \brief Set GSD update data
834 *
835 * \param update_data Pointer to an allocated GSD update data object
836 * \param aero_rep_id Id of the aerosol representation(s) to update
837 * \param section_id Id of the mode to update
838 * \param gsd New mode GSD (unitless)
839 */
841 int aero_rep_id,
842 int section_id,
843 double gsd) {
844 int *new_aero_rep_id = (int *)update_data;
845 int *update_type = (int *)&(new_aero_rep_id[1]);
846 int *new_section_id = (int *)&(update_type[1]);
847 double *new_GSD = (double *)&(new_section_id[1]);
848 *new_aero_rep_id = aero_rep_id;
849 *update_type = UPDATE_GSD;
850 *new_section_id = section_id;
851 *new_GSD = gsd;
852}
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)
#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)