CAMP 1.0.0
Chemistry Across Multiple Phases
aero_phase_solver.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 * Aerosol phase-specific functions for use by the solver
6 *
7 */
8/** \file
9 * \brief Aerosol phase functions
10 */
11#include "aero_phase_solver.h"
12#include <stdio.h>
13#include <stdlib.h>
14
15/// Minimum aerosol-phase mass concentration [kg m-3]
16#define MINIMUM_MASS_ 1.0e-25L
17/// Minimum mass assumed molecular weight [kg mol-1]
18#define MINIMUM_MW_ 0.1L
19/// Minimum mass assumed density [kg m-3]
20#define MINIMUM_DENSITY_ 1800.0L
21
22// TODO move all shared constants to a common header file
23#define CHEM_SPEC_UNKNOWN_TYPE 0
24#define CHEM_SPEC_VARIABLE 1
25#define CHEM_SPEC_CONSTANT 2
26#define CHEM_SPEC_PSSA 3
27#define CHEM_SPEC_ACTIVITY_COEFF 4
28
29#define NUM_STATE_VAR_ (int_data[0])
30#define NUM_INT_PROP_ 1
31#define NUM_FLOAT_PROP_ 0
32#define SPEC_TYPE_(x) (int_data[NUM_INT_PROP_ + x])
33#define MW_(x) (float_data[NUM_FLOAT_PROP_ + x])
34#define DENSITY_(x) (float_data[NUM_FLOAT_PROP_ + NUM_STATE_VAR_ + x])
35
36/** \brief Flag Jacobian elements used in calculations of mass and volume
37 *
38 * \param model_data Pointer to the model data(state, env, aero_phase)
39 * \param aero_phase_idx Index of the aerosol phase to find elements for
40 * \param state_var_id Index in the state array for this aerosol phase
41 * \param jac_struct 1D array of flags indicating potentially non-zero
42 * Jacobian elements. (The dependent variable should have
43 * been chosen by the calling function.)
44 * \return Number of Jacobian elements flagged
45 */
46int aero_phase_get_used_jac_elem(ModelData *model_data, int aero_phase_idx,
47 int state_var_id, bool *jac_struct) {
48 // Get the requested aerosol phase data
49 int *int_data = &(model_data->aero_phase_int_data
50 [model_data->aero_phase_int_indices[aero_phase_idx]]);
51 double *float_data =
52 &(model_data->aero_phase_float_data
53 [model_data->aero_phase_float_indices[aero_phase_idx]]);
54
55 int num_flagged_elem = 0;
56
57 for (int i_spec = 0; i_spec < NUM_STATE_VAR_; i_spec++) {
58 if (SPEC_TYPE_(i_spec) == CHEM_SPEC_VARIABLE ||
59 SPEC_TYPE_(i_spec) == CHEM_SPEC_CONSTANT ||
60 SPEC_TYPE_(i_spec) == CHEM_SPEC_PSSA) {
61 jac_struct[state_var_id + i_spec] = true;
62 num_flagged_elem++;
63 }
64 }
65
66 return num_flagged_elem;
67}
68
69/** \brief Get the mass and average MW in an aerosol phase
70 *
71 * \param model_data Pointer to the model data (state, env, aero_phase)
72 * \param aero_phase_idx Index of the aerosol phase to use in the calculation
73 * \param state_var Pointer the aerosol phase on the state variable array
74 * \param mass Pointer to hold total aerosol phase mass
75 * (\f$\mbox{\si{\kilogram\per\cubic\metre}}\f$ or
76 * \f$\mbox{\si{\kilogram\per particle}}\f$)
77 * \param MW Pointer to hold average MW of the aerosol phase
78 * (\f$\mbox{\si{\kilogram\per\mol}}\f$)
79 * \param jac_elem_mass When not NULL, a pointer to an array whose length is the
80 * number of Jacobian elements used in calculations of mass and
81 * volume of this aerosol phase returned by
82 * \c aero_phase_get_used_jac_elem and whose contents will be
83 * set to the partial derivatives of mass by concentration
84 * \f$\frac{dm}{dy_i}\f$ of each component species \f$y_i\f$.
85 * \param jac_elem_MW When not NULL, a pointer to an array whose length is the
86 * number of Jacobian elements used in calculations of mass and
87 * volume of this aerosol phase returned by
88 * \c aero_phase_get_used_jac_elem and whose contents will be
89 * set to the partial derivatives of total molecular weight by
90 * concentration \f$\frac{dMW}{dy_i}\f$ of each component
91 * species \f$y_i\f$.
92 */
93void aero_phase_get_mass__kg_m3(ModelData *model_data, int aero_phase_idx,
94 double *state_var, double *mass, double *MW,
95 double *jac_elem_mass, double *jac_elem_MW) {
96 // Get the requested aerosol phase data
97 int *int_data = &(model_data->aero_phase_int_data
98 [model_data->aero_phase_int_indices[aero_phase_idx]]);
99 double *float_data =
100 &(model_data->aero_phase_float_data
101 [model_data->aero_phase_float_indices[aero_phase_idx]]);
102
103 // Sum the mass and MW
104 long double l_mass = MINIMUM_MASS_;
105 long double moles = MINIMUM_MASS_ / MINIMUM_MW_;
106 int i_jac = 0;
107 for (int i_spec = 0; i_spec < NUM_STATE_VAR_; i_spec++) {
108 if (SPEC_TYPE_(i_spec) == CHEM_SPEC_VARIABLE ||
109 SPEC_TYPE_(i_spec) == CHEM_SPEC_CONSTANT ||
110 SPEC_TYPE_(i_spec) == CHEM_SPEC_PSSA) {
111 l_mass += state_var[i_spec];
112 moles += state_var[i_spec] / (long double)MW_(i_spec);
113 if (jac_elem_mass) jac_elem_mass[i_jac] = 1.0L;
114 if (jac_elem_MW) jac_elem_MW[i_jac] = 1.0L / MW_(i_spec);
115 i_jac++;
116 }
117 }
118 *MW = (double)l_mass / moles;
119 if (jac_elem_MW) {
120 for (int j_jac = 0; j_jac < i_jac; j_jac++) {
121 jac_elem_MW[j_jac] =
122 (moles - jac_elem_MW[j_jac] * l_mass) / (moles * moles);
123 }
124 }
125 *mass = (double)l_mass;
126}
127
128/** \brief Get the volume of an aerosol phase
129 *
130 * \param model_data Pointer to the model data (state, env, aero_phase)
131 * \param aero_phase_idx Index of the aerosol phase to use in the calculation
132 * \param state_var Pointer to the aerosol phase on the state variable array
133 * \param volume Pointer to hold the aerosol phase volume
134 * (\f$\mbox{\si{\cubic\metre\per\cubic\metre}}\f$ or
135 * \f$\mbox{\si{\cubic\metre\per particle}}\f$)
136 * \param jac_elem When not NULL, a pointer to an array whose length is the
137 * number of Jacobian elements used in calculations of mass and
138 * volume of this aerosol phase returned by
139 * \c aero_phase_get_used_jac_elem and whose contents will be
140 * set to the partial derivatives of total phase volume by
141 * concentration \f$\frac{dv}{dy_i}\f$ of each component
142 * species \f$y_i\f$.
143 */
144void aero_phase_get_volume__m3_m3(ModelData *model_data, int aero_phase_idx,
145 double *state_var, double *volume,
146 double *jac_elem) {
147 // Get the requested aerosol phase data
148 int *int_data = &(model_data->aero_phase_int_data
149 [model_data->aero_phase_int_indices[aero_phase_idx]]);
150 double *float_data =
151 &(model_data->aero_phase_float_data
152 [model_data->aero_phase_float_indices[aero_phase_idx]]);
153
154 // Sum the mass and MW
156 int i_jac = 0;
157 for (int i_spec = 0; i_spec < NUM_STATE_VAR_; i_spec++) {
158 if (SPEC_TYPE_(i_spec) == CHEM_SPEC_VARIABLE ||
159 SPEC_TYPE_(i_spec) == CHEM_SPEC_CONSTANT ||
160 SPEC_TYPE_(i_spec) == CHEM_SPEC_PSSA) {
161 *volume += state_var[i_spec] / DENSITY_(i_spec);
162 if (jac_elem) jac_elem[i_jac++] = 1.0 / DENSITY_(i_spec);
163 }
164 }
165}
166
167/** \brief Add condensed data to the condensed data block for aerosol phases
168 *
169 * \param n_int_param Number of integer parameters
170 * \param n_float_param Number of floating-point parameters
171 * \param int_param Pointer to the integer parameter array
172 * \param float_param Pointer to the floating-point parameter array
173 * \param solver_data Pointer to the solver data
174 */
175void aero_phase_add_condensed_data(int n_int_param, int n_float_param,
176 int *int_param, double *float_param,
177 void *solver_data) {
178 ModelData *model_data =
179 (ModelData *)&(((SolverData *)solver_data)->model_data);
180
181 // Get pointers to the aerosol phase data
182 int *aero_phase_int_data =
183 &(model_data->aero_phase_int_data[model_data->aero_phase_int_indices
184 [model_data->n_added_aero_phases]]);
185 double *aero_phase_float_data = &(
186 model_data->aero_phase_float_data[model_data->aero_phase_float_indices
187 [model_data->n_added_aero_phases]]);
188
189 // Save next indices by adding lengths
190 model_data->aero_phase_int_indices[model_data->n_added_aero_phases + 1] =
191 n_int_param +
192 model_data->aero_phase_int_indices[model_data->n_added_aero_phases];
193 model_data->aero_phase_float_indices[model_data->n_added_aero_phases + 1] =
194 n_float_param +
195 model_data->aero_phase_float_indices[model_data->n_added_aero_phases];
196 ++(model_data->n_added_aero_phases);
197
198 // Add the integer parameters
199 for (; n_int_param > 0; --n_int_param)
200 *(aero_phase_int_data++) = *(int_param++);
201
202 // Add the floating-point parameters
203 for (; n_float_param > 0; --n_float_param)
204 *(aero_phase_float_data++) = (double)*(float_param++);
205}
206
207/** \brief Print the aerosol phase data
208 * \param solver_data Pointer to the solver data
209 */
210void aero_phase_print_data(void *solver_data) {
211 ModelData *model_data =
212 (ModelData *)&(((SolverData *)solver_data)->model_data);
213
214 // Get the number of aerosol phases
215 int n_aero_phase = model_data->n_aero_phase;
216
217 // Loop through the aerosol phases and print their data
218 // advancing the aero_phase_data pointer each time
219 for (int i_aero_phase = 0; i_aero_phase < n_aero_phase; i_aero_phase++) {
220 int *int_data = &(model_data->aero_phase_int_data
221 [model_data->aero_phase_int_indices[i_aero_phase]]);
222 double *float_data =
223 &(model_data->aero_phase_float_data
224 [model_data->aero_phase_float_indices[i_aero_phase]]);
225
226 printf("\n\nAerosol Phase %d\n\n", i_aero_phase);
227 }
228 fflush(stdout);
229}
230
231#undef NUM_STATE_VAR_
232#undef NUM_INT_PROP_
233#undef NUM_FLOAT_PROP_
234#undef SPEC_TYPE_
235#undef MW_
236#undef DENSITY_
237#undef INT_DATA_SIZE_
238#undef FLOAT_DATA_SIZE_
#define CHEM_SPEC_VARIABLE
#define MINIMUM_MASS_
Minimum aerosol-phase mass concentration [kg m-3].
void aero_phase_print_data(void *solver_data)
Print the aerosol phase data.
#define MINIMUM_DENSITY_
Minimum mass assumed density [kg m-3].
#define DENSITY_(x)
#define CHEM_SPEC_CONSTANT
#define MW_(x)
#define SPEC_TYPE_(x)
#define NUM_STATE_VAR_
#define MINIMUM_MW_
Minimum mass assumed molecular weight [kg mol-1].
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_add_condensed_data(int n_int_param, int n_float_param, int *int_param, double *float_param, void *solver_data)
Add condensed data to the condensed data block for aerosol phases.
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.
#define CHEM_SPEC_PSSA
Header file for aerosol phase functions.
int * aero_phase_float_indices
int * aero_phase_int_indices
double * aero_phase_float_data
int * aero_phase_int_data
int n_added_aero_phases
int n_aero_phase