CAMP 1.0.0
Chemistry Across Multiple Phases
aero_rep_single_particle.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 * Single particle aerosol representation functions
6 *
7 */
8/** \file
9 * \brief Single particle aerosol representation functions
10 */
11#include <stdio.h>
12#include <stdlib.h>
13#include "../aero_phase_solver.h"
14#include "../aero_reps.h"
15#include "../camp_solver.h"
16
17// TODO Lookup environmental indicies during initialization
18#define TEMPERATURE_K_ env_data[0]
19#define PRESSURE_PA_ env_data[1]
20
21#define UPDATE_NUMBER 0
22
23#define NUM_LAYERS_ int_data[0]
24#define AERO_REP_ID_ int_data[1]
25#define MAX_PARTICLES_ int_data[2]
26#define PARTICLE_STATE_SIZE_ int_data[3]
27#define NUMBER_CONC_(x) aero_rep_env_data[x]
28#define NUM_INT_PROP_ 4
29#define NUM_FLOAT_PROP_ 0
30#define NUM_ENV_PARAM_ MAX_PARTICLES_
31#define LAYER_PHASE_START_(l) (int_data[NUM_INT_PROP_+l]-1)
32#define LAYER_PHASE_END_(l) (int_data[NUM_INT_PROP_+NUM_LAYERS_+l]-1)
33#define TOTAL_NUM_PHASES_ (LAYER_PHASE_END_(NUM_LAYERS_-1)-LAYER_PHASE_START_(0)+1)
34#define NUM_PHASES_(l) (LAYER_PHASE_END_(l)-LAYER_PHASE_START_(l)+1)
35#define PHASE_STATE_ID_(l,p) (int_data[NUM_INT_PROP_+2*NUM_LAYERS_+LAYER_PHASE_START_(l)+p]-1)
36#define PHASE_MODEL_DATA_ID_(l,p) (int_data[NUM_INT_PROP_+2*NUM_LAYERS_+TOTAL_NUM_PHASES_+LAYER_PHASE_START_(l)+p]-1)
37#define PHASE_NUM_JAC_ELEM_(l,p) int_data[NUM_INT_PROP_+2*NUM_LAYERS_+2*TOTAL_NUM_PHASES_+LAYER_PHASE_START_(l)+p]
38
39/** \brief Flag Jacobian elements used in calcualtions of mass and volume
40 *
41 * \param aero_rep_int_data Pointer to the aerosol representation integer data
42 * \param aero_rep_float_data Pointer to the aerosol representation
43 * floating-point data
44 * \param model_data Pointer to the model data
45 * \param aero_phase_idx Index of the aerosol phase to find elements for
46 * \param jac_struct 1D array of flags indicating potentially non-zero
47 * Jacobian elements. (The dependent variable should have
48 * been chosen by the calling function.)
49 * \return Number of Jacobian elements flagged
50 */
51
53 int aero_phase_idx,
54 int *aero_rep_int_data,
55 double *aero_rep_float_data,
56 bool *jac_struct) {
57 int *int_data = aero_rep_int_data;
58 double *float_data = aero_rep_float_data;
59 int n_jac_elem = 0;
60 int i_part = aero_phase_idx / TOTAL_NUM_PHASES_;
61
62 // Each phase in a single particle has the same jac elements
63 // (one for each species in each phase in the particle)
64 for (int i_layer = 0; i_layer < NUM_LAYERS_; ++i_layer) {
65 for (int i_phase = 0; i_phase < NUM_PHASES_(i_layer); ++i_phase) {
67 model_data, PHASE_MODEL_DATA_ID_(i_layer,i_phase),
68 i_part * PARTICLE_STATE_SIZE_ + PHASE_STATE_ID_(i_layer,i_phase), jac_struct);
69 n_jac_elem += PHASE_NUM_JAC_ELEM_(i_layer,i_phase);
70 }
71 }
72 return n_jac_elem;
73}
74
75/** \brief Flag elements on the state array used by this aerosol representation
76 *
77 * The single particle aerosol representation functions do not use state array
78 * values
79 *
80 * \param aero_rep_int_data Pointer to the aerosol representation integer data
81 * \param aero_rep_float_data Pointer to the aerosol representation
82 * floating-point data
83 * \param state_flags Array of flags indicating state array elements used
84 */
85
87 double *aero_rep_float_data,
88 bool *state_flags) {
89 int *int_data = aero_rep_int_data;
90 double *float_data = aero_rep_float_data;
91
92 return;
93}
94
95/** \brief Update aerosol representation data for new environmental conditions
96 *
97 * The single particle aerosol representation does not use environmental
98 * conditions
99 *
100 * \param model_data Pointer to the model data
101 * \param aero_rep_int_data Pointer to the aerosol representation integer data
102 * \param aero_rep_float_data Pointer to the aerosol representation
103 * floating-point data
104 * \param aero_rep_env_data Pointer to the aerosol representation
105 * environment-dependent parameters
106 */
107
109 int *aero_rep_int_data,
110 double *aero_rep_float_data,
111 double *aero_rep_env_data) {
112 int *int_data = aero_rep_int_data;
113 double *float_data = aero_rep_float_data;
114 double *env_data = model_data->grid_cell_env;
115
116 return;
117}
118
119/** \brief Update aerosol representation data for a new state
120 *
121 * \param model_data Pointer to the model data, include the state array
122 * \param aero_rep_int_data Pointer to the aerosol representation integer data
123 * \param aero_rep_float_data Pointer to the aerosol representation
124 * floating-point data
125 * \param aero_rep_env_data Pointer to the aerosol representation
126 * environment-dependent parameters
127 */
128
130 int *aero_rep_int_data,
131 double *aero_rep_float_data,
132 double *aero_rep_env_data) {
133 int *int_data = aero_rep_int_data;
134 double *float_data = aero_rep_float_data;
135
136 return;
137}
138
139/** \brief Get the effective particle radius \f$r_{eff}\f$ (m)
140 *
141 * \param model_data Pointer to the model data, including the state array
142 * \param aero_phase_idx Index of the aerosol phase within the representation
143 * \param radius Effective particle radius (m)
144 * \param partial_deriv \f$\frac{\partial r_{eff}}{\partial y}\f$ where \f$y\f$
145 * are species on the state array
146 * \param aero_rep_int_data Pointer to the aerosol representation integer data
147 * \param aero_rep_float_data Pointer to the aerosol representation
148 * floating-point data
149 * \param aero_rep_env_data Pointer to the aerosol representation
150 * environment-dependent parameters
151 */
152
154 ModelData *model_data, int aero_phase_idx, double *radius,
155 double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data,
156 double *aero_rep_env_data) {
157
158 int *int_data = aero_rep_int_data;
159 double *float_data = aero_rep_float_data;
160 int i_part = aero_phase_idx / TOTAL_NUM_PHASES_;
161 double *curr_partial = NULL;
162
163 *radius = 0.0;
164 if (partial_deriv) curr_partial = partial_deriv;
165 for (int i_layer = 0; i_layer < NUM_LAYERS_; ++i_layer) {
166 for (int i_phase = 0; i_phase < NUM_PHASES_(i_layer); ++i_phase) {
167 double *state = (double *)(model_data->grid_cell_state);
168 state += i_part * PARTICLE_STATE_SIZE_ + PHASE_STATE_ID_(i_layer,i_phase);
169 double volume;
170 aero_phase_get_volume__m3_m3(model_data, PHASE_MODEL_DATA_ID_(i_layer,i_phase),
171 state, &(volume), curr_partial);
172 if (partial_deriv) curr_partial += PHASE_NUM_JAC_ELEM_(i_layer,i_phase);
173 *radius += volume;
174 }
175 }
176 *radius = pow(((*radius) * 3.0 / 4.0 / 3.14159265359), 1.0 / 3.0);
177 if (!partial_deriv) return;
178 for (int i_layer = 0; i_layer < NUM_LAYERS_; ++i_layer) {
179 for (int i_phase = 0; i_phase < NUM_PHASES_(i_layer); ++i_phase) {
180 for (int i_spec = 0; i_spec < PHASE_NUM_JAC_ELEM_(i_layer,i_phase); ++i_spec) {
181 *partial_deriv =
182 1.0 / 4.0 / 3.14159265359 * pow(*radius, -2.0) * (*partial_deriv);
183 ++partial_deriv;
184 }
185 }
186 }
187 return;
188}
189
190/** \brief Get the particle number concentration \f$n\f$
191 * (\f$\mbox{\si{\#\per\cubic\metre}}\f$)
192 *
193 * This single particle number concentration is set by the aerosol model prior
194 * to solving the chemistry. Thus, all \f$\frac{\partial n}{\partial y}\f$ are
195 * zero. Also, there is only one set of particles in the single particle
196 * representation, so the phase index is not used.
197 *
198 * \param model_data Pointer to the model data, including the state array
199 * \param aero_phase_idx Index of the aerosol phase within the representation
200 * (not used)
201 * \param number_conc Particle number concentration, \f$n\f$
202 * (\f$\mbox{\si{\#\per\cubic\metre}}\f$)
203 * \param partial_deriv \f$\frac{\partial n}{\partial y}\f$ where \f$y\f$ are
204 * the species on the state array
205 * \param aero_rep_int_data Pointer to the aerosol representation integer data
206 * \param aero_rep_float_data Pointer to the aerosol representation
207 * floating-point data
208 * \param aero_rep_env_data Pointer to the aerosol representation
209 * environment-dependent parameters
210 */
211
213 ModelData *model_data, int aero_phase_idx, double *number_conc,
214 double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data,
215 double *aero_rep_env_data) {
216
217
218 int *int_data = aero_rep_int_data;
219 double *float_data = aero_rep_float_data;
220 int i_part = aero_phase_idx / TOTAL_NUM_PHASES_;
221
222 *number_conc = NUMBER_CONC_(i_part);
223
224 if (partial_deriv) {
225 for (int i_layer = 0; i_layer < NUM_LAYERS_; ++i_layer) {
226 for (int i_phase = 0; i_phase < NUM_PHASES_(i_layer); ++i_phase) {
227 for (int i_spec = 0; i_spec < PHASE_NUM_JAC_ELEM_(i_layer,i_phase); ++i_spec)
228 *(partial_deriv++) = ZERO;
229 }
230 }
231 }
232 return;
233}
234
235/** \brief Get the type of aerosol concentration used.
236 *
237 * Single particle concentrations are per-particle.
238 *
239 * \param aero_phase_idx Index of the aerosol phase within the representation
240 * \param aero_conc_type Pointer to int that will hold the concentration type
241 * code (0 = per particle mass concentrations;
242 * 1 = total particle mass concentrations)
243 * \param aero_rep_int_data Pointer to the aerosol representation integer data
244 * \param aero_rep_float_data Pointer to the aerosol representation
245 * floating-point data
246 * \param aero_rep_env_data Pointer to the aerosol representation
247 * environment-dependent parameters
248 */
249
251 int *aero_conc_type,
252 int *aero_rep_int_data,
253 double *aero_rep_float_data,
254 double *aero_rep_env_data) {
255 int *int_data = aero_rep_int_data;
256 double *float_data = aero_rep_float_data;
257
258 *aero_conc_type = 0;
259
260 return;
261}
262
263/** \brief Get the total mass in an aerosol phase \f$m\f$
264 * (\f$\mbox{\si{\kilogram\per\cubic\metre}}\f$)
265 *
266 * The single particle mass is set for each new state as the sum of the masses
267 * of the aerosol phases that compose the particle
268 *
269 * \param model_data Pointer to the model data, including the state array
270 * \param aero_phase_idx Index of the aerosol phase within the representation
271 * \param aero_phase_mass Total mass in the aerosol phase, \f$m\f$
272 * (\f$\mbox{\si{\kilogram\per\cubic\metre}}\f$)
273 * \param partial_deriv \f$\frac{\partial m}{\partial y}\f$ where \f$y\f$ are
274 * the species on the state array
275 * \param aero_rep_int_data Pointer to the aerosol representation integer data
276 * \param aero_rep_float_data Pointer to the aerosol representation
277 * floating-point data
278 * \param aero_rep_env_data Pointer to the aerosol representation
279 * environment-dependent parameters
280 */
281
283 ModelData *model_data, int aero_phase_idx, double *aero_phase_mass,
284 double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data,
285 double *aero_rep_env_data) {
286
287 int *int_data = aero_rep_int_data;
288 double *float_data = aero_rep_float_data;
289 int i_part = aero_phase_idx / TOTAL_NUM_PHASES_;
290 aero_phase_idx -= i_part * TOTAL_NUM_PHASES_;
291
292 int i_total_phase = 0;
293 for (int i_layer = 0; i_layer < NUM_LAYERS_; ++i_layer) {
294 for (int i_phase = 0; i_phase < NUM_PHASES_(i_layer); ++i_phase) {
295 if ( i_total_phase == aero_phase_idx ) {
296 double *state = (double *)(model_data->grid_cell_state);
297 state += i_part * PARTICLE_STATE_SIZE_ + PHASE_STATE_ID_(i_layer,i_phase);
298 double mw;
299 aero_phase_get_mass__kg_m3(model_data, aero_phase_idx, state,
300 aero_phase_mass, &mw, partial_deriv, NULL);
301 if (partial_deriv) partial_deriv += PHASE_NUM_JAC_ELEM_(i_layer,i_phase);
302 } else if (partial_deriv) {
303 for (int i_spec = 0; i_spec < PHASE_NUM_JAC_ELEM_(i_layer,i_phase); ++i_spec)
304 *(partial_deriv++) = ZERO;
305 }
306 ++i_total_phase;
307 }
308 }
309 return;
310}
311
312/** \brief Get the average molecular weight in an aerosol phase
313 ** \f$m\f$ (\f$\mbox{\si{\kilo\gram\per\mol}}\f$)
314 *
315 * The single particle mass is set for each new state as the sum of the masses
316 * of the aerosol phases that compose the particle
317 *
318 * \param model_data Pointer to the model data, including the state array
319 * \param aero_phase_idx Index of the aerosol phase within the representation
320 * \param aero_phase_avg_MW Average molecular weight in the aerosol phase
321 * (\f$\mbox{\si{\kilogram\per\mole}}\f$)
322 * \param partial_deriv \f$\frac{\partial m}{\partial y}\f$ where \f$y\f$ are
323 * the species on the state array
324 * \param aero_rep_int_data Pointer to the aerosol representation integer data
325 * \param aero_rep_float_data Pointer to the aerosol representation
326 * floating-point data
327 * \param aero_rep_env_data Pointer to the aerosol representation
328 * environment-dependent parameters
329 */
330
332 ModelData *model_data, int aero_phase_idx, double *aero_phase_avg_MW,
333 double *partial_deriv, int *aero_rep_int_data, double *aero_rep_float_data,
334 double *aero_rep_env_data) {
335
336 int *int_data = aero_rep_int_data;
337 double *float_data = aero_rep_float_data;
338 int i_part = aero_phase_idx / TOTAL_NUM_PHASES_;
339 aero_phase_idx -= i_part * TOTAL_NUM_PHASES_;
340
341 int i_total_phase = 0;
342 for (int i_layer = 0; i_layer < NUM_LAYERS_; ++i_layer) {
343 for (int i_phase = 0; i_phase < NUM_PHASES_(i_layer); ++i_phase) {
344 if ( i_total_phase == aero_phase_idx ) {
345 double *state = (double *)(model_data->grid_cell_state);
346 state += i_part * PARTICLE_STATE_SIZE_ + PHASE_STATE_ID_(i_layer,i_phase);
347 double mass;
348 aero_phase_get_mass__kg_m3(model_data, aero_phase_idx, state, &mass,
349 aero_phase_avg_MW, NULL, partial_deriv);
350 if (partial_deriv) partial_deriv += PHASE_NUM_JAC_ELEM_(i_layer,i_phase);
351 } else if (partial_deriv) {
352 for (int i_spec = 0; i_spec < PHASE_NUM_JAC_ELEM_(i_layer,i_phase); ++i_spec)
353 *(partial_deriv++) = ZERO;
354 }
355 ++i_total_phase;
356 }
357 }
358 return;
359}
360
361/** \brief Update aerosol representation data
362 *
363 * Single particle aerosol representation update data is structured as follows:
364 *
365 * - \b int aero_rep_id (Id of one or more aerosol representations set by the
366 * host model using the
367 * camp_aero_rep_single_particle::aero_rep_single_particle_t::set_id
368 * function prior to initializing the solver.)
369 * - \b int update_type (Type of update to perform. Can be UPDATE_NUMBER
370 * only.)
371 * - \b double new_value (Either the new radius (m) or the new number
372 * concentration (\f$\mbox{\si{\#\per\cubic\centi\metre}}\f$).)
373 *
374 * \param update_data Pointer to the updated aerosol representation data
375 * \param aero_rep_int_data Pointer to the aerosol representation integer data
376 * \param aero_rep_float_data Pointer to the aerosol representation
377 * floating-point data
378 * \param aero_rep_env_data Pointer to the aerosol representation
379 * environment-dependent parameters
380 * \return Flag indicating whether this is the aerosol representation to update
381 */
382
384 int *aero_rep_int_data,
385 double *aero_rep_float_data,
386 double *aero_rep_env_data) {
387 int *int_data = aero_rep_int_data;
388 double *float_data = aero_rep_float_data;
389
390 int *aero_rep_id = (int *)update_data;
391 int *update_type = (int *)&(aero_rep_id[1]);
392 int *particle_id = (int *)&(update_type[1]);
393 double *new_value = (double *)&(update_type[2]);
394
395 // Set the new radius or number concentration for matching aerosol
396 // representations
397 if (*aero_rep_id == AERO_REP_ID_ && AERO_REP_ID_ != 0) {
398 if (*update_type == UPDATE_NUMBER) {
399 NUMBER_CONC_(*particle_id) = (double)*new_value;
400 return true;
401 }
402 }
403
404 return false;
405}
406
407/** \brief Print the Single Particle reaction parameters
408 *
409 * \param aero_rep_int_data Pointer to the aerosol representation integer data
410 * \param aero_rep_float_data Pointer to the aerosol representation
411 * floating-point data
412 */
413
414void aero_rep_single_particle_print(int *aero_rep_int_data,
415 double *aero_rep_float_data) {
416 int *int_data = aero_rep_int_data;
417 double *float_data = aero_rep_float_data;
418
419 printf("\n\nSingle particle aerosol representation\n");
420 printf("\nNumber of phases: %d", TOTAL_NUM_PHASES_);
421 printf("\nAerosol representation id: %d", AERO_REP_ID_);
422 printf("\nMax computational particles: %d", MAX_PARTICLES_);
423 printf("\nParticle state size: %d", PARTICLE_STATE_SIZE_);
424 for(int i_layer = 0; i_layer < NUM_LAYERS_; ++i_layer){
425 printf("\nLayer: %d", i_layer);
426 printf("\n\n - Phases -");
427 for (int i_phase = 0; i_phase < NUM_PHASES_(i_layer); ++i_phase) {
428 printf("\n state id: %d model data id: %d num Jac elements: %d",
429 PHASE_STATE_ID_(i_layer,i_phase), PHASE_MODEL_DATA_ID_(i_layer,i_phase),
430 PHASE_NUM_JAC_ELEM_(i_layer,i_phase));
431 }
432 }
433 printf("\n\nEnd single particle aerosol representation\n");
434 return;
435}
436
437
438/** \brief Create update data for new particle number
439 *
440 * \return Pointer to a new number update data object
441 */
443 int *update_data = (int *)malloc(3 * sizeof(int) + sizeof(double));
444 if (update_data == NULL) {
445 printf("\n\nERROR allocating space for number update data\n\n");
446 exit(1);
447 }
448 return (void *)update_data;
449}
450
451/** \brief Set number update data (#/m3)
452 *
453 * \param update_data Pointer to an allocated number update data object
454 * \param aero_rep_id Id of the aerosol representation(s) to update
455 * \param particle_id Id of the computational particle
456 * \param number_conc New particle number (#/m3)
457 */
459 int aero_rep_id,
460 int particle_id,
461 double number_conc) {
462 int *new_aero_rep_id = (int *)update_data;
463 int *update_type = (int *)&(new_aero_rep_id[1]);
464 int *new_particle_id = (int *)&(update_type[1]);
465 double *new_number_conc = (double *)&(update_type[2]);
466 *new_aero_rep_id = aero_rep_id;
467 *update_type = UPDATE_NUMBER;
468 *new_particle_id = particle_id;
469 *new_number_conc = number_conc;
470}
471
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.
int aero_rep_single_particle_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.
void aero_rep_single_particle_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 ( )
#define NUM_PHASES_(l)
void aero_rep_single_particle_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)
#define PARTICLE_STATE_SIZE_
void aero_rep_single_particle_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.
void * aero_rep_single_particle_create_number_update_data()
Create update data for new particle number.
#define PHASE_NUM_JAC_ELEM_(l, p)
#define AERO_REP_ID_
void aero_rep_single_particle_print(int *aero_rep_int_data, double *aero_rep_float_data)
Print the Single Particle reaction parameters.
#define MAX_PARTICLES_
#define PHASE_MODEL_DATA_ID_(l, p)
#define TOTAL_NUM_PHASES_
#define PHASE_STATE_ID_(l, p)
void aero_rep_single_particle_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.
bool aero_rep_single_particle_update_data(void *update_data, int *aero_rep_int_data, double *aero_rep_float_data, double *aero_rep_env_data)
Update aerosol representation data.
void aero_rep_single_particle_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 UPDATE_NUMBER
#define NUM_LAYERS_
void aero_rep_single_particle_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 ( )
#define NUMBER_CONC_(x)
void aero_rep_single_particle_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_single_particle_set_number_update_data__n_m3(void *update_data, int aero_rep_id, int particle_id, double number_conc)
Set number update data (#/m3)
void aero_rep_single_particle_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.
#define ZERO
Definition camp_common.h:42
double * grid_cell_env
double * grid_cell_state