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 particle radius \f$r_{eff}\f$ (m)
269 *
270 * The modal mass effective radius is calculated for a log-normal distribution
271 * where the geometric mean diameter (\f$\tilde{D}_n\f$) and geometric standard
272 * deviation (\f$\sigma_g\f$) are set by the aerosol model prior to
273 * solving the chemistry. Thus, all \f$\frac{\partial r_{eff}}{\partial y}\f$
274 * are zero. The effective radius is calculated according to the equation given
275 * in Table 1 of Zender \cite Zender2002 :
276 *
277 * \f[
278 * \tilde{\sigma_g} \equiv ln( \sigma_g )
279 * \f]
280 * \f[
281 * D_s = D_{eff} = \tilde{D_n} e^{5 \tilde{\sigma}_g^2 / 2}
282 * \f]
283 * \f[
284 * r_{eff} = \frac{D_{eff}}{2}
285 * \f]
286 *
287 * For bins, \f$r_{eff}\f$ is assumed to be the bin radius.
288 *
289 * \param model_data Pointer to the model data, including the state array
290 * \param aero_phase_idx Index of the aerosol phase within the representation
291 * \param radius Effective particle radius (m)
292 * \param partial_deriv \f$\frac{\partial r_{eff}}{\partial y}\f$ where \f$y\f$
293 * are species on the state array
294 * \param aero_rep_int_data Pointer to the aerosol representation integer data
295 * \param aero_rep_float_data Pointer to the aerosol representation
296 * floating-point data
297 * \param aero_rep_env_data Pointer to the aerosol representation
298 * environment-dependent parameters
299 */
301 ModelData *model_data, int aero_phase_idx, double *radius,
302 double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data,
303 double *aero_rep_env_data) {
304 int *int_data = aero_rep_int_data;
305 double *float_data = aero_rep_float_data;
306
307 for (int i_section = 0; i_section < NUM_SECTION_; i_section++) {
308 for (int i_bin = 0; i_bin < NUM_BINS_(i_section); i_bin++) {
309 aero_phase_idx -= NUM_PHASE_(i_section);
310 if (aero_phase_idx < 0) {
311 *radius = EFFECTIVE_RADIUS_(i_section, i_bin);
312 // Effective radii are constant for bins and modes
313 if (partial_deriv) {
314 for (int i_phase = 0; i_phase < NUM_PHASE_(i_section); ++i_phase) {
315 for (int i_elem = 0;
316 i_elem < PHASE_NUM_JAC_ELEM_(i_section, i_phase, i_bin);
317 ++i_elem) {
318 *(partial_deriv++) = ZERO;
319 }
320 }
321 }
322 i_section = NUM_SECTION_;
323 break;
324 }
325 }
326 }
327
328 return;
329}
330
331/** \brief Get the effective particle surface area \f$r_{eff}\f$ (m)
332 *
333 * The surface area interface that exists between two specified phases within
334 * the modal/binned aerosol representation always returns an error, because no
335 * specific internal structure is assumed for phases within modes or bins.
336 *
337 * \param model_data Pointer to the model data, including the state array
338 * \param aero_phase_idx_first Index of the first aerosol phase within the representation
339 * \param aero_phase_idx_second Index of the second aerosol phase within the representation
340 * \param surface_area Effective surface area of interface between phases (m^2)
341 * \param partial_deriv \f$\frac{\partial sa_{eff}}{\partial y}\f$ where \f$y\f$
342 * are species on the state array
343 * \param aero_rep_int_data Pointer to the aerosol representation integer data
344 * \param aero_rep_float_data Pointer to the aerosol representation
345 * floating-point data
346 * \param aero_rep_env_data Pointer to the aerosol representation
347 * environment-dependent parameters
348 */
350 ModelData *model_data, int aero_phase_idx_first, int aero_phase_idx_second,
351 double *surface_area, double *partial_deriv, int *aero_rep_int_data,
352 double *aero_rep_float_data, double *aero_rep_env_data) {
353 int *int_data = aero_rep_int_data;
354 double *float_data = aero_rep_float_data;
355
356
357 printf("\n\nERROR There are no adjacent pairs in the modal/binned representation.\n\n");
358 exit(1);
359}
360
361/** \brief Get the particle number concentration \f$n\f$
362 * (\f$\mbox{\si{\#\per\cubic\metre}}\f$)
363 *
364 * The modal mass number concentration is calculated for a log-normal
365 * distribution where the geometric mean diameter (\f$\tilde{D}_n\f$) and
366 * geometric standard deviation (\f$\tilde{\sigma}_g\f$) are set by the aerosol
367 * model prior to solving the chemistry. The number concentration is
368 * calculated according to the equation given in Table 1 of Zender
369 * \cite Zender2002 :
370 * \f[
371 * n = N_0 = \frac{6V_0}{\pi}\tilde{D}_n^{-3}e^{-9
372 * ln(\tilde{\sigma}_g)^2/2} \f] \f[ V_0 = \sum_i{\frac{m_i}{\rho_i}} \f] where
373 * \f$\rho_i\f$ and \f$m_i\f$ are the density and total mass of species \f$i\f$
374 * in the specified mode.
375 *
376 * The binned number concentration is calculated according to:
377 * \f[
378 * n = V_0 / V_p
379 * \f]
380 * \f[
381 * V_p = \frac{4}{3}\pi r^{3}
382 * \f]
383 * where \f$r\f$ is the radius of the size bin and \f$V_0\f$ is defined as
384 * above.
385 *
386 * \param model_data Pointer to the model data, including the state array
387 * \param aero_phase_idx Index of the aerosol phase within the representation
388 * \param number_conc Particle number concentration, \f$n\f$
389 * (\f$\mbox{\si{\#\per\cubic\centi\metre}}\f$)
390 * \param partial_deriv \f$\frac{\partial n}{\partial y}\f$ where \f$y\f$ are
391 * the species on the state array
392 * \param aero_rep_int_data Pointer to the aerosol representation integer data
393 * \param aero_rep_float_data Pointer to the aerosol representation
394 * floating-point data
395 * \param aero_rep_env_data Pointer to the aerosol representation
396 * environment-dependent parameters
397 */
399 ModelData *model_data, int aero_phase_idx, double *number_conc,
400 double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data,
401 double *aero_rep_env_data) {
402 int *int_data = aero_rep_int_data;
403 double *float_data = aero_rep_float_data;
404
405 for (int i_section = 0; i_section < NUM_SECTION_ && aero_phase_idx >= 0;
406 i_section++) {
407 for (int i_bin = 0; i_bin < NUM_BINS_(i_section) && aero_phase_idx >= 0;
408 i_bin++) {
409 aero_phase_idx -= NUM_PHASE_(i_section);
410 if (aero_phase_idx < 0) {
411 *number_conc = NUMBER_CONC_(i_section, i_bin);
412 if (partial_deriv) {
413 for (int i_phase = 0; i_phase < NUM_PHASE_(i_section); ++i_phase) {
414 // Get a pointer to the phase on the state array
415 double *state = (double *)(model_data->grid_cell_state);
416 state += PHASE_STATE_ID_(i_section, i_phase, i_bin);
417
418 // Get the aerosol phase volume [m3 m-3]
419 double phase_volume = 0.0;
421 model_data, PHASE_MODEL_DATA_ID_(i_section, i_phase, i_bin),
422 state, &phase_volume, partial_deriv);
423
424 // Convert d_vol/d_conc to d_number/d_conc
425 for (int i_elem = 0;
426 i_elem < PHASE_NUM_JAC_ELEM_(i_section, i_phase, i_bin);
427 ++i_elem) {
428 switch (SECTION_TYPE_(i_section)) {
429 case (MODAL):
430 *(partial_deriv++) *=
431 6.0 / (M_PI * pow(GMD_(i_section), 3) *
432 exp(9.0 / 2.0 * pow(log(GSD_(i_section)), 2)));
433 break;
434 case (BINNED):
435 *(partial_deriv++) *= 3.0 / (4.0 * M_PI) /
436 pow(BIN_DP_(i_section, i_bin) / 2.0, 3);
437 break;
438 }
439 }
440 }
441 }
442 i_section = NUM_SECTION_;
443 break;
444 }
445 }
446 }
447
448 return;
449}
450
451/** \brief Get the type of aerosol concentration used.
452 *
453 * Modal mass concentrations are per-mode or per-bin.
454 *
455 * \param aero_phase_idx Index of the aerosol phase within the representation
456 * \param aero_conc_type Pointer to int that will hold the concentration type
457 * code
458 * \param aero_rep_int_data Pointer to the aerosol representation integer data
459 * \param aero_rep_float_data Pointer to the aerosol representation
460 * floating-point data
461 * \param aero_rep_env_data Pointer to the aerosol representation
462 * environment-dependent parameters
463 */
465 int *aero_conc_type,
466 int *aero_rep_int_data,
467 double *aero_rep_float_data,
468 double *aero_rep_env_data) {
469 int *int_data = aero_rep_int_data;
470 double *float_data = aero_rep_float_data;
471
472 *aero_conc_type = 1;
473
474 return;
475}
476
477/** \brief Get the total mass in an aerosol phase \f$m\f$
478 * (\f$\mbox{\si{\kilogram\per\cubic\metre}}\f$)
479 *
480 * \param model_data Pointer to the model data, including the state array
481 * \param aero_phase_idx Index of the aerosol phase within the representation
482 * \param aero_phase_mass Total mass in the aerosol phase, \f$m\f$
483 * (\f$\mbox{\si{\kilogram\per\cubic\metre}}\f$)
484 * \param partial_deriv \f$\frac{\partial m}{\partial y}\f$ where \f$y\f$ are
485 * the species on the state array
486 * \param aero_rep_int_data Pointer to the aerosol representation integer data
487 * \param aero_rep_float_data Pointer to the aerosol representation
488 * floating-point data
489 * \param aero_rep_env_data Pointer to the aerosol representation
490 * environment-dependent parameters
491 */
493 ModelData *model_data, int aero_phase_idx, double *aero_phase_mass,
494 double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data,
495 double *aero_rep_env_data) {
496 int *int_data = aero_rep_int_data;
497 double *float_data = aero_rep_float_data;
498
499 for (int i_section = 0; i_section < NUM_SECTION_ && aero_phase_idx >= 0;
500 ++i_section) {
501 for (int i_bin = 0; i_bin < NUM_BINS_(i_section) && aero_phase_idx >= 0;
502 ++i_bin) {
503 if (aero_phase_idx < 0 || aero_phase_idx >= NUM_PHASE_(i_section)) {
504 aero_phase_idx -= NUM_PHASE_(i_section);
505 continue;
506 }
507 for (int i_phase = 0; i_phase < NUM_PHASE_(i_section); ++i_phase) {
508 if (aero_phase_idx == 0) {
509 *aero_phase_mass = PHASE_MASS_(i_section, i_phase, i_bin);
510 if (partial_deriv) {
511 // Get a pointer to the phase on the state array
512 double *state = (double *)(model_data->grid_cell_state);
513 state += PHASE_STATE_ID_(i_section, i_phase, i_bin);
514
515 // Get d_mass / d_conc
516 double mass, mw;
518 model_data, PHASE_MODEL_DATA_ID_(i_section, i_phase, i_bin),
519 state, &mass, &mw, partial_deriv, NULL);
520 partial_deriv += PHASE_NUM_JAC_ELEM_(i_section, i_phase, i_bin);
521 }
522
523 // Other phases present in the bin or mode do not contribute to
524 // the aerosol phase mass
525 } else if (partial_deriv) {
526 for (int i_elem = 0;
527 i_elem < PHASE_NUM_JAC_ELEM_(i_section, i_phase, i_bin);
528 ++i_elem) {
529 *(partial_deriv++) = ZERO;
530 }
531 }
532 aero_phase_idx -= 1;
533 }
534 }
535 }
536
537 return;
538}
539
540/** \brief Get the average molecular weight in an aerosol phase
541 ** \f$m\f$ (\f$\mbox{\si{\kilogram\per\mole}}\f$)
542 *
543 * \param model_data Pointer to the model data, including the state array
544 * \param aero_phase_idx Index of the aerosol phase within the representation
545 * \param aero_phase_avg_MW Average molecular weight in the aerosol phase
546 * (\f$\mbox{\si{\kilogram\per\mole}}\f$)
547 * \param partial_deriv \f$\frac{\partial m}{\partial y}\f$ where \f$y\f$ are
548 * the species on the state array
549 * \param aero_rep_int_data Pointer to the aerosol representation integer data
550 * \param aero_rep_float_data Pointer to the aerosol representation
551 * floating-point data
552 * \param aero_rep_env_data Pointer to the aerosol representation
553 * environment-dependent parameters
554 */
556 ModelData *model_data, int aero_phase_idx, double *aero_phase_avg_MW,
557 double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data,
558 double *aero_rep_env_data) {
559 int *int_data = aero_rep_int_data;
560 double *float_data = aero_rep_float_data;
561
562 for (int i_section = 0; i_section < NUM_SECTION_ && aero_phase_idx >= 0;
563 ++i_section) {
564 for (int i_bin = 0; i_bin < NUM_BINS_(i_section) && aero_phase_idx >= 0;
565 ++i_bin) {
566 if (aero_phase_idx < 0 || aero_phase_idx >= NUM_PHASE_(i_section)) {
567 aero_phase_idx -= NUM_PHASE_(i_section);
568 continue;
569 }
570 for (int i_phase = 0; i_phase < NUM_PHASE_(i_section); ++i_phase) {
571 if (aero_phase_idx == 0) {
572 *aero_phase_avg_MW = PHASE_AVG_MW_(i_section, i_phase, i_bin);
573 if (partial_deriv) {
574 // Get a pointer to the phase on the state array
575 double *state = (double *)(model_data->grid_cell_state);
576 state += PHASE_STATE_ID_(i_section, i_phase, i_bin);
577
578 // Get d_MW / d_conc
579 double mass, mw;
581 model_data, PHASE_MODEL_DATA_ID_(i_section, i_phase, i_bin),
582 state, &mass, &mw, NULL, partial_deriv);
583 partial_deriv += PHASE_NUM_JAC_ELEM_(i_section, i_phase, i_bin);
584 }
585
586 // Other phases present in the bin/mode do not contribute to the
587 // average MW of the aerosol phase
588 } else if (partial_deriv) {
589 for (int i_elem = 0;
590 i_elem < PHASE_NUM_JAC_ELEM_(i_section, i_phase, i_bin);
591 ++i_elem) {
592 *(partial_deriv++) = ZERO;
593 }
594 }
595 aero_phase_idx -= 1;
596 }
597 }
598 }
599
600 return;
601}
602
603/** \brief Update the aerosol representation data
604 *
605 * The model mass aerosol representation update data is structured as follows:
606 *
607 * - \b int aero_rep_id (Id of one or more aerosol representations set by the
608 * host model using the
609 * camp_aero_rep_modal_binned_mass::aero_rep_modal_binned_mass_t::set_id
610 * function prior to initializing the solver.)
611 * - \b int update_type (Type of update to perform. Can be UPDATE_GMD or
612 * UPDATE_GSD.)
613 * - \b int section_id (Index of the mode to update.)
614 * - \b double new_value (Either the new GMD (m) or the new GSD (unitless).)
615 *
616 * \param update_data Pointer to the updated aerosol representation data
617 * \param aero_rep_int_data Pointer to the aerosol representation integer data
618 * \param aero_rep_float_data Pointer to the aerosol representation
619 * floating-point data
620 * \param aero_rep_env_data Pointer to the aerosol representation
621 * environment-dependent parameters
622 * \return Flag indicating whether this is the aerosol representation to update
623 */
625 int *aero_rep_int_data,
626 double *aero_rep_float_data,
627 double *aero_rep_env_data) {
628 int *int_data = aero_rep_int_data;
629 double *float_data = aero_rep_float_data;
630
631 int *aero_rep_id = (int *)update_data;
632 int *update_type = (int *)&(aero_rep_id[1]);
633 int *section_id = (int *)&(update_type[1]);
634 double *new_value = (double *)&(section_id[1]);
635 bool ret_val = false;
636
637 // Set the new GMD or GSD for matching aerosol representations
638 if (*aero_rep_id == AERO_REP_ID_ && AERO_REP_ID_ != 0) {
639 if (*update_type == UPDATE_GMD) {
640 if (SECTION_TYPE_(*section_id) != MODAL) {
641 printf(
642 "\n\nERROR Trying to set geometric mean diameter for non-modal"
643 " aerosol section.");
644 exit(1);
645 }
646 GMD_(*section_id) = (double)*new_value; // [m]
647 ret_val = true;
648 } else if (*update_type == UPDATE_GSD) {
649 if (SECTION_TYPE_(*section_id) != MODAL) {
650 printf(
651 "\n\nERROR Trying to set geometric standard deviation for non-modal"
652 " aerosol section.");
653 exit(1);
654 }
655 GSD_(*section_id) = (double)*new_value;
656 ret_val = true;
657 }
658 }
659
660 if (ret_val == true) {
661 /// Recalculate the effective radius [m]
662 ///
663 /// Equation based on \cite Zender2002
664 /// Table 1 effective diameter \f$(D_s, D_{eff}\f$) equations:
665 /// \f[
666 /// \tilde{\sigma_g} \equiv ln( \sigma_g )
667 /// \f]
668 /// \f[
669 /// D_s = D_{eff} = \tilde{D_n} e^{5 \tilde{\sigma}_g^2 / 2}
670 /// \f]
671 /// \f[
672 /// r_{eff} = \frac{D_{eff}}{2}
673 /// \f]
674 /// where \f$\tilde{D_n}\f$ is the geometric mean diameter [m],
675 /// \f$\sigma_g\f$
676 /// is the geometric standard deviation [unitless], and \f$r_{eff}\f$
677 /// is the effective radius [m].
678 ///
679 double ln_gsd = log(GSD_(*section_id));
680 EFFECTIVE_RADIUS_(*section_id, 0) =
681 GMD_(*section_id) / 2.0 * exp(5.0 * ln_gsd * ln_gsd / 2.0);
682 }
683
684 return ret_val;
685}
686
687/** \brief Print the mass-only modal/binned reaction parameters
688 *
689 * \param aero_rep_int_data Pointer to the aerosol representation integer data
690 * \param aero_rep_float_data Pointer to the aerosol representation
691 * floating-point data
692 */
693void aero_rep_modal_binned_mass_print(int *aero_rep_int_data,
694 double *aero_rep_float_data) {
695 int *int_data = aero_rep_int_data;
696 double *float_data = aero_rep_float_data;
697
698 printf("\n\nModal/binned mass-only aerosol representation\n");
699
700 return;
701}
702
703/** \brief Create update data for new GMD
704 *
705 * \return Pointer to a new GMD update data object
706 */
708 int *update_data = (int *)malloc(3 * sizeof(int) + sizeof(double));
709 if (update_data == NULL) {
710 printf("\n\nERROR allocating space for GMD update data.\n\n");
711 exit(1);
712 }
713 return (void *)update_data;
714}
715
716/** \brief Set GMD update data
717 *
718 * \param update_data Pointer to an allocated GMD update data object
719 * \param aero_rep_id Id of the aerosol representation(s) to update
720 * \param section_id Id of the mode to update
721 * \param gmd New mode GMD (m)
722 */
724 int aero_rep_id,
725 int section_id,
726 double gmd) {
727 int *new_aero_rep_id = (int *)update_data;
728 int *update_type = (int *)&(new_aero_rep_id[1]);
729 int *new_section_id = (int *)&(update_type[1]);
730 double *new_GMD = (double *)&(new_section_id[1]);
731 *new_aero_rep_id = aero_rep_id;
732 *update_type = UPDATE_GMD;
733 *new_section_id = section_id;
734 *new_GMD = gmd;
735}
736
737/** \brief Create update data for new GSD
738 *
739 * \return Pointer to a new GSD update data object
740 */
742 int *update_data = (int *)malloc(3 * sizeof(int) + sizeof(double));
743 if (update_data == NULL) {
744 printf("\n\nERROR allocating space for GSD update data.\n\n");
745 exit(1);
746 }
747 return (void *)update_data;
748}
749
750/** \brief Set GSD update data
751 *
752 * \param update_data Pointer to an allocated GSD update data object
753 * \param aero_rep_id Id of the aerosol representation(s) to update
754 * \param section_id Id of the mode to update
755 * \param gsd New mode GSD (unitless)
756 */
758 int aero_rep_id,
759 int section_id,
760 double gsd) {
761 int *new_aero_rep_id = (int *)update_data;
762 int *update_type = (int *)&(new_aero_rep_id[1]);
763 int *new_section_id = (int *)&(update_type[1]);
764 double *new_GSD = (double *)&(new_section_id[1]);
765 *new_aero_rep_id = aero_rep_id;
766 *update_type = UPDATE_GSD;
767 *new_section_id = section_id;
768 *new_GSD = gsd;
769}
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)
#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)
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)