CAMP 1.0.0
Chemistry Across Multiple Phases
sub_model_UNIFAC.F90
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!> \file
6!> The camp_sub_model_UNIFAC module.
7
8!> \page camp_sub_model_UNIFAC CAMP: UNIFAC Activity Coefficients
9!!
10!! The UNIFAC activity coefficient sub model calculates activity coefficients
11!! for species in an aerosol phase based on the current aerosol phase
12!! composition \cite Marcolli2005. The \c json object for this
13!! \ref camp_sub_model "sub model" has the following format :
14!! \code{.json}
15!! { "camp-data" : [
16!! {
17!! "name" : "my UNIFAC activity",
18!! "type" : "SUB_MODEL_UNIFAC",
19!! "phases" : [
20!! "some phase",
21!! "some other phase"
22!! ],
23!! "functional groups" : {
24!! "CH3" : {
25!! "main group" : "CH2",
26!! "volume param" : 0.9011,
27!! "surface param" : 0.8480
28!! },
29!! "CH2" : {
30!! "main group" : "CH2",
31!! "volume param" : 0.6744,
32!! "surface param" : 0.5400
33!! },
34!! "CH=CH" : {
35!! "main group" : "C=C",
36!! "volume param" : 1.1167,
37!! "suface param" : 0.8670
38!! }
39!! },
40!! "main groups" : {
41!! "CH2" : {
42!! "interactions with" : {
43!! "C=C" : -35.36
44!! }
45!! },
46!! "C=C" : {
47!! "interactions with" : {
48!! "CH2" : 86.02
49!! }
50!! }
51!! }
52!! },
53!! ...
54!! ]}
55!! \endcode
56!! The key-value pair \b type is required and must be \b SUB_MODEL_UNIFAC.
57!! The key-value pair \b phases is also required, and its value must be an
58!! array of strings that correspond to valid
59!! \ref camp_aero_phase "aerosol phases". The key-value pair \b functional
60!! \b groups is also required, and must contain a set of key-value pairs whose
61!! keys are the names of UNIFAC functions groups, and whose values are a set
62!! of key value pairs that contain, at minimum:
63!! - \b main \b group : a string that corresponds to a key in the \b
64!! main \b groups set.
65!! - \b volume \b param : the floating-point volume parameter for this
66!! functional group.
67!! - \b surface \b param : this floating-point surface parameter for this
68!! functional group.
69!! The last required key-value pair is \b main \b groups whose value must
70!! be a set of key-value pairs whose keys are the names of the UNIFAC main
71!! groups and whose values are a set key-pairs that contain, at minimum,
72!! \b interaction \b with whose value is a set of key-value pairs whose keys
73!! are the names of the other \b main \b groups and whose values are the
74!! floating-point interation parameters for that interaction. Each main group
75!! may contain up to one interaction with each other main group, and may
76!! not contain an interaction with itself. Missing interactions are assumed
77!! to be 0.0.
78!!
79!! Species in the specified phase for whom acitivity coefficients will be
80!! calculated must contain a key-value pair \b UNIFAC \b groups whose value
81!! is a set of key value pairs that correspond with members of the
82!! \b functional \b groups set and whose values are the integer number of
83!! instances of a particular functional group in this species. There must also
84!! be a \b CHEM_SPEC_ACTIVITY_COEFF species with a key value pair \b chemical
85!! \b species whose value is the name of the chemical species. Both the
86!! chemical species and the activity coefficient must be included in any
87!! aerosol phase for which activities are being calculated.
88!! For the above example UNIFAC model, the following species would be valid
89!! and included in activity coefficient calculations:
90!! \code{.json}
91!! { "camp-data" : [
92!! {
93!! "name" : "my species",
94!! "type" : "CHEM_SPEC",
95!! "phase" : "AEROSOL",
96!! "UNIFAC groups" : {
97!! "CH3" : 4,
98!! "C=C" : 1
99!! }
100!! },
101!! {
102!! "name" : "gamma my species",
103!! "type" : "CHEM_SPEC",
104!! "phase" : "AEROSOL",
105!! "tracer type" : "ACTIVITY_COEFF",
106!! "chemical species" : "my species"
107!! },
108!! {
109!! "name" : "my other species",
110!! "type" : "CHEM_SPEC",
111!! "phase" : "AEROSOL",
112!! "UNIFAC groups" : {
113!! "CH3" : 2,
114!! "CH2" : 4
115!! },
116!! },
117!! {
118!! "name" : "gamma my other species",
119!! "type" : "CHEM_SPEC",
120!! "phase" : "AEROSOL",
121!! "tracer type" : "ACTIVITY_COEFF"
122!! "chemical species" : "my other species"
123!! },
124!! {
125!! "name" : "some phase",
126!! "type" : "AERO_PHASE",
127!! "species" : { "my species", "gamma my species",
128!! "my other species", gamma my other species"}
129!! }
130!! ]}
131!! \endcode
132
133!> The sub_model_UNIFAC_t type and assocatiated subroutines
134!!
135!! This module is based on the UNIFAC module of Alf Grini, CNRM, 2005, which
136!! in turn, was based original code received from Pierre Tulet, who got it
137!! from Betty Pun who (it seems) got it from Pradeep Saxena. The UNIFAC
138!! module is part of the Model to Predict the Multi-phase Partitioning of
139!! Organics (Griffin et al., JGR 110, D05304, 2005 doi: 10.1029/2004JD005219)
140!!
141!! Equations referenced are from Marcolli and Peter, ACP 5(2), 1501-1527,
142!! 2005, and variable names roughly follow their naming scheme.
143!!
145
150 use camp_property
152 use camp_util, only : dp, i_kind, &
155 assert
156
157 implicit none
158 private
159
160#define NUM_UNIQUE_PHASE_ this%condensed_data_int(1)
161#define NUM_GROUP_ this%condensed_data_int(2)
162#define TOTAL_INT_PROP_ this%condensed_data_int(3)
163#define TOTAL_REAL_PROP_ this%condensed_data_int(4)
164#define NUM_INT_PROP_ 4
165#define NUM_REAL_PROP_ 0
166#define PHASE_INT_LOC_(p) this%condensed_data_int(NUM_INT_PROP_+p)
167#define PHASE_FLOAT_LOC_(p) this%condensed_data_int(NUM_INT_PROP_+NUM_UNIQUE_PHASE_+p)
168#define PHASE_ENV_LOC_(p) this%condensed_data_int(NUM_INT_PROP_+2*NUM_UNIQUE_PHASE_+p)
169#define NUM_PHASE_INSTANCE_(p) this%condensed_data_int(PHASE_INT_LOC_(p))
170#define NUM_SPEC_(p) this%condensed_data_int(PHASE_INT_LOC_(p)+1)
171#define PHASE_INST_ID_(p,c) this%condensed_data_int(PHASE_INT_LOC_(p)+1+c)
172#define SPEC_ID_(p,i) this%condensed_data_int(PHASE_INT_LOC_(p)+1+NUM_PHASE_INSTANCE_(p)+i)
173#define GAMMA_ID_(p,i) this%condensed_data_int(PHASE_INT_LOC_(p)+1+NUM_PHASE_INSTANCE_(p)+NUM_SPEC_(p)+i)
174#define JAC_ID_(p,c,j,i) this%condensed_data_int(PHASE_INT_LOC_(p)+1+NUM_PHASE_INSTANCE_(p)+(2+(c-1)*NUM_SPEC_(p)+(j-1))*NUM_SPEC_(p)+i)
175#define V_IK_(p,i,k) this%condensed_data_int(PHASE_INT_LOC_(p)+1+NUM_PHASE_INSTANCE_(p)+(k+1+NUM_PHASE_INSTANCE_(p)*NUM_SPEC_(p))*NUM_SPEC_(p)+i)
176
177#define Q_K_(k) this%condensed_data_real(k)
178#define R_K_(k) this%condensed_data_real(NUM_GROUP_+k)
179#define X_K_(m) this%condensed_data_real(2*NUM_GROUP_+m)
180#define DTHETA_M_DC_I_(m) this%condensed_data_real(3*NUM_GROUP_+m)
181#define XI_M_(m) this%condensed_data_real(4*NUM_GROUP_+m)
182#define LN_GAMMA_K_(m) this%condensed_data_real(5*NUM_GROUP_+m)
183#define A_MN_(m,n) this%condensed_data_real((m+5)*NUM_GROUP_+n)
184#define R_I_(p,i) this%condensed_data_real(PHASE_FLOAT_LOC_(p)+i-1)
185#define Q_I_(p,i) this%condensed_data_real(PHASE_FLOAT_LOC_(p)+NUM_SPEC_(p)+i-1)
186#define L_I_(p,i) this%condensed_data_real(PHASE_FLOAT_LOC_(p)+2*NUM_SPEC_(p)+i-1)
187#define MW_I_(p,i) this%condensed_data_real(PHASE_FLOAT_LOC_(p)+3*NUM_SPEC_(p)+i-1)
188#define X_I_(p,i) this%condensed_data_real(PHASE_FLOAT_LOC_(p)+4*NUM_SPEC_(p)+i-1)
189
190 ! Update types (These must match values in sub_model_UNIFAC.c)
191 ! (none for now)
192
193 public :: sub_model_unifac_t
194
195 !> UNIFAC activity coefficient calculation
196 !!
197 !! Time-invariant data required by the UNIFAC activity coefficient sub model
199 contains
200 !> Initialize the sub model data, validating input parameters and
201 !! loading any required information form the \c
202 !! sub_model_data_t::property_set. This routine should be called
203 !! once for each sub model at the beginning of the model run after all
204 !! the input files have been read in. It ensures all data required
205 !! during the model run are included in the condensed data arrays.
206 procedure :: initialize
207 !> Return a real number representing the priority of the sub-model
208 !! calculations. Low priority sub models may depend on the results
209 !! of higher priority sub models.
210 procedure :: priority
211 !> Finalize the sub-model
212 final :: finalize
213 end type sub_model_unifac_t
214
215 ! Constructor for sub_model_UNIFAC_t
216 interface sub_model_unifac_t
217 procedure :: constructor
218 end interface sub_model_unifac_t
219
220contains
221
222!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
223
224 !> Constructor for sub_model_UNIFAC_t
225 function constructor() result (new_obj)
226
227 !> New sub model
228 type(sub_model_unifac_t), pointer :: new_obj
229
230 allocate(new_obj)
231
232 end function constructor
233
234!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
235
236 !> Initialize the sub model data, validating input parameters and
237 !! loading any required information form the \c
238 !! sub_model_data_t::property_set. This routine should be called
239 !! once for each sub model at the beginning of the model run after all
240 !! the input files have been read in. It ensures all data required
241 !! during the model run are included in the condensed data arrays.
242 subroutine initialize(this, aero_rep_set, aero_phase_set, chem_spec_data)
243
244 !> Sub model data
245 class(sub_model_unifac_t), intent(inout) :: this
246 !> The set of aerosol representations
247 type(aero_rep_data_ptr), pointer, intent(in) :: aero_rep_set(:)
248 !> The set of aerosol phases
249 type(aero_phase_data_ptr), pointer, intent(in) :: aero_phase_set(:)
250 !> Chemical species data
251 type(chem_spec_data_t), intent(in) :: chem_spec_data
252
253 type(property_t), pointer :: spec_props, phases
254 type(property_t), pointer :: func_groups, func_group
255 type(property_t), pointer :: main_groups, main_group
256 type(property_t), pointer :: spec_groups, interactions
257 character(len=:), allocatable :: key_name, phase_name, spec_name
258 character(len=:), allocatable :: inter_group_name
259 character(len=:), allocatable :: main_group_name, spec_group_name
260 integer(kind=i_kind) :: i_spec, j_spec, i_phase, i_rep, i_main_group
261 integer(kind=i_kind) :: i_instance, i_inter, i_phase_inst, i_spec_group
262 integer(kind=i_kind) :: i_group, i_inter_group
263 integer(kind=i_kind) :: i_UNIFAC_phase
264 integer(kind=i_kind) :: num_unique_phase, num_group, num_main_group
265 integer(kind=i_kind) :: num_int_data, num_real_data, num_env_data
266 integer(kind=i_kind) :: num_spec_group, curr_spec_id, curr_phase_inst_id
267 integer(kind=i_kind) :: m, n, spec_type
268 integer(kind=i_kind), allocatable :: num_phase_inst(:)
269 integer(kind=i_kind), allocatable :: num_phase_spec(:)
270 integer(kind=i_kind), allocatable :: unique_phase_set_id(:)
271 integer(kind=i_kind), allocatable :: main_group_id(:)
272 type(string_t), allocatable :: phase_names(:)
273 type(string_t), allocatable :: main_group_names(:)
274 type(string_t), allocatable :: group_names(:)
275 type(string_t), allocatable :: unique_names(:)
276 type(string_t), allocatable :: spec_names(:)
277 real(kind=dp) :: q_i, r_i
278 real(kind=dp) :: inter_param
279 real(kind=dp), allocatable :: main_group_interactions(:,:)
280 logical :: found, phase_ids_set
281
282 ! Get the property set
283 call assert_msg(403771584, associated(this%property_set), &
284 "Missing property set needed to initialize UNIFAC model.")
285
286 ! Get the aerosol phase names
287 key_name = "phases"
288 call assert_msg(114578789, &
289 this%property_set%get_property_t(key_name, phases), &
290 "Missing set of aerosol phase names for UNIFAC model.")
291 num_unique_phase = phases%size()
292 call assert_msg(780318074, num_unique_phase.gt.0, &
293 "Received empty list of aerosol phase names for UNIFAC model.")
294
295 ! Get the functional groups
296 key_name = "functional groups"
297 call assert_msg(372089388, &
298 this%property_set%get_property_t(key_name, func_groups), &
299 "Missing set of functional groups for UNIFAC model.")
300 num_group = func_groups%size()
301 call assert_msg(974837385, num_group.gt.0, &
302 "Received empty set of functional groups for UNIFAC model.")
303
304 ! Get the main groups
305 key_name = "main groups"
306 call assert_msg(956137986, &
307 this%property_set%get_property_t(key_name, main_groups), &
308 "Missing set of main groups for UNIFAC model.")
309 num_main_group = main_groups%size()
310 call assert_msg(556532380, num_main_group.gt.0, &
311 "Received empty set of main groups for UNIFAC model.")
312
313 ! Count the species in each phase, and the number of instances of each
314 ! phase
315 allocate(num_phase_inst(num_unique_phase))
316 allocate(num_phase_spec(num_unique_phase))
317 allocate(unique_phase_set_id(num_unique_phase))
318 allocate(phase_names(num_unique_phase))
319 key_name = "UNIFAC groups"
320 call phases%iter_reset()
321 do i_unifac_phase = 1, num_unique_phase
322 call assert_msg(112836027, phases%get_string(val = phase_name), &
323 "Received non-string phase name in UNIFAC model.")
324 phase_names(i_unifac_phase)%string = phase_name
325 found = .false.
326 do i_phase = 1, size(aero_phase_set)
327 if (aero_phase_set(i_phase)%val%name().eq.phase_name) then
328 found = .true.
329 unique_phase_set_id(i_unifac_phase) = i_phase
330 num_phase_spec(i_unifac_phase) = 0
331 spec_names = aero_phase_set(i_phase)%val%get_species_names()
332 do i_spec = 1, size(spec_names)
333 call assert(698678581, &
334 chem_spec_data%get_property_set( &
335 spec_names(i_spec)%string, spec_props))
336 if (spec_props%get_property_t(key_name, spec_groups)) then
337 num_phase_spec(i_unifac_phase) = &
338 num_phase_spec(i_unifac_phase) + 1
339 end if
340 end do
341 deallocate(spec_names)
342 end if
343 end do
344 call assert_msg(835247755, found, "Cannot find aerosol phase '"// &
345 phase_name//"' for UNIFAC model.")
346
347 num_phase_inst(i_unifac_phase) = 0
348 do i_rep = 1, size(aero_rep_set)
349 num_phase_inst(i_unifac_phase) = num_phase_inst(i_unifac_phase) + &
350 aero_rep_set(i_rep)%val%num_phase_instances(phase_name)
351 end do
352 call assert_msg(187041753, num_phase_inst(i_unifac_phase).gt.0, &
353 "No instances of phase '"//phase_name//"' for UNIFAC model.")
354
355 call phases%iter_next()
356 end do
357
358 ! Size of condensed data arrays
359 num_int_data = num_int_prop_ & ! int props
360 + 3*num_unique_phase ! PHASE_INT_LOC, PHASE_REAL_LOC,
361 ! PHASE_ENV_LOC
362 num_real_data = num_real_prop_ & ! real props
363 + 6*num_group & ! Q_k, R_k, X_k,
364 ! dTheta_n / dc_i, ln(gamma_k), Xi_m
365 + num_group*num_group ! a_mn
366 num_env_data = num_group & ! THETA_m
367 + num_group*num_group ! PSI_mn
368 do i_unifac_phase = 1, num_unique_phase
369 num_int_data = num_int_data + 2 & ! NUM_PHASE_INSTANCE, NUM_SPEC
370 + num_phase_inst(i_unifac_phase) & ! PHASE_INST_ID
371 + num_phase_spec(i_unifac_phase) & ! SPEC_ID
372 + num_phase_spec(i_unifac_phase) & ! GAMMA_ID
373 + num_phase_inst(i_unifac_phase) * &
374 num_phase_spec(i_unifac_phase) * &
375 num_phase_spec(i_unifac_phase) & ! SPEC_JAC_ID
376 + num_phase_spec(i_unifac_phase) * num_group ! v_ik
377 num_real_data = num_real_data &
378 + 5*num_phase_spec(i_unifac_phase) ! r_i, q_i, l_i, MW_i, X_i
379 num_env_data = num_env_data &
380 + num_phase_spec(i_unifac_phase) * num_group ! ln_GAMMA_ik
381 end do
382
383 ! Allocate condensed data arrays
384 allocate(this%condensed_data_int(num_int_data))
385 allocate(this%condensed_data_real(num_real_data))
386 this%condensed_data_int(:) = int(999999, kind=i_kind)
387 this%condensed_data_real(:) = real(999999.0, kind=dp)
388
389 ! Save space for the environment-dependent parameters
390 this%num_env_params = num_env_data
391
392 ! Set sub model dimensions
393 num_unique_phase_ = num_unique_phase
394 num_group_ = num_group
395 total_int_prop_ = size(this%condensed_data_int)
396 total_real_prop_ = size(this%condensed_data_real)
397
398 ! Set data locations
399 num_int_data = num_int_prop_ & ! int props
400 + 3*num_unique_phase ! PHASE_INT_LOC, PHASE_REAL_LOC,
401 ! PHASE_ENV_LOC
402 num_real_data = num_real_prop_ & ! real props
403 + 6*num_group & ! Q_k, R_k, THETA_m, X_k,
404 ! dTheta_n / dc_i, ln(Gamma_k), Xi_m
405 + num_group*num_group ! a_mn, PSI_mn
406 num_env_data = num_group & ! THETA_m
407 + num_group*num_group ! PSI_mn
408 do i_unifac_phase = 1, num_unique_phase
409 phase_int_loc_(i_unifac_phase) = num_int_data + 1
410 phase_float_loc_(i_unifac_phase) = num_real_data + 1
411 phase_env_loc_(i_unifac_phase) = num_env_data + 1
412 num_int_data = num_int_data + 2 & ! NUM_PHASE_INSTANCE, NUM_SPEC
413 + num_phase_inst(i_unifac_phase) & ! PHASE_INST_ID
414 + num_phase_spec(i_unifac_phase) & ! SPEC_ID
415 + num_phase_spec(i_unifac_phase) & ! GAMMA_ID
416 + num_phase_inst(i_unifac_phase) * &
417 num_phase_spec(i_unifac_phase) * &
418 num_phase_spec(i_unifac_phase) & ! SPEC_JAC_ID
419 + num_phase_spec(i_unifac_phase) * num_group ! v_ik
420 num_real_data = num_real_data &
421 + 5*num_phase_spec(i_unifac_phase) ! r_i, q_i, l_i, MW_i, X_i
422 num_env_data = num_env_data &
423 + num_phase_spec(i_unifac_phase) * num_group ! ln_GAMMA_ik
424 end do
425
426 ! Set phase dimensions
427 do i_unifac_phase = 1, num_unique_phase_
428 num_spec_(i_unifac_phase) = num_phase_spec(i_unifac_phase)
429 num_phase_instance_(i_unifac_phase) = num_phase_inst(i_unifac_phase)
430 end do
431
432 ! Set the main group names
433 allocate(main_group_names(main_groups%size()))
434 call main_groups%iter_reset()
435 do i_main_group = 1, main_groups%size()
436
437 ! Save the main group name
438 call assert(296339642, &
439 main_groups%get_key(main_group_names(i_main_group)%string))
440
441 call main_groups%iter_next()
442 end do
443
444 ! Set the main group interaction parameter matrix
445 allocate(main_group_interactions(main_groups%size(), main_groups%size()))
446 main_group_interactions(:,:) = real(0.0, kind=dp)
447 call main_groups%iter_reset()
448 do i_main_group = 1, main_groups%size()
449
450 ! Get the main group properties
451 call assert_msg(577361652, main_groups%get_property_t(val = main_group), &
452 "Invalid main group '"//main_group_names(i_main_group)%string// &
453 "' in UNIFAC model.")
454
455 ! Get the interactions
456 key_name = "interactions with"
457 call assert_msg(208272126, &
458 main_group%get_property_t(key_name, interactions), &
459 "Missing interactions for main group '"// &
460 main_group_names(i_main_group)%string//"' in UNIFAC model.")
461
462 ! Set the interactions
463 call interactions%iter_reset()
464 do i_inter = 1, interactions%size()
465
466 ! Get the interacting group name
467 call assert(363540699, interactions%get_key(inter_group_name))
468
469 ! Get the interaction parameter
470 call assert_msg(976253437, interactions%get_real(val = inter_param), &
471 "Invalid interaction parameter for interaction between "// &
472 "main groups '"//main_group_names(i_main_group)%string// &
473 "' and '"//trim(inter_group_name)//"' in UNIFAC model.")
474
475 ! Set the interaction parameter
476 do i_inter_group = 1, size(main_group_names)
477 found = .false.
478 if (main_group_names(i_inter_group)%string .eq. &
479 inter_group_name) then
480 main_group_interactions(i_main_group, i_inter_group) = inter_param
481 found = .true.
482 exit
483 end if
484 end do
485 call assert_msg(898262240, found, "Bad main group name '"// &
486 inter_group_name//"' in interactions of '"// &
487 main_group_names(i_main_group)%string//"' in UNIFAC model.")
488
489 call interactions%iter_next()
490 end do
491
492 call main_groups%iter_next()
493 end do
494
495 ! Set the functional group parameters
496 allocate(group_names(num_group_))
497 allocate(main_group_id(num_group_))
498 call func_groups%iter_reset()
499 do i_group = 1, num_group_
500
501 ! Save the group name
502 call assert(803878279, func_groups%get_key(group_names(i_group)%string))
503
504 ! Get the functional group
505 call assert_msg(657972204, func_groups%get_property_t(val = func_group), &
506 "Invalid functional group '"//group_names(i_group)%string// &
507 "' in UNIFAC model.")
508
509 ! Set the group volume parameter (R_k) Eq. 6
510 key_name = "volume param"
511 call assert_msg(549012632, &
512 func_group%get_real(key_name, r_k_(i_group)), &
513 "Missing volume parameter in functional group '"// &
514 group_names(i_group)%string//"' in UNIFAC model.")
515
516 ! Set the group volume parameter (Q_k) Eq. 6
517 key_name = "surface param"
518 call assert_msg(127348854, &
519 func_group%get_real(key_name, q_k_(i_group)), &
520 "Missing surface parameter in functional group '"// &
521 group_names(i_group)%string//"' in UNIFAC model.")
522
523 ! Get the main group name
524 key_name = "main group"
525 call assert_msg(702688391, &
526 func_group%get_string(key_name, main_group_name), &
527 "Missing main group name in functional group '"// &
528 group_names(i_group)%string//"' in UNIFAC model.")
529
530 ! Set the main group id
531 do i_main_group = 1, num_main_group
532 found = .false.
533 if (main_group_names(i_main_group)%string.eq.main_group_name) then
534 main_group_id(i_group) = i_main_group
535 found = .true.
536 exit
537 end if
538 end do
539 call assert_msg(752356165, found, "Missing main group '"// &
540 main_group_name//"' needed by functional group '"// &
541 group_names(i_group)%string//"' in UNIFAC model.")
542
543 call func_groups%iter_next()
544 end do
545
546 ! Set the group interaction parameters
547 do m = 1, num_group_
548 do n = 1, num_group_
549 a_mn_(m,n) = main_group_interactions(main_group_id(m), &
550 main_group_id(n))
551 end do
552 end do
553
554 ! Set up parameters for each phase
555 do i_unifac_phase = 1, num_unique_phase_
556 phase_name = phase_names(i_unifac_phase)%string
557 i_phase = unique_phase_set_id(i_unifac_phase)
558
559 ! Set the properties for each species in the phase
560 curr_spec_id = 0
561 phase_ids_set = .false.
562 spec_names = aero_phase_set(i_phase)%val%get_species_names()
563 do i_spec = 1, size(spec_names)
564
565 ! Get the species properties
566 call assert(698678581, &
567 chem_spec_data%get_property_set( &
568 spec_names(i_spec)%string, spec_props))
569
570 ! Check if this is a UNIFAC species, and get its groups
571 key_name = "UNIFAC groups"
572 if (spec_props%get_property_t(key_name, spec_groups)) then
573 curr_spec_id = curr_spec_id + 1
574
575 ! Get the molecular weight
576 key_name = "molecular weight [kg mol-1]"
577 call assert_msg(421151319, &
578 spec_props%get_real(key_name, &
579 mw_i_(i_unifac_phase, curr_spec_id)), &
580 "Missing molecular weight for UNIFAC species '"// &
581 spec_names(i_spec)%string//"'")
582
583 ! Check the number of UNIFAC groups
584 call assert_msg(511238330, spec_groups%size().gt.0, &
585 "Received empty set of UNIFAC groups for species '"// &
586 spec_names(i_spec)%string//"'")
587
588 ! Initialize the number of groups for this species
589 do i_group = 1, size(group_names)
590 v_ik_(i_unifac_phase, curr_spec_id, i_group) = 0
591 end do
592
593 ! Set the functional groups (v_ik) Eq. 6
594 ! and the r_i, q_i, and l_i parameters
595 call spec_groups%iter_reset()
596 do i_spec_group = 1, spec_groups%size()
597
598 ! Get the group name
599 call assert(649713038, spec_groups%get_key(spec_group_name))
600
601 ! Get the number of this group for this species
602 call assert_msg(429888360, spec_groups%get_int(val = num_spec_group), &
603 "Received non-integer number of UNIFAC groups for '"// &
604 spec_names(i_spec)%string//"'")
605
606 ! Locate the group in the set of functional groups
607 ! and set the v_ik parameter
608 found = .false.
609 do i_group = 1, size(group_names)
610 if (group_names(i_group)%string.eq.spec_group_name) then
611 found = .true.
612 v_ik_(i_unifac_phase, curr_spec_id,i_group) = num_spec_group
613 exit
614 end if
615 end do
616 call assert_msg(175022713, found, &
617 "Invalid UNIFAC functional group specified for '"// &
618 spec_names(i_spec)%string//"'")
619
620 ! Set the surface area (q_i) and volume (r_i) parameter for this
621 ! species
622 r_i = real(0.0, kind=dp)
623 q_i = real(0.0, kind=dp)
624 do i_group = 1, num_group_
625 r_i = r_i + r_k_(i_group) &
626 * real(v_ik_(i_unifac_phase ,curr_spec_id, i_group), &
627 kind=dp)
628 q_i = q_i + q_k_(i_group) &
629 * real(v_ik_(i_unifac_phase, curr_spec_id, i_group), &
630 kind=dp)
631 end do
632 r_i_(i_unifac_phase, curr_spec_id) = r_i
633 q_i_(i_unifac_phase, curr_spec_id) = q_i
634
635 ! Set the l_i parameter for this species (Eq. 5)
636 l_i_(i_unifac_phase, curr_spec_id) = 5.0d0 &
637 * ( r_i - q_i ) - ( r_i - 1.0d0 )
638
639 call spec_groups%iter_next()
640 end do
641
642 ! Get the state ids of this species relative to the phase id
643 ! If the phase ids have not been set, use this species to set them
644 if (.not.phase_ids_set) then
645 curr_phase_inst_id = 0
646 do i_rep = 1, size(aero_rep_set)
647 unique_names = aero_rep_set(i_rep)%val%unique_names( &
648 phase_name = phase_name, &
649 spec_name = spec_names(i_spec)%string )
650 do i_instance = 1, size(unique_names)
651 curr_phase_inst_id = curr_phase_inst_id + 1
652 phase_inst_id_(i_unifac_phase, curr_phase_inst_id) = &
653 aero_rep_set(i_rep)%val%spec_state_id( &
654 unique_names(i_instance)%string)
655 end do
656 end do
657 spec_id_(i_unifac_phase, curr_spec_id) = 0
658 phase_ids_set = .true.
659 else
660 do i_rep = 1, size(aero_rep_set)
661 unique_names = aero_rep_set(i_rep)%val%unique_names( &
662 phase_name = phase_name, &
663 spec_name = spec_names(i_spec)%string )
664 if (size(unique_names).gt.0) then
665 spec_id_(i_unifac_phase, curr_spec_id) = &
666 aero_rep_set(i_rep)%val%spec_state_id( &
667 unique_names(1)%string) - &
668 phase_inst_id_(i_unifac_phase, 1)
669 exit
670 end if
671 end do
672 end if
673
674 ! Set the activity coefficient ids
675 key_name = "chemical species"
676 found = .false.
677 do j_spec = 1, size(spec_names)
678 call assert(739328266, chem_spec_data%get_type( &
679 spec_names(j_spec)%string, spec_type))
680 if (spec_type.ne.chem_spec_activity_coeff) cycle
681 call assert(190005517, chem_spec_data%get_property_set( &
682 spec_names(j_spec)%string, spec_props))
683 if (spec_props%get_string(key_name, spec_name)) then
684 if (spec_names(i_spec)%string.eq.spec_name) then
685 do i_rep = 1, size(aero_rep_set)
686 unique_names = aero_rep_set(i_rep)%val%unique_names( &
687 phase_name = phase_name, &
688 spec_name = spec_names(j_spec)%string )
689 if (size(unique_names).eq.0) continue
690 gamma_id_(i_unifac_phase, curr_spec_id) = &
691 aero_rep_set(i_rep)%val%spec_state_id( &
692 unique_names(1)%string) - &
693 phase_inst_id_(i_unifac_phase, 1)
694 found = .true.
695 exit
696 end do
697 end if
698 end if
699 if (found) exit
700 end do
701 call assert_msg(202726788, found, &
702 "Missing activity coefficient for "//trim( &
703 spec_names(i_spec)%string))
704 end if
705 end do
706 end do
707
708 ! Clean up
709 deallocate(num_phase_inst)
710 deallocate(num_phase_spec)
711 deallocate(unique_phase_set_id)
712 deallocate(phase_names)
713 deallocate(main_group_names)
714 deallocate(main_group_interactions)
715 deallocate(group_names)
716
717 end subroutine initialize
718
719!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
720
721 !> Return a real number representing the priority of the sub model
722 !! calculations. Low priority sub models may use the results of higher
723 !! priority sub models. Lower numbers indicate higher priority.
724 !!
725 !! UNIFAC calculations may depend on water concentrations, so must be
726 !! lower priority than the ZSR sub model.
727 function priority(this)
728
729 !> Sub model priority
730 real(kind=dp) :: priority
731 !> Sub model data
732 class(sub_model_unifac_t), intent(in) :: this
733
734 priority = 2.0;
735
736 end function priority
737
738!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
739
740 !> Finalize the sub-model
741 elemental subroutine finalize(this)
742
743 !> Sub-model data
744 type(sub_model_unifac_t), intent(inout) :: this
745
746 if (associated(this%property_set)) &
747 deallocate(this%property_set)
748 if (allocated(this%model_name)) &
749 deallocate(this%model_name)
750 if (allocated(this%condensed_data_real)) &
751 deallocate(this%condensed_data_real)
752 if (allocated(this%condensed_data_int)) &
753 deallocate(this%condensed_data_int)
754
755 end subroutine finalize
756
757!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
758
759end module camp_sub_model_unifac
Initialize the aerosol representation data, validating component data and loading any required inform...
Get the non-unique name of a chemical species by its unique name.
Get a list of unique names for each element on the camp_camp_state::camp_state_t::state_var array for...
Return a real number representing the priority of the sub model calculations. Low priority sub models...
Interface for to_string functions.
Definition util.F90:32
The abstract aero_phase_data_t structure and associated subroutines.
elemental subroutine finalize(this)
Finalize the aerosol phase data.
type(aero_phase_data_t) function, pointer constructor(phase_name, init_size)
Constructor for aero_phase_data_t.
The abstract aero_rep_data_t structure and associated subroutines.
The camp_state_t structure and associated subroutines.
Definition camp_state.F90:9
The chem_spec_data_t structure and associated subroutines.
integer(kind=i_kind), parameter, public chem_spec_activity_coeff
The property_t structure and associated subroutines.
Definition property.F90:9
The abstract sub_model_data_t structure and associated subroutines.
The sub_model_UNIFAC_t type and assocatiated subroutines.
Common utility subroutines.
Definition util.F90:9
subroutine assert(code, condition_ok)
Errors unless condition_ok is true.
Definition util.F90:165
subroutine die_msg(code, error_msg)
Error immediately.
Definition util.F90:196
subroutine assert_msg(code, condition_ok, error_msg)
Errors unless condition_ok is true.
Definition util.F90:130
Pointer type for building arrays.
Pointer to aero_rep_data_t extending types.
Abstract sub-model data type.
UNIFAC activity coefficient calculation.
String type for building arrays of string of various size.
Definition util.F90:38