CAMP 1.0.0
Chemistry Across Multiple Phases
camp_core.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_camp_core module.
7
8!> \mainpage CAMP Documentation
9!!
10!! \ref index "CAMP" is software for
11!! solving multi-phase chemistry in atmospheric models. For a overview of what
12!! \ref index "CAMP" can do, check out
13!! \ref camp_tutorial_part_0 "part 0 of the CAMP tutorial".
14!! A description of the CAMP model elements and how to use them follows.
15!!
16!! # CAMP Input Classes #
17!!
18!! - \subpage camp_aero_phase "Aerosol Phases"
19!! - \subpage camp_aero_rep "Aerosol Representations"
20!! - \subpage camp_species "Chemical Species"
21!! - \subpage camp_mechanism "Mechanisms"
22!! - \subpage camp_rxn "Reactions"
23!! - \subpage camp_sub_model "Sub-Models"
24!!
25!! # Usage #
26!!
27!! \ref index "CAMP" uses
28!! json input files to load \ref input_format_species "chemical species",
29!! \ref input_format_mechanism "mechanisms", \ref input_format_aero_phase
30!! "aerosol phases", \ref input_format_aero_rep "aerosol representations",
31!! and \ref input_format_sub_model "sub-models" at runtime. How to use
32!! \ref index "CAMP" in a new or existing model is described in the
33!! \ref camp_tutorial "Boot CAMP" tutorial.
34!!
35!! ## Compiling ##
36!!
37!! You will need to have c and Fortran compilers along with CMake to build
38!! the CAMP library. In addition, CAMP has the following dependencies:
39!!
40!! | Library | Version | Source |
41!! |--------------|---------|-----------------------------------------------|
42!! | SUNDIALS | custom | camp/cvode-3.4-alpha.tar.gz |
43!! | SuiteSparse | 5.1.0 | http://faculty.cse.tamu.edu/davis/SuiteSparse/SuiteSparse-5.1.0.tar.gz |
44!! | GSL | | https://www.gnu.org/software/gsl/ |
45!! | json-fortran | 6.1.0 | https://github.com/jacobwilliams/json-fortran/archive/6.1.0.tar.gz |
46!!
47!! The SUNDIALS library must be built with the `ENABLE_KLU` flag set to `ON`
48!! and the KLU library and include paths set according to the SuiteSparse
49!! installation.
50!!
51!! To install CAMP locally, from the `camp/` folder:
52!!
53!! \code{.sh}
54!! mkdir build
55!! cd build
56!! cmake ..
57!! make install
58!! \endcode
59!!
60!! You can also check out
61!! \ref ./Dockerfile
62!! (or \ref ./Dockerfile.mpi for MPI applications) to see how CAMP
63!! is built for automated testing.
64!!
65!!## Input files ##
66!!
67!! \ref index "CAMP" uses two types of input files:
68!!
69!! - \subpage input_format_camp_file_list "File List" A \c json file
70!! containing a list of \ref index "CAMP" configuration
71!! file names.
72!! - \subpage input_format_camp_config "Configuration File" One or more
73!! \c json files containing all the \ref index "CAMP"
74!! configuration data.
75!!
76!! To initialize \ref index "CAMP" , the path to the
77!! \ref input_format_camp_file_list "file list" must be passed to the
78!! \ref camp_camp_core::camp_core_t constructor. The method by which this is
79!! done depends on the host model configuration.
80!!
81!! ## CAMP tutorial ##
82!!
83!! Follow the \ref camp_tutorial "Boot CAMP" tutorial to see how to
84!! integrate CAMP into your favorite model!
85
86
87!> The camp_core_t structure and associated subroutines.
89
90#ifdef CAMP_USE_JSON
91 use json_module
92#endif
93#ifdef CAMP_USE_MPI
94 use mpi
95#endif
100 use camp_constants, only : i_kind, dp
103 use camp_mpi
106 use camp_rxn_data
112 use camp_util, only : die_msg, string_t
113
114 implicit none
115 private
116
117 public :: camp_core_t
118
119 !> Part-MC model data
120 !!
121 !! Contains all time-invariant data for a Part-MC model run.
123 private
124 !> Chemical mechanisms
125 !! FIXME set up an iterator for external modules to use and
126 !! make all data members private
127 type(mechanism_data_ptr), pointer, public :: mechanism(:) => null()
128 !> Chemical species data
129 type(chem_spec_data_t), pointer :: chem_spec_data => null()
130 !> Sub models
131 type(sub_model_data_ptr), pointer :: sub_model(:) => null()
132 !> Aerosol representations
133 type(aero_rep_data_ptr), pointer, public :: aero_rep(:) => null()
134 !> Aerosol phases
135 type(aero_phase_data_ptr), pointer :: aero_phase(:) => null()
136 !> Size of the state array per grid cell
137 integer(kind=i_kind) :: size_state_per_cell
138 !> Number of cells to compute
139 integer(kind=i_kind) :: n_cells = 1
140 !> Initial state values
141 real(kind=dp), allocatable :: init_state(:)
142 !> Flag to split gas- and aerosol-phase reactions
143 !! (for large aerosol representations, like single-particle)
144 logical :: split_gas_aero = .false.
145 !> Relative integration tolerance
146 real(kind=dp) :: rel_tol = 0.0
147 ! Absolute integration tolerances
148 ! (Values for non-solver species will be ignored)
149 real(kind=dp), allocatable :: abs_tol(:)
150 ! Variable types
151 integer(kind=i_kind), allocatable :: var_type(:)
152 !> Solver data (gas-phase reactions)
153 type(camp_solver_data_t), pointer, public :: solver_data_gas => null()
154 !> Solver data (aerosol-phase reactions)
155 type(camp_solver_data_t), pointer, public :: solver_data_aero => null()
156 !> Solver data (mixed gas- and aerosol-phase reactions)
157 type(camp_solver_data_t), pointer, public :: solver_data_gas_aero => null()
158 !> Flag indicating the model data has been initialized
159 logical :: core_is_initialized = .false.
160 !> Flag indicating the solver has been initialized
161 logical :: solver_is_initialized = .false.
162 contains
163 !> Load a set of configuration files
164 procedure :: load_files
165 !> Load model data from a configuration file
166 procedure :: load
167 !> Initialize the model
168 procedure :: initialize
169 !> Indicate whether the core has been initialized
170 procedure :: is_initialized
171 !> Indicate whether the solver has been initialized
173 !> Get a pointer to an aerosol phase by name
174 procedure :: get_aero_phase
175 !> Get a pointer to an aerosol representation by name
176 procedure :: get_aero_rep
177 !> Get a pointer to the set of chemical species
179 !> Get a pointer to a mechanism by name
180 procedure :: get_mechanism
181 !> Get a pointer to a sub-model by name
182 procedure :: get_sub_model
183 !> Get the relative tolerance for the solver
184 procedure :: get_rel_tol
185 !> Get the absolute tolerance for a species on the state array
186 procedure :: get_abs_tol
187 !> Get a new model state variable
191 !> Get the size of the state array
192 procedure :: state_size
193 !> Get the size of the state array per grid cell
195 !> Get an array of unique names for all species on the state array
196 procedure :: unique_names
197 !> Get the index of a species on the state array by its unique name
198 procedure :: spec_state_id
199 !> Initialize the solver
200 procedure :: solver_initialize
201 !> Free the solver
202 procedure :: free_solver
203 !> Initialize an update_data object
205 procedure, private :: initialize_rxn_update_object
207 generic :: initialize_update_object => &
211 !> Update model data
212 procedure, private :: aero_rep_update_data
213 procedure, private :: rxn_update_data
214 procedure, private :: sub_model_update_data
215 generic :: update_data => &
219 !> Run the chemical mechanisms
220 procedure :: solve
221 !> Determine the number of bytes required to pack the variable
222 procedure :: pack_size
223 !> Pack the given variable into a buffer, advancing position
224 procedure :: bin_pack
225 !> Unpack the given variable from a buffer, advancing position
226 procedure :: bin_unpack
227 !> Print the core data
228 procedure :: print => do_print
229 !> Finalize the core
230 final :: finalize
231
232 ! Private functions
233 !> Add an aerosol phase to the model
234 procedure, private :: add_aero_phase
235 !> Add an aerosol representation to the model
236 procedure, private :: add_aero_rep
237 !> Add a mechanism to the model
238 procedure, private :: add_mechanism
239 !> Add a sub-model to the model
240 procedure, private :: add_sub_model
241 end type camp_core_t
242
243 !> Constructor for camp_core_t
244 interface camp_core_t
245 procedure :: constructor
246 end interface camp_core_t
247
248contains
249
250!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
251
252 !> Constructor for camp_core_t
253 function constructor(input_file_path, n_cells) result(new_obj)
254
255 !> A new set of model parameters
256 type(camp_core_t), pointer :: new_obj
257 !> Part-MC input file paths
258 character(len=*), intent(in), optional :: input_file_path
259 !> Num cells to compute simulatenously
260 integer(kind=i_kind), optional :: n_cells
261
262 allocate(new_obj)
263 allocate(new_obj%mechanism(0))
264 new_obj%chem_spec_data => chem_spec_data_t()
265 allocate(new_obj%aero_phase(0))
266 allocate(new_obj%aero_rep(0))
267 allocate(new_obj%sub_model(0))
268
269 if (present(n_cells)) then
270 new_obj%n_cells=n_cells
271 end if
272
273 if (present(input_file_path)) then
274 call new_obj%load_files(trim(input_file_path))
275 end if
276
277 end function constructor
278
279!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
280
281 !> \page input_format_camp_file_list Input File Format: CAMP-Chem Configuration File List
282 !!
283 !! A list of files containing configuration data for the \ref index
284 !! "CAMP". The file should be in \c json format
285 !! and the general structure should be the following:
286 !! \code{.json}
287 !! { "camp-files" : [
288 !! "file_one.json",
289 !! "some_dir/file_two.json",
290 !! ...
291 !! ]}
292 !! \endcode
293 !! The file should contain a single key-value pair named \b camp-files whose
294 !! value is an array of \b strings with paths to the set of \ref
295 !! input_format_camp_config "configuration" files to load. Input files
296 !! should be in \c json format.
297
298!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
299
300 !> Load a set of model data files.
301 !!
302 !! See \ref input_format_camp_file_list for the input file format.
303 subroutine load_files(this, input_file_path)
304
305 !> Model data
306 class(camp_core_t), intent(inout) :: this
307 !> Part-MC input file paths
308 character(len=*), intent(in) :: input_file_path
309
310#ifdef CAMP_USE_JSON
311 type(json_core), target :: json
312 type(json_file) :: j_file
313 type(json_value), pointer :: j_obj, j_next
314
315 logical(kind=json_lk) :: found, valid
316 character(kind=json_ck, len=:), allocatable :: unicode_str_val
317 character(kind=json_ck, len=:), allocatable :: json_err_msg
318 integer(kind=json_ik) :: i_file, num_files
319 type(string_t), allocatable :: file_list(:)
320 logical :: file_exists
321
322 ! load the file containing the paths to the configuration files
323 call j_file%initialize()
324 call j_file%get_core(json)
325 call assert_msg(600888426, trim(input_file_path).ne."", &
326 "Received empty string for file path")
327 inquire( file=trim(input_file_path), exist=file_exists )
328 call assert_msg(433777575, file_exists, "Cannot find file: "//&
329 trim(input_file_path))
330 call j_file%load_file(filename = trim(input_file_path))
331
332 ! get the set of configuration file names
333 call j_file%get('camp-files(1)', j_obj, found)
334 call assert_msg(405149265, found, &
335 "Could not find camp-files object in input file: "// &
336 input_file_path)
337 call json%validate(j_obj, valid, json_err_msg)
338 if(.not.valid) then
339 call die_msg(959537834, "Bad JSON format in file '"// &
340 trim(input_file_path)//"': "//trim(json_err_msg))
341 end if
342 call j_file%info('camp-files', n_children = num_files)
343 call assert_msg(411804027, num_files.gt.0, &
344 "No file names were found in "//input_file_path)
345
346 ! allocate space for the configurtaion file names
347 allocate(file_list(num_files))
348
349 ! cycle through the list of file names, adding each to the list
350 j_next => null()
351 i_file = 1
352 do while (associated(j_obj))
353 call json%get(j_obj, unicode_str_val)
354 file_list(i_file)%string = unicode_str_val
355 i_file = i_file + 1
356 call json%get_next(j_obj, j_next)
357 j_obj => j_next
358 end do
359
360 ! free the json file
361 call j_file%destroy()
362
363 ! load all the configuration files
364 call this%load(file_list)
365
366#else
367 call warn_msg(171627969, "No support for input files.");
368#endif
369
370 end subroutine load_files
371
372!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
373
374 !> \page input_format_camp_config Input File Format: CAMP-Chem Configuration Data
375 !!
376 !! Configuration data for the
377 !! \ref index "CAMP". The files are in
378 !! \c json format and their general structure should be the following:
379 !! \code{.json}
380 !! { "camp-data" : [
381 !! {
382 !! "type" : "OBJECT_TYPE",
383 !! ...
384 !! },
385 !! {
386 !! "type" : "OBJECT_TYPE",
387 !! ...
388 !! },
389 !! ...
390 !! ]}
391 !! \endcode
392 !! Each input file should contain exactly one \c json object with a single
393 !! key-value pair \b camp-data whose value is an array of \c json objects.
394 !! Additional top-level key-value pairs will be ignored. Each of the \c json
395 !! objects in the \b camp-data array must contain a key-value pair \b type
396 !! whose value is a string referencing a valid PartMC object.
397 !!
398 !! The valid values for \b type are:
399 !!
400 !! - \subpage input_format_mechanism "MECHANISM"
401 !! - \subpage input_format_species "CHEM_SPEC"
402 !! - \subpage input_format_aero_phase "AERO_PHASE"
403 !! - \subpage input_format_aero_rep "AERO_REP_*"
404 !! - \subpage input_format_sub_model "SUB_MODEL_*"
405 !!
406 !! The arrangement of objects within the \b camp-data array and between input
407 !! files is arbitrary. Additionally, some objects, such as \ref
408 !! input_format_species "chemical species" and \ref input_format_mechanism
409 !! "mechanisms" may be split into multiple objects within the \b camp-data
410 !! array and/or between files, and will be combined based on their unique
411 !! name. This flexibility is provided so that the chemical mechanism data
412 !! can be organized in a way that makes sense to the designer of the
413 !! mechanism. For example, files could be split based on species source
414 !! (biogenic, fossil fuel, etc.) or based on properties (molecular weight,
415 !! density, etc.) or any combination of criteria. However, if a single
416 !! property of an object (e.g., the molecular weight of a chemical species)
417 !! is set in more than one location, this will cause an error.
418
419!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
420
421 !> Load model data from input files
422 !!
423 !! See \ref input_format_camp_config for the input file format.
424 subroutine load(this, input_file_path)
425
426 !> Model data
427 class(camp_core_t), intent(inout) :: this
428 !> Part-MC input file paths
429 type(string_t), allocatable, intent(in) :: input_file_path(:)
430
431 integer(kind=i_kind) :: i_file, cell
432#ifdef CAMP_USE_JSON
433 type(json_core), pointer :: json
434 type(json_file) :: j_file
435 type(json_value), pointer :: j_obj, j_next
436
437 logical(kind=json_lk) :: valid
438 character(kind=json_ck, len=:), allocatable :: unicode_str_val
439 character(kind=json_ck, len=:), allocatable :: json_err_msg
440 character(len=:), allocatable :: str_val
441 real(kind=json_rk) :: real_val
442 logical :: file_exists, found
443
444 ! mechansim
445 type(mechanism_data_t), pointer :: mech_ptr
446
447 ! sub models
448 type(sub_model_data_ptr), pointer :: new_sub_model(:)
449 type(sub_model_factory_t) :: sub_model_factory
450 type(sub_model_data_ptr) :: sub_model_ptr
451 class(sub_model_data_t), pointer :: existing_sub_model_ptr
452 logical :: sub_model_placed
453 integer(kind=i_kind) :: i_sub_model, j_sub_model
454
455 ! aerosol representations
456 type(aero_rep_data_ptr), pointer :: new_aero_rep(:)
457 type(aero_rep_factory_t) :: aero_rep_factory
458 type(aero_rep_data_ptr) :: aero_rep_ptr
459 class(aero_rep_data_t), pointer :: existing_aero_rep_ptr
460
461 ! aerosol phases
462 type(aero_phase_data_ptr), pointer :: new_aero_phase(:)
463 class(aero_phase_data_t), pointer :: aero_phase, existing_aero_phase
464
465 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
466 !!! cycle through the list of configuration files !!!
467 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
468 j_obj => null()
469 j_next => null()
470 allocate(json)
471 do i_file = 1, size(input_file_path)
472
473 ! load the configuration file
474 call j_file%initialize()
475 call j_file%get_core(json)
476 call assert_msg(366175417, allocated(input_file_path(i_file)%string), &
477 "Received non-allocated string for file path")
478 call assert_msg(936390222, trim(input_file_path(i_file)%string).ne."", &
479 "Received empty string for file path")
480 inquire( file=input_file_path(i_file)%string, exist=file_exists )
481 call assert_msg(910660557, file_exists, "Cannot file file: "// &
482 input_file_path(i_file)%string)
483 call j_file%load_file(filename = input_file_path(i_file)%string)
484
485 ! get the CAMP objects
486 call j_file%get('camp-data(1)', j_obj)
487 call json%validate(j_obj, valid, json_err_msg)
488 if (.not.valid) then
489 call die_msg(560270545, "Bad JSON format in file '"// &
490 trim(input_file_path(i_file)%string)//"': "// &
491 trim(json_err_msg))
492 end if
493 do while (associated(j_obj))
494
495 ! get the object type and load data into the appropriate CAMP
496 ! derived type
497 call json%get(j_obj, 'type', unicode_str_val, found)
498 call assert_msg(689470331, found, &
499 "Missing type in json input file "// &
500 input_file_path(i_file)%string)
501 str_val = unicode_str_val
502
503 !!!!!!!!!!!!!!!!!!!!!!!!
504 !!! load a mechanism !!!
505 !!!!!!!!!!!!!!!!!!!!!!!!
506 if (str_val.eq.'MECHANISM') then
507 call json%get(j_obj, 'name', unicode_str_val, found)
508 call assert_msg(822680732, found, &
509 "Missing mechanism name in file "// &
510 input_file_path(i_file)%string)
511 str_val = unicode_str_val
512
513 ! if a mechanism with the same name already exists, add data to it
514 ! otherwise, add a new mechanism
515 if (.not.this%get_mechanism(str_val, mech_ptr)) then
516 call this%add_mechanism(str_val)
517 call assert(105816325, this%get_mechanism(str_val, mech_ptr))
518 end if
519 call mech_ptr%load(json, j_obj)
520
521 ! load a chemical species
522 else if (str_val.eq.'CHEM_SPEC') then
523 call this%chem_spec_data%load(json, j_obj)
524
525 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
526 !!! load an aerosol representation !!!
527 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
528 else if (str_val(1:8).eq.'AERO_REP') then
529 aero_rep_ptr%val => aero_rep_factory%load(json, j_obj)
530 str_val = aero_rep_ptr%val%name()
531
532 ! if an aerosol representation with the same name already exists,
533 ! add data to it. otherwise, add a new aerosol representation
534 if (this%get_aero_rep(str_val, existing_aero_rep_ptr)) then
535 deallocate(aero_rep_ptr%val)
536 call existing_aero_rep_ptr%load(json, j_obj)
537 else
538 allocate(new_aero_rep(size(this%aero_rep)+1))
539 new_aero_rep(1:size(this%aero_rep)) = &
540 this%aero_rep(1:size(this%aero_rep))
541 new_aero_rep(size(new_aero_rep))%val => aero_rep_ptr%val
542 call this%aero_rep(:)%dereference()
543 deallocate(this%aero_rep)
544 this%aero_rep => new_aero_rep
545 call aero_rep_ptr%dereference()
546 end if
547
548 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
549 !!! load an aerosol phase !!!
550 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
551 else if (str_val.eq.'AERO_PHASE') then
552 aero_phase => aero_phase_data_t()
553 call aero_phase%load(json, j_obj)
554 str_val = aero_phase%name()
555
556 ! if an aerosol phase with the same name already exists, add data to
557 ! it. otherwise, add a new aerosol phase
558 if (this%get_aero_phase(str_val, existing_aero_phase)) then
559 deallocate(aero_phase)
560 call existing_aero_phase%load(json, j_obj)
561 else
562 allocate(new_aero_phase(size(this%aero_phase)+1))
563 new_aero_phase(1:size(this%aero_phase)) = &
564 this%aero_phase(1:size(this%aero_phase))
565 new_aero_phase(size(new_aero_phase))%val => aero_phase
566 call this%aero_phase(:)%dereference()
567 deallocate(this%aero_phase)
568 this%aero_phase => new_aero_phase
569 end if
570
571 !!!!!!!!!!!!!!!!!!!!!!!!
572 !!! load a sub-model !!!
573 !!!!!!!!!!!!!!!!!!!!!!!!
574 else if (str_val(1:9).eq.'SUB_MODEL') then
575 sub_model_ptr%val => sub_model_factory%load(json, j_obj)
576 str_val = sub_model_ptr%val%name()
577
578 ! if an sub-model with the same name already exists, add data to it.
579 ! otherwise, add a new sub-model
580 if (this%get_sub_model(str_val, existing_sub_model_ptr)) then
581 deallocate(sub_model_ptr%val)
582 call existing_sub_model_ptr%load(json, j_obj)
583 else
584 sub_model_placed = .false.
585 allocate(new_sub_model(size(this%sub_model)+1))
586 j_sub_model = 1
587 do i_sub_model = 1, size(this%sub_model)
588 if (.not.sub_model_placed .and. &
589 sub_model_ptr%val%priority() < &
590 this%sub_model(i_sub_model)%val%priority()) then
591 sub_model_placed = .true.
592 new_sub_model(j_sub_model)%val => sub_model_ptr%val
593 j_sub_model = j_sub_model + 1
594 end if
595 new_sub_model(j_sub_model) = &
596 this%sub_model(i_sub_model)
597 j_sub_model = j_sub_model + 1
598 end do
599 if (.not.sub_model_placed) then
600 new_sub_model(j_sub_model)%val => sub_model_ptr%val
601 end if
602 call this%sub_model(:)%dereference()
603 deallocate(this%sub_model)
604 this%sub_model => new_sub_model
605 call sub_model_ptr%dereference()
606 end if
607
608 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
609 !!! set the relative tolerance for the model !!!
610 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
611 else if (str_val.eq.'RELATIVE_TOLERANCE') then
612 call json%get(j_obj, 'value', real_val, found)
613 call assert_msg(761842352, found, &
614 "Missing value for relative tolerance")
615 call assert_msg(162564706, real_val.gt.0.0.and.real_val.lt.1.0, &
616 "Invalid relative tolerance: "// &
617 trim(to_string(real(real_val, kind=dp))))
618 this%rel_tol = real(real_val, kind=dp)
619
620 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
621 !!! set whether to solve gas and aerosol phases separately !!!
622 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
623 else if (str_val.eq.'SPLIT_GAS_AERO') then
624 this%split_gas_aero = .true.
625
626 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
627 !!! fail on invalid object type !!!
628 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
629 else
630 call die_msg(448039776, &
631 "Received invalid json input object type: "//str_val)
632 end if
633
634 ! get the next object
635 j_next => j_obj
636 call json%get_next(j_next, j_obj)
637 end do
638
639 ! reset the json objects
640 call j_file%destroy()
641 call json%destroy()
642 end do
643
644 ! free the json core
645 deallocate(json)
646#else
647 call warn_msg(350136328, "No support for input files.");
648#endif
649
650 end subroutine load
651
652!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
653
654 !> Initialize the model data
655 subroutine initialize(this)
656
657 use iso_c_binding
658
659 !> Model data
660 class(camp_core_t), target, intent(inout) :: this
661
662 ! Indices for iteration
663 integer(kind=i_kind) :: i_mech, i_phase, i_aero_rep, i_sub_model
664 integer(kind=i_kind) :: i_state_var, i_spec, i_cell
665
666 ! Variables for setting initial state values
667 class(aero_rep_data_t), pointer :: rep
668 integer(kind=i_kind) :: i_state_elem, i_name
669
670 ! Species name for looking up properties
671 character(len=:), allocatable :: spec_name
672 ! Gas-phase species names
673 type(string_t), allocatable :: gas_spec_names(:)
674 ! Aerosol species
675 type(string_t), allocatable :: unique_names(:)
676
677 ! make sure the core has not already been initialized
678 call assert_msg(157261665, .not.this%core_is_initialized, &
679 "Attempting to initialize a camp_core_t object twice.")
680
681 ! Initialize the species database
682 call this%chem_spec_data%initialize()
683
684 ! Get the next index on the state array after the gas-phase species
685 i_state_var = this%chem_spec_data%size(spec_phase=chem_spec_gas_phase) + 1
686
687 ! Initialize the aerosol phases
688 do i_phase = 1, size(this%aero_phase)
689 call assert(254948966, associated(this%aero_phase(i_phase)%val))
690 call this%aero_phase(i_phase)%val%initialize(this%chem_spec_data)
691 end do
692
693 ! Initialize the aerosol representations
694 do i_aero_rep = 1, size(this%aero_rep)
695 call assert(251590193, associated(this%aero_rep(i_aero_rep)%val))
696 call this%aero_rep(i_aero_rep)%val%initialize(this%aero_phase, &
697 i_state_var)
698 i_state_var = i_state_var + this%aero_rep(i_aero_rep)%val%size()
699 end do
700
701 ! Initialize the sub-models
702 do i_sub_model = 1, size(this%sub_model)
703 call assert(565644925, associated(this%sub_model(i_sub_model)%val))
704 call this%sub_model(i_sub_model)%val%initialize(this%aero_rep, &
705 this%aero_phase, this%chem_spec_data)
706 end do
707
708 ! Set the size of the state array per grid cell
709 this%size_state_per_cell = i_state_var - 1
710
711 ! Initialize the mechanisms
712 do i_mech = 1, size(this%mechanism)
713 call this%mechanism(i_mech)%val%initialize(this%chem_spec_data, &
714 this%aero_rep, this%n_cells)
715 end do
716
717 ! Allocate space for the variable types and absolute tolerances
718 allocate(this%abs_tol(this%size_state_per_cell))
719 allocate(this%var_type(this%size_state_per_cell))
720
721 ! Start at the first state array element
722 i_state_var = 0
723
724 ! Add gas-phase species variable types and absolute tolerances
725 gas_spec_names = &
726 this%chem_spec_data%get_spec_names(spec_phase = &
728 do i_spec = 1, size(gas_spec_names)
729 i_state_var = i_state_var + 1
730 call assert(716433999, &
731 this%chem_spec_data%get_abs_tol(gas_spec_names(i_spec)%string, &
732 this%abs_tol(i_state_var)))
733 call assert(888496437, &
734 this%chem_spec_data%get_type(gas_spec_names(i_spec)%string, &
735 this%var_type(i_state_var)))
736 end do
737
738 ! Loop through aerosol representations
739 do i_aero_rep = 1, size(this%aero_rep)
740 ! Set aerosol-phase species variable types and absolute tolerances
741 ! TODO Move this to the aerosol representations, so they have control
742 ! of their portion on the state array and what is stored there
743 call assert(666823548, associated(this%aero_rep(i_aero_rep)%val))
744 unique_names = this%aero_rep(i_aero_rep)%val%unique_names()
745 do i_spec = 1, this%aero_rep(i_aero_rep)%val%size()
746 i_state_var = i_state_var + 1
747 spec_name = this%aero_rep(i_aero_rep)%val%spec_name( &
748 unique_names(i_spec)%string)
749 call assert(709716453, &
750 this%chem_spec_data%get_abs_tol(spec_name, &
751 this%abs_tol(i_state_var)))
752 call assert(257084300, &
753 this%chem_spec_data%get_type(spec_name, &
754 this%var_type(i_state_var)))
755 end do
756 deallocate(unique_names)
757 end do
758
759 ! Make sure absolute tolerance and variable type arrays are completely
760 ! filled
761 call assert_msg(501609702, i_state_var.eq.this%size_state_per_cell, &
762 "Internal error. Filled "//trim(to_string(i_state_var))// &
763 " of "//trim(to_string(this%size_state_per_cell))// &
764 " elements of absolute tolerance and variable type arrays")
765
766 this%core_is_initialized = .true.
767
768 ! Set the initial state values
769 allocate(this%init_state(this%size_state_per_cell * this%n_cells))
770
771 ! Set species concentrations to zero
772 this%init_state(:) = 0.0
773
774 ! Set activity coefficients to 1.0
775 do i_cell = 0, this%n_cells - 1
776 do i_aero_rep = 1, size(this%aero_rep)
777
778 rep => this%aero_rep(i_aero_rep)%val
779
780 ! Get the ion pairs for which activity coefficients can be calculated
781 unique_names = rep%unique_names(tracer_type = chem_spec_activity_coeff)
782
783 ! Set the activity coefficients to 1.0 as default
784 do i_name = 1, size(unique_names)
785 i_state_elem = rep%spec_state_id(unique_names(i_name)%string)
786 this%init_state(i_state_elem + i_cell * this%size_state_per_cell) = &
787 real(1.0d0, kind=dp)
788 end do
789
790 deallocate(unique_names)
791
792 end do
793 end do
794
795 end subroutine initialize
796
797!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
798
799 !> Inidicate whether the core has been initialized
800 logical function is_initialized(this)
801
802 !> Model data
803 class(camp_core_t), intent(in) :: this
804
805 is_initialized = this%core_is_initialized
806
807 end function is_initialized
808
809!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
810
811 !> Inidicate whether the solver has been initialized
812 logical function is_solver_initialized(this)
813
814 !> Model data
815 class(camp_core_t), intent(in) :: this
816
817 is_solver_initialized = this%solver_is_initialized
818
819 end function is_solver_initialized
820
821!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
822
823 !> Get a pointer to an aerosol phase by name
824 logical function get_aero_phase(this, aero_phase_name, aero_phase) &
825 result(found)
826
827 !> Model data
828 class(camp_core_t), intent(in) :: this
829 !> Aerosol phase name to search for
830 character(len=*), intent(in) :: aero_phase_name
831 !> Pointer to the aerosol phase
832 class(aero_phase_data_t), pointer, intent(out) :: aero_phase
833
834 integer(kind=i_kind) :: i_aero_phase
835
836 found = .false.
837 aero_phase => null()
838 if (.not.associated(this%aero_phase)) return
839 do i_aero_phase = 1, size(this%aero_phase)
840 if (this%aero_phase(i_aero_phase)%val%name().eq.aero_phase_name) then
841 found = .true.
842 aero_phase => this%aero_phase(i_aero_phase)%val
843 return
844 end if
845 end do
846
847 end function get_aero_phase
848
849!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
850
851 !> Get a pointer to an aerosol representation by name
852 logical function get_aero_rep(this, aero_rep_name, aero_rep) result (found)
853
854 !> Model data
855 class(camp_core_t), intent(in) :: this
856 !> Aerosol representation name to search for
857 character(len=*), intent(in) :: aero_rep_name
858 !> Aerosol representation
859 class(aero_rep_data_t), pointer, intent(out) :: aero_rep
860
861 integer(kind=i_kind) :: i_aero_rep
862
863 found = .false.
864 aero_rep => null()
865 if (.not.associated(this%aero_rep)) return
866 do i_aero_rep = 1, size(this%aero_rep)
867 if (this%aero_rep(i_aero_rep)%val%name().eq.trim(aero_rep_name)) then
868 aero_rep => this%aero_rep(i_aero_rep)%val
869 found = .true.
870 return
871 end if
872 end do
873
874 end function get_aero_rep
875
876!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
877
878 !> Get a pointer to the chemical species data
879 logical function get_chem_spec_data(this, chem_spec_data) result (found)
880
881 !> Model data
882 class(camp_core_t), intent(in) :: this
883 !> Pointer to the chemical species data
884 type(chem_spec_data_t), pointer :: chem_spec_data
885
886 found = .false.
887 chem_spec_data => this%chem_spec_data
888 if (associated(chem_spec_data)) found = .true.
889
890 end function get_chem_spec_data
891
892!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
893
894 !> Get a pointer to a mechanism by name
895 logical function get_mechanism(this, mech_name, mechanism) result (found)
896
897 !> Model data
898 class(camp_core_t), intent(in) :: this
899 !> Mechanism name to search for
900 character(len=*), intent(in) :: mech_name
901 !> Pointer to the mechanism
902 type(mechanism_data_t), pointer, intent(out) :: mechanism
903
904 integer(kind=i_kind) :: i_mech
905
906 found = .false.
907 mechanism => null()
908 if (.not.associated(this%mechanism)) return
909 do i_mech = 1, size(this%mechanism)
910 if (this%mechanism(i_mech)%val%name().eq.mech_name) then
911 found = .true.
912 mechanism => this%mechanism(i_mech)%val
913 return
914 end if
915 end do
916
917 end function get_mechanism
918
919!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
920
921 !> Find an sub-model by name
922 logical function get_sub_model(this, sub_model_name, sub_model) &
923 result(found)
924
925 !> Model data
926 class(camp_core_t), intent(in) :: this
927 !> Sub model name to search for
928 character(len=*), intent(in) :: sub_model_name
929 !> Sub model
930 class(sub_model_data_t), pointer, intent(out) :: sub_model
931
932 integer(kind=i_kind) :: i_sub_model
933
934 found = .false.
935 sub_model => null()
936 if (.not.associated(this%sub_model)) return
937 do i_sub_model = 1, size(this%sub_model)
938 if (this%sub_model(i_sub_model)%val%name().eq.sub_model_name) then
939 sub_model => this%sub_model(i_sub_model)%val
940 found = .true.
941 return
942 end if
943 end do
944
945 end function get_sub_model
946
947!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
948
949 !> Get the relative tolerance for the solver
950 function get_rel_tol( this )
951
952 !> Relative tolerance
953 real(kind=dp) :: get_rel_tol
954 !> Model data
955 class(camp_core_t), intent(in) :: this
956
957 get_rel_tol = this%rel_tol
958
959 end function get_rel_tol
960
961!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
962
963 !> Get the absolute tolerance for a species on the state array
964 function get_abs_tol( this, spec_id )
965
966 !> Absolute tolerance
967 real(kind=dp) :: get_abs_tol
968 !> Model data
969 class(camp_core_t), intent(in) :: this
970 !> Species id
971 integer(kind=i_kind), intent(in) :: spec_id
972
973 call assert_msg( 374310824, spec_id .ge. 1 .and. &
974 spec_id .le. size( this%abs_tol ), &
975 "Species id out of bounds: "// &
976 trim( to_string( spec_id ) ) )
977 get_abs_tol = this%abs_tol( spec_id )
978
979 end function get_abs_tol
980
981!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
982
983 !> Get a model state variable based on the this set of model data
984 function new_state_multi_cell(this, env_states) result (new_state)
985
986 !> New model state
987 type(camp_state_t), pointer :: new_state
988 !> Chemical model
989 class(camp_core_t), intent(in) :: this
990 !> Environmental state array
991 !! (one element per grid cell to solve simultaneously)
992 type(env_state_ptr), target, intent(in) :: env_states(:)
993
994 ! Initialize camp_state
995 new_state => camp_state_t(this%n_cells, env_states)
996
997 ! Set up the state variable array
998 allocate(new_state%state_var, source=this%init_state)
999
1000 end function new_state_multi_cell
1001
1002!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1003
1004 !> Get a model state variable based on the this set of model data
1005 !! This is also called for multi-cell systems when no env_state_t array
1006 !! is passed.
1007 function new_state_one_cell(this, env_state) result (new_state)
1008
1009 !> New model state
1010 type(camp_state_t), pointer :: new_state
1011 !> Chemical model
1012 class(camp_core_t), intent(in) :: this
1013 !> Environmental state array
1014 !! (one element per grid cell to solve simultaneously)
1015 type(env_state_t), optional, target, intent(in) :: env_state
1016
1017 ! Initialize camp_state
1018 if (this%n_cells.eq.1) then
1019 new_state => camp_state_t(env_state)
1020 else
1021 call assert_msg(386790682, .not.present(env_state), &
1022 "Cannot use a single env_state_t object to create "// &
1023 "a new camp_state_t in a multi-cell system")
1024 new_state => camp_state_t(this%n_cells)
1025 end if
1026
1027 ! Set up the state variable array
1028 allocate(new_state%state_var, source=this%init_state)
1029
1030 end function new_state_one_cell
1031
1032!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1033
1034 !> Get the size of the state array
1035 function state_size(this)
1036
1037 !> State size
1038 integer(kind=i_kind) :: state_size
1039 !> Chemical model
1040 class(camp_core_t), intent(in) :: this
1041
1042 call assert_msg(629102639, allocated(this%init_state), &
1043 "Trying to get the size of the state array before "// &
1044 "initializing the camp_core")
1045
1046 state_size = size(this%init_state)
1047
1048 end function state_size
1049
1050!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1051
1052 !> Get the size of the state array for each grid cell
1053 function state_size_per_cell(this) result(state_size)
1054
1055 !> State size
1056 integer(kind=i_kind) :: state_size
1057 !> Chemical model
1058 class(camp_core_t), intent(in) :: this
1059
1060 call assert_msg(175845182, allocated(this%init_state), &
1061 "Trying to get the size of the state array before "// &
1062 "initializing the camp_core")
1063
1064 state_size = this%size_state_per_cell
1065
1066 end function state_size_per_cell
1067
1068!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1069
1070 !> Get an array of unique names for all species on the state array
1071 !!
1072 !! The order of this array is the same as the state array for one grid cell
1073 function unique_names( this )
1074
1075 !> Array of unique species names
1076 type(string_t), allocatable :: unique_names(:)
1077 !> CAMP core
1078 class(camp_core_t), intent(in) :: this
1079
1080 integer(kind=i_kind) :: i_aero_rep
1081 type(string_t), allocatable :: new_names(:), temp_list(:)
1082
1083 unique_names = this%chem_spec_data%get_spec_names( &
1084 spec_phase = chem_spec_gas_phase )
1085
1086 if( .not. associated( this%aero_rep ) ) return
1087 do i_aero_rep = 1, size( this%aero_rep )
1088 new_names = this%aero_rep( i_aero_rep )%val%unique_names( )
1089 if( .not. allocated( new_names ) ) cycle
1090 if( size( new_names ).eq.0 ) cycle
1091 allocate( temp_list( size( unique_names ) + size( new_names ) ) )
1092 temp_list( 1 : size( unique_names ) ) = unique_names( : )
1093 temp_list( size( unique_names ) + 1 : size( temp_list ) ) = &
1094 new_names( : )
1095 deallocate( unique_names )
1096 allocate( unique_names, source = temp_list )
1097 end do
1098 if( allocated( new_names ) ) deallocate( new_names )
1099 if( allocated( temp_list ) ) deallocate( temp_list )
1100
1101 end function unique_names
1102
1103!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1104
1105 !> Get the id of a species on the state array by its unique name
1106 function spec_state_id( this, spec_name, state_id ) result( found )
1107
1108 !> Flag indicating whether the species was found
1109 logical :: found
1110 !> CAMP core
1111 class(camp_core_t), intent(in) :: this
1112 !> Species unique name
1113 character(len=*), intent(in) :: spec_name
1114 !> Species state id
1115 integer(kind=i_kind), intent(inout) :: state_id
1116
1117 integer(kind=i_kind) :: i_spec, i_aero_rep
1118
1119 found = .false.
1120 i_spec = this%chem_spec_data%gas_state_id( spec_name )
1121 do i_aero_rep = 1, size( this%aero_rep )
1122 if( i_spec .eq. 0 ) &
1123 i_spec = this%aero_rep( i_aero_rep )%val%spec_state_id( spec_name )
1124 end do
1125
1126 if( i_spec .gt. 0 ) then
1127 state_id = i_spec
1128 found = .true.
1129 end if
1130
1131 end function spec_state_id
1132
1133!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1134
1135 !> Initialize the solver
1136 subroutine solver_initialize(this)
1137
1138 !> Chemical model
1139 class(camp_core_t), intent(inout) :: this
1140
1141 call assert_msg(662920365, .not.this%solver_is_initialized, &
1142 "Attempting to initialize the solver twice.")
1143
1144 ! Set up either two solvers (gas and aerosol) or one solver (combined)
1145 if (this%split_gas_aero) then
1146
1147 ! Create the new solver data objects
1148 this%solver_data_gas => camp_solver_data_t()
1149 this%solver_data_aero => camp_solver_data_t()
1150
1151 ! Set custom relative integration tolerance, if present
1152 if (this%rel_tol.ne.real(0.0, kind=dp)) then
1153 this%solver_data_gas%rel_tol = this%rel_tol
1154 this%solver_data_aero%rel_tol = this%rel_tol
1155 end if
1156
1157 ! Initialize the solvers
1158 call this%solver_data_gas%initialize( &
1159 this%var_type, & ! State array variable types
1160 this%abs_tol, & ! Absolute tolerances for each state var
1161 this%mechanism, & ! Pointer to the mechanisms
1162 this%aero_phase, & ! Pointer to the aerosol phases
1163 this%aero_rep, & ! Pointer to the aerosol representations
1164 this%sub_model, & ! Pointer to the sub-models
1165 gas_rxn, & ! Reaction phase
1166 this%n_cells & ! # of cells computed simultaneosly
1167 )
1168 call this%solver_data_aero%initialize( &
1169 this%var_type, & ! State array variable types
1170 this%abs_tol, & ! Absolute tolerances for each state var
1171 this%mechanism, & ! Pointer to the mechanisms
1172 this%aero_phase, & ! Pointer to the aerosol phases
1173 this%aero_rep, & ! Pointer to the aerosol representations
1174 this%sub_model, & ! Pointer to the sub-models
1175 aero_rxn, & ! Reaction phase
1176 this%n_cells & ! # of cells computed simultaneosly
1177 )
1178 else
1179
1180 ! Create a new solver data object
1181 this%solver_data_gas_aero => camp_solver_data_t()
1182
1183 ! Set custom relative integration tolerance, if present
1184 if (this%rel_tol.ne.0.0) then
1185 this%solver_data_gas_aero%rel_tol = this%rel_tol
1186 end if
1187
1188 ! Initialize the solver
1189 call this%solver_data_gas_aero%initialize( &
1190 this%var_type, & ! State array variable types
1191 this%abs_tol, & ! Absolute tolerances for each state var
1192 this%mechanism, & ! Pointer to the mechanisms
1193 this%aero_phase, & ! Pointer to the aerosol phases
1194 this%aero_rep, & ! Pointer to the aerosol representations
1195 this%sub_model, & ! Pointer to the sub-models
1196 gas_aero_rxn, & ! Reaction phase
1197 this%n_cells & ! # of cells computed simultaneosly
1198 )
1199
1200 end if
1201
1202 this%solver_is_initialized = .true.
1203
1204 end subroutine solver_initialize
1205
1206!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1207
1208 !> Free the solver memory
1209 subroutine free_solver(this)
1210
1211 !> CAMP-core
1212 class(camp_core_t), intent(inout) :: this
1213
1214 if( associated( this%solver_data_gas ) ) &
1215 deallocate( this%solver_data_gas )
1216 if( associated( this%solver_data_aero ) ) &
1217 deallocate( this%solver_data_aero )
1218 if( associated( this%solver_data_gas_aero ) ) &
1219 deallocate( this%solver_data_gas_aero )
1220
1221 end subroutine free_solver
1222
1223!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1224
1225 !> Initialize an update data object for an aerosol representation
1226 subroutine initialize_aero_rep_update_object( this, aero_rep, update_data )
1227
1228 !> CAMP core
1229 class(camp_core_t), intent(in) :: this
1230 !> Aerosol representation to be updated
1231 class(aero_rep_data_t), intent(inout) :: aero_rep
1232 !> Update data object
1233 class(aero_rep_update_data_t), intent(out) :: update_data
1234
1235 type(aero_rep_factory_t) :: factory
1236
1237 call assert_msg( 962343826, .not. this%is_solver_initialized( ), &
1238 "Cannot initialize update data objects after the "// &
1239 "solver has been initialized." )
1240 call factory%initialize_update_data(aero_rep, update_data)
1241
1243
1244!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1245
1246 !> Initialize an update data object for a reaction
1247 subroutine initialize_rxn_update_object( this, rxn, update_data )
1248
1249 !> CAMP core
1250 class(camp_core_t), intent(in) :: this
1251 !> Reaction to be updated
1252 class(rxn_data_t), intent(inout) :: rxn
1253 !> Update data object
1254 class(rxn_update_data_t), intent(out) :: update_data
1255
1256 type(rxn_factory_t) :: factory
1257
1258 call assert_msg( 166064689, .not. this%is_solver_initialized( ), &
1259 "Cannot initialize update data objects after the "// &
1260 "solver has been initialized." )
1261 call factory%initialize_update_data(rxn, update_data)
1262
1263 end subroutine initialize_rxn_update_object
1264
1265!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1266
1267 !> Initialize an update data object for a sub model
1268 subroutine initialize_sub_model_update_object( this, sub_model, update_data )
1269
1270 !> CAMP core
1271 class(camp_core_t), intent(in) :: this
1272 !> Sub model to be updated
1273 class(sub_model_data_t), intent(inout) :: sub_model
1274 !> Update data object
1275 class(sub_model_update_data_t), intent(out) :: update_data
1276
1277 type(sub_model_factory_t) :: factory
1278
1279 call assert_msg( 771607586, .not. this%is_solver_initialized( ), &
1280 "Cannot initialize update data objects after the "// &
1281 "solver has been initialized." )
1282 call factory%initialize_update_data(sub_model, update_data)
1283
1285
1286!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1287
1288 !> Update data associated with an aerosol representation. This function
1289 !! should be called by an external aerosol microphysics model whenever
1290 !! the aerosol condensed data needs updated based on changes in, e.g.,
1291 !! particle size or number concentration. The update types are aerosol-
1292 !! representation specific.
1293 subroutine aero_rep_update_data(this, update_data)
1294
1295 !> Chemical model
1296 class(camp_core_t), intent(in) :: this
1297 !> Update data
1298 class(aero_rep_update_data_t), intent(in) :: update_data
1299
1300 if (associated(this%solver_data_gas)) &
1301 call this%solver_data_gas%update_aero_rep_data(update_data)
1302 if (associated(this%solver_data_aero)) &
1303 call this%solver_data_aero%update_aero_rep_data(update_data)
1304 if (associated(this%solver_data_gas_aero)) &
1305 call this%solver_data_gas_aero%update_aero_rep_data(update_data)
1306
1307 end subroutine aero_rep_update_data
1308
1309!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1310
1311 !> Update data associated with a reaction. This function should be called
1312 !! when reaction parameters need updated from the host model. For example,
1313 !! this function can be called to update photolysis rates from a host
1314 !! model's photolysis module.
1315 subroutine rxn_update_data(this, update_data)
1316
1317 !> Chemical model
1318 class(camp_core_t), intent(in) :: this
1319 !> Update data
1320 class(rxn_update_data_t), intent(in) :: update_data
1321
1322 if (associated(this%solver_data_gas)) &
1323 call this%solver_data_gas%update_rxn_data(update_data)
1324 if (associated(this%solver_data_aero)) &
1325 call this%solver_data_aero%update_rxn_data(update_data)
1326 if (associated(this%solver_data_gas_aero)) &
1327 call this%solver_data_gas_aero%update_rxn_data(update_data)
1328
1329 end subroutine rxn_update_data
1330
1331!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1332
1333 !> Update data associated with a sub-model. This function should be called
1334 !! when sub-model parameters need updated from the host model.
1335 subroutine sub_model_update_data(this, update_data)
1336
1337 !> Chemical model
1338 class(camp_core_t), intent(in) :: this
1339 !> Update data
1340 class(sub_model_update_data_t), intent(in) :: update_data
1341
1342 if (associated(this%solver_data_gas)) &
1343 call this%solver_data_gas%update_sub_model_data(update_data)
1344 if (associated(this%solver_data_aero)) &
1345 call this%solver_data_aero%update_sub_model_data(update_data)
1346 if (associated(this%solver_data_gas_aero)) &
1347 call this%solver_data_gas_aero%update_sub_model_data(update_data)
1348
1349 end subroutine sub_model_update_data
1350
1351!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1352
1353 !> Integrate the chemical mechanism
1354 subroutine solve(this, camp_state, time_step, rxn_phase, solver_stats)
1355
1356 use camp_rxn_data
1358 use iso_c_binding
1359
1360 !> Chemical model
1361 class(camp_core_t), intent(in) :: this
1362 !> Current model state
1363 type(camp_state_t), intent(inout), target :: camp_state
1364 !> Time step over which to integrate (s)
1365 real(kind=dp), intent(in) :: time_step
1366 !> Phase to solve - gas, aerosol, or both (default)
1367 !! Use parameters in camp_rxn_data to specify phase:
1368 !! GAS_RXN, AERO_RXN, GAS_AERO_RXN
1369 integer(kind=i_kind), intent(in), optional :: rxn_phase
1370 !> Return solver statistics to the host model
1371 type(solver_stats_t), intent(inout), optional, target :: solver_stats
1372
1373 ! Phase to solve
1374 integer(kind=i_kind) :: phase
1375 ! Pointer to solver data
1376 type(camp_solver_data_t), pointer :: solver
1377
1378 call assert_msg(593328365, this%solver_is_initialized, &
1379 "Trying to solve system with uninitialized solver" )
1380
1381 ! Get the phase(s) to solve for
1382 if (present(rxn_phase)) then
1383 phase = rxn_phase
1384 else
1385 phase = gas_aero_rxn
1386 end if
1387
1388 ! Update the solver array of environmental states
1389 call camp_state%update_env_state( )
1390
1391 ! Determine the solver to use
1392 if (phase.eq.gas_rxn) then
1393 solver => this%solver_data_gas
1394 else if (phase.eq.aero_rxn) then
1395 solver => this%solver_data_aero
1396 else if (phase.eq.gas_aero_rxn) then
1397 solver => this%solver_data_gas_aero
1398 else
1399 call die_msg(704896254, "Invalid rxn phase specified for chemistry "// &
1400 "solver: "//to_string(phase))
1401 end if
1402
1403 ! Make sure the requested solver was loaded
1404 call assert_msg(730097030, associated(solver), "Invalid solver requested")
1405
1406 ! Run the integration
1407 if (present(solver_stats)) then
1408 call solver%solve(camp_state, real(0.0, kind=dp), time_step, &
1409 solver_stats)
1410 else
1411 call solver%solve(camp_state, real(0.0, kind=dp), time_step)
1412 end if
1413
1414 end subroutine solve
1415
1416!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1417
1418 !> Determine the size of a binary required to pack the mechanism
1419 integer(kind=i_kind) function pack_size(this, comm)
1420
1421 !> Chemical model
1422 class(camp_core_t), intent(in) :: this
1423 !> MPI communicator
1424 integer, intent(in), optional :: comm
1425
1426 type(aero_rep_factory_t) :: aero_rep_factory
1427 type(sub_model_factory_t) :: sub_model_factory
1428 class(aero_rep_data_t), pointer :: aero_rep
1429 class(sub_model_data_t), pointer :: sub_model
1430 integer(kind=i_kind) :: i_mech, i_phase, i_rep, i_sub_model, l_comm
1431
1432#ifdef CAMP_USE_MPI
1433 if (present(comm)) then
1434 l_comm = comm
1435 else
1436 l_comm = mpi_comm_world
1437 endif
1438
1439 call assert_msg(143374295, this%core_is_initialized, &
1440 "Trying to get the buffer size of an uninitialized core.")
1441
1442 pack_size = camp_mpi_pack_size_integer(size(this%mechanism), l_comm) + &
1443 camp_mpi_pack_size_integer(size(this%aero_phase), l_comm) + &
1444 camp_mpi_pack_size_integer(size(this%aero_rep), l_comm) + &
1445 camp_mpi_pack_size_integer(size(this%sub_model), l_comm)
1446 do i_mech = 1, size(this%mechanism)
1447 pack_size = pack_size + this%mechanism(i_mech)%val%pack_size(l_comm)
1448 end do
1449 do i_phase = 1, size(this%aero_phase)
1450 pack_size = pack_size + this%aero_phase(i_phase)%val%pack_size(l_comm)
1451 end do
1452 do i_rep = 1, size(this%aero_rep)
1453 aero_rep => this%aero_rep(i_rep)%val
1454 pack_size = pack_size + aero_rep_factory%pack_size(aero_rep, l_comm)
1455 aero_rep => null()
1456 end do
1457 do i_sub_model = 1, size(this%sub_model)
1458 sub_model => this%sub_model(i_sub_model)%val
1459 pack_size = pack_size + sub_model_factory%pack_size(sub_model, l_comm)
1460 sub_model => null()
1461 end do
1462 pack_size = pack_size + &
1463 camp_mpi_pack_size_integer(this%size_state_per_cell, l_comm) + &
1464 camp_mpi_pack_size_integer(this%n_cells, l_comm) + &
1465 camp_mpi_pack_size_logical(this%split_gas_aero, l_comm) + &
1466 camp_mpi_pack_size_real(this%rel_tol, l_comm) + &
1467 camp_mpi_pack_size_real_array(this%abs_tol, l_comm) + &
1468 camp_mpi_pack_size_integer_array(this%var_type, l_comm) + &
1469 camp_mpi_pack_size_real_array(this%init_state, l_comm)
1470#else
1471 pack_size = 0
1472#endif
1473 end function pack_size
1474
1475!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1476
1477 !> Pack the given value to the buffer, advancing position
1478 subroutine bin_pack(this, buffer, pos, comm)
1479
1480 !> Chemical model
1481 class(camp_core_t), intent(in) :: this
1482 !> Memory buffer
1483 character, intent(inout) :: buffer(:)
1484 !> Current buffer position
1485 integer, intent(inout) :: pos
1486 !> MPI communicator
1487 integer, intent(in), optional :: comm
1488
1489#ifdef CAMP_USE_MPI
1490 type(aero_rep_factory_t) :: aero_rep_factory
1491 type(sub_model_factory_t) :: sub_model_factory
1492 class(aero_rep_data_t), pointer :: aero_rep
1493 class(sub_model_data_t), pointer :: sub_model
1494 integer(kind=i_kind) :: i_mech, i_phase, i_rep, i_sub_model, &
1495 prev_position, l_comm
1496
1497 if (present(comm)) then
1498 l_comm = comm
1499 else
1500 l_comm = mpi_comm_world
1501 endif
1502
1503 call assert_msg(143374295, this%core_is_initialized, &
1504 "Trying to pack an uninitialized core.")
1505
1506 prev_position = pos
1507 call camp_mpi_pack_integer(buffer, pos, size(this%mechanism), l_comm)
1508 call camp_mpi_pack_integer(buffer, pos, size(this%aero_phase), l_comm)
1509 call camp_mpi_pack_integer(buffer, pos, size(this%aero_rep), l_comm)
1510 call camp_mpi_pack_integer(buffer, pos, size(this%sub_model), l_comm)
1511 do i_mech = 1, size(this%mechanism)
1512 call this%mechanism(i_mech)%val%bin_pack(buffer, pos, l_comm)
1513 end do
1514 do i_phase = 1, size(this%aero_phase)
1515 call this%aero_phase(i_phase)%val%bin_pack(buffer, pos, l_comm)
1516 end do
1517 do i_rep = 1, size(this%aero_rep)
1518 aero_rep => this%aero_rep(i_rep)%val
1519 call aero_rep_factory%bin_pack(aero_rep, buffer, pos, l_comm)
1520 aero_rep => null()
1521 end do
1522 do i_sub_model = 1, size(this%sub_model)
1523 sub_model => this%sub_model(i_sub_model)%val
1524 call sub_model_factory%bin_pack(sub_model, buffer, pos, l_comm)
1525 sub_model => null()
1526 end do
1527 call camp_mpi_pack_integer(buffer, pos, this%size_state_per_cell, l_comm)
1528 call camp_mpi_pack_integer(buffer, pos, this%n_cells, l_comm)
1529 call camp_mpi_pack_logical(buffer, pos, this%split_gas_aero, l_comm)
1530 call camp_mpi_pack_real(buffer, pos, this%rel_tol, l_comm)
1531 call camp_mpi_pack_real_array(buffer, pos, this%abs_tol, l_comm)
1532 call camp_mpi_pack_integer_array(buffer, pos, this%var_type, l_comm)
1533 call camp_mpi_pack_real_array(buffer, pos, this%init_state, l_comm)
1534 call assert(184050835, &
1535 pos - prev_position <= this%pack_size(l_comm))
1536#endif
1537
1538 end subroutine bin_pack
1539
1540!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1541
1542 !> Unpack the given value from the buffer, advancing position
1543 subroutine bin_unpack(this, buffer, pos, comm)
1544
1545 !> Chemical model
1546 class(camp_core_t), intent(inout) :: this
1547 !> Memory buffer
1548 character, intent(inout) :: buffer(:)
1549 !> Current buffer position
1550 integer, intent(inout) :: pos
1551 !> MPI communicator
1552 integer, intent(in), optional :: comm
1553
1554#ifdef CAMP_USE_MPI
1555 type(aero_rep_factory_t) :: aero_rep_factory
1556 type(sub_model_factory_t) :: sub_model_factory
1557 integer(kind=i_kind) :: i_mech, i_phase, i_rep, i_sub_model, &
1558 prev_position, num_mech, num_phase, num_rep, num_sub_model, &
1559 l_comm
1560
1561 if (present(comm)) then
1562 l_comm = comm
1563 else
1564 l_comm = mpi_comm_world
1565 endif
1566
1567 call finalize(this)
1568 this%chem_spec_data => chem_spec_data_t()
1569
1570 prev_position = pos
1571 call camp_mpi_unpack_integer(buffer, pos, num_mech, l_comm)
1572 call camp_mpi_unpack_integer(buffer, pos, num_phase, l_comm)
1573 call camp_mpi_unpack_integer(buffer, pos, num_rep, l_comm)
1574 call camp_mpi_unpack_integer(buffer, pos, num_sub_model, l_comm)
1575 allocate(this%mechanism(num_mech))
1576 allocate(this%aero_phase(num_phase))
1577 allocate(this%aero_rep(num_rep))
1578 allocate(this%sub_model(num_sub_model))
1579 do i_mech = 1, num_mech
1580 this%mechanism(i_mech)%val => mechanism_data_t()
1581 call this%mechanism(i_mech)%val%bin_unpack(buffer, pos, l_comm)
1582 end do
1583 do i_phase = 1, num_phase
1584 this%aero_phase(i_phase)%val => aero_phase_data_t()
1585 call this%aero_phase(i_phase)%val%bin_unpack(buffer, pos, l_comm)
1586 end do
1587 do i_rep = 1, num_rep
1588 this%aero_rep(i_rep)%val => aero_rep_factory%bin_unpack(buffer, pos, l_comm)
1589 end do
1590 do i_sub_model = 1, num_sub_model
1591 this%sub_model(i_sub_model)%val => &
1592 sub_model_factory%bin_unpack(buffer, pos, l_comm)
1593 end do
1594 call camp_mpi_unpack_integer(buffer, pos, this%size_state_per_cell, l_comm)
1595 call camp_mpi_unpack_integer(buffer, pos, this%n_cells, l_comm)
1596 call camp_mpi_unpack_logical(buffer, pos, this%split_gas_aero, l_comm)
1597 call camp_mpi_unpack_real(buffer, pos, this%rel_tol, l_comm)
1598 call camp_mpi_unpack_real_array(buffer, pos, this%abs_tol, l_comm)
1599 call camp_mpi_unpack_integer_array(buffer, pos, this%var_type, l_comm)
1600 call camp_mpi_unpack_real_array(buffer, pos, this%init_state, l_comm)
1601 this%core_is_initialized = .true.
1602 call assert(291557168, &
1603 pos - prev_position <= this%pack_size(l_comm))
1604#endif
1605
1606 end subroutine bin_unpack
1607
1608!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1609
1610 !> Print the core data
1611 subroutine do_print(this, file_unit, solver_data_only)
1612
1613 !> Core data
1614 class(camp_core_t), intent(in) :: this
1615 !> File unit for output
1616 integer(kind=i_kind), intent(in), optional :: file_unit
1617 !> Print only the solver data (can be used during runtime for debugging)
1618 logical, intent(in), optional :: solver_data_only
1619
1620 integer(kind=i_kind) :: i_gas_spec, i_spec, j_spec, i_phase, i_aero_rep, i_mech
1621 integer(kind=i_kind) :: i_sub_model, i_solver_spec
1622 integer(kind=i_kind) :: f_unit
1623 type(string_t), allocatable :: state_names(:), rep_spec_names(:)
1624 logical :: sd_only
1625
1626 f_unit = 6
1627 sd_only = .false.
1628
1629 if (present(file_unit)) f_unit = file_unit
1630 if (present(solver_data_only)) sd_only = solver_data_only
1631
1632 write(f_unit,*) "*********************"
1633 write(f_unit,*) "** CAMP core data **"
1634 write(f_unit,*) "*********************"
1635 if (.not.sd_only ) then
1636 write(f_unit,*) "Number of grid cells to solve simultaneously: ", &
1637 this%n_cells
1638 write(f_unit,*) "Relative integration tolerance: ", this%rel_tol
1639 call this%chem_spec_data%print(f_unit)
1640 write(f_unit,*) "*** Aerosol Phases ***"
1641 do i_phase=1, size(this%aero_phase)
1642 call this%aero_phase(i_phase)%val%print(f_unit)
1643 end do
1644 write(f_unit,*) "*** Aerosol Representations ***"
1645 do i_aero_rep=1, size(this%aero_rep)
1646 write(f_unit,*) "Aerosol representation ", i_aero_rep
1647 call this%aero_rep(i_aero_rep)%val%print(f_unit)
1648 end do
1649 write(f_unit,*) "*** Sub Models ***"
1650 do i_sub_model=1, size(this%sub_model)
1651 write(f_unit,*) "Sub model: ", i_sub_model
1652 call this%sub_model(i_sub_model)%val%print(f_unit)
1653 end do
1654 write(f_unit,*) "*** Mechanisms ***"
1655 write(f_unit,*) "Number of mechanisms: ", size(this%mechanism)
1656 do i_mech=1, size(this%mechanism)
1657 call this%mechanism(i_mech)%val%print(f_unit)
1658 end do
1659 write(f_unit,*) "*** State Array ***"
1660 write(f_unit,*) "Number of species on the state array per grid cell: ", &
1661 this%size_state_per_cell
1662 allocate(state_names(this%size_state_per_cell))
1663 i_spec = 0
1664 do i_gas_spec = 1, &
1665 this%chem_spec_data%size(spec_phase=chem_spec_gas_phase)
1666 i_spec = i_gas_spec
1667 state_names(i_spec)%string = &
1668 this%chem_spec_data%gas_state_name(i_gas_spec)
1669 end do
1670 write(f_unit,*) "Gas-phase species: ", i_spec
1671 do i_aero_rep = 1, size(this%aero_rep)
1672 rep_spec_names = this%aero_rep(i_aero_rep)%val%unique_names()
1673 call assert(620697091, allocated(rep_spec_names))
1674 call assert(787495222, size(rep_spec_names).gt.0)
1675 forall (j_spec=1:size(rep_spec_names)) &
1676 state_names(i_spec+j_spec)%string = &
1677 rep_spec_names(j_spec)%string
1678 i_spec = i_spec + size(rep_spec_names)
1679 write(f_unit,*) "Aerosol rep ", &
1680 this%aero_rep(i_aero_rep)%val%rep_name, &
1681 " species: ", size(rep_spec_names)
1682 deallocate(rep_spec_names)
1683 end do
1684 do i_spec = 1, size(state_names)
1685 write(f_unit,*) i_spec-1, state_names(i_spec)%string
1686 end do
1687
1688 write(f_unit,*) "*** Solver Data ***"
1689 write(f_unit,*) "Relative tolerance:", this%rel_tol
1690 write(f_unit,*) " Solver id | Absolute Tolerance "// &
1691 "| Species Name"
1692 i_solver_spec = 0
1693 do i_spec = 1, size(state_names)
1694 if (this%var_type(i_spec).eq.chem_spec_variable) then
1695 write(f_unit,*) i_solver_spec, "|", this%abs_tol(i_spec), "| ", &
1696 state_names(i_spec)%string
1697 i_solver_spec = i_solver_spec + 1
1698 end if
1699 end do
1700 write(f_unit,*) ""
1701 deallocate(state_names)
1702 end if
1703
1704 flush(f_unit)
1705
1706 if (associated(this%solver_data_gas)) &
1707 call this%solver_data_gas%print()
1708 if (associated(this%solver_data_gas_aero)) &
1709 call this%solver_data_gas_aero%print()
1710
1711 flush(f_unit)
1712
1713 end subroutine do_print
1714
1715!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1716
1717 !> Finalize the core
1718 elemental subroutine finalize(this)
1719
1720 !> CAMP-core data
1721 type(camp_core_t), intent(inout) :: this
1722
1723 if (associated(this%mechanism)) &
1724 deallocate(this%mechanism)
1725 if (associated(this%chem_spec_data)) &
1726 deallocate(this%chem_spec_data)
1727 if (associated(this%sub_model)) &
1728 deallocate(this%sub_model)
1729 if (associated(this%aero_rep)) &
1730 deallocate(this%aero_rep)
1731 if (associated(this%aero_phase)) &
1732 deallocate(this%aero_phase)
1733 if (allocated(this%abs_tol)) &
1734 deallocate(this%abs_tol)
1735 if (allocated(this%var_type)) &
1736 deallocate(this%var_type)
1737 if (associated(this%solver_data_gas)) &
1738 deallocate(this%solver_data_gas)
1739 if (associated(this%solver_data_aero)) &
1740 deallocate(this%solver_data_aero)
1741 if (associated(this%solver_data_gas_aero)) &
1742 deallocate(this%solver_data_gas_aero)
1743
1744 end subroutine finalize
1745
1746!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1747
1748 !> Add a aerosol phase to the model data
1749 subroutine add_aero_phase(this, phase_name)
1750
1751 !> Model data
1752 class(camp_core_t), intent(inout) :: this
1753 !> Aerosol phase name
1754 character(len=*), intent(in) :: phase_name
1755
1756 type(aero_phase_data_ptr), pointer :: new_aero_phase(:)
1757
1758 allocate(new_aero_phase(size(this%aero_phase)+1))
1759
1760 new_aero_phase(1:size(this%aero_phase)) = &
1761 this%aero_phase(1:size(this%aero_phase))
1762
1763 new_aero_phase(size(new_aero_phase))%val => aero_phase_data_t(phase_name)
1764
1765 deallocate(this%aero_phase)
1766 this%aero_phase => new_aero_phase
1767
1768 end subroutine add_aero_phase
1769
1770!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1771
1772 !> Add a aerosol representation to the model data
1773 subroutine add_aero_rep(this, rep_name)
1774
1775 !> Model data
1776 class(camp_core_t), intent(inout) :: this
1777 !> Aerosol representation name
1778 character(len=*), intent(in) :: rep_name
1779
1780 type(aero_rep_data_ptr), pointer :: new_aero_rep(:)
1781 type(aero_rep_factory_t) :: aero_rep_factory
1782
1783 !TODO: Improve this multiple reallocation
1784 allocate(new_aero_rep(size(this%aero_rep)+1))
1785
1786 new_aero_rep(1:size(this%aero_rep)) = &
1787 this%aero_rep(1:size(this%aero_rep))
1788 new_aero_rep(size(new_aero_rep))%val => aero_rep_factory%create(rep_name)
1789
1790 call this%aero_rep(:)%dereference()
1791 deallocate(this%aero_rep)
1792 this%aero_rep => new_aero_rep
1793
1794 end subroutine add_aero_rep
1795
1796!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1797
1798 !> Add a chemical mechanism to the model data
1799 subroutine add_mechanism(this, mech_name)
1800
1801 !> Model data
1802 class(camp_core_t), intent(inout) :: this
1803 !> Mechanism name
1804 character(len=*), intent(in) :: mech_name
1805
1806 type(mechanism_data_ptr), pointer :: new_mechanism(:)
1807
1808 allocate(new_mechanism(size(this%mechanism)+1))
1809
1810 new_mechanism(1:size(this%mechanism)) = &
1811 this%mechanism(1:size(this%mechanism))
1812
1813 new_mechanism(size(new_mechanism))%val => mechanism_data_t(mech_name)
1814
1815 call this%mechanism(:)%dereference()
1816 deallocate(this%mechanism)
1817 this%mechanism => new_mechanism
1818
1819 end subroutine add_mechanism
1820
1821!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1822
1823 !> Add a sub-model to the model data
1824 subroutine add_sub_model(this, sub_model_name)
1825
1826 !> Model data
1827 class(camp_core_t), intent(inout) :: this
1828 !> Sub model name
1829 character(len=*), intent(in) :: sub_model_name
1830
1831 type(sub_model_data_ptr), pointer :: new_sub_model(:)
1832 type(sub_model_factory_t) :: sub_model_factory
1833
1834 allocate(new_sub_model(size(this%sub_model)+1))
1835
1836 new_sub_model(1:size(this%sub_model)) = &
1837 this%sub_model(1:size(this%sub_model))
1838 new_sub_model(size(new_sub_model))%val => &
1839 sub_model_factory%create(sub_model_name)
1840
1841 deallocate(this%sub_model)
1842 this%sub_model => new_sub_model
1843
1844 end subroutine add_sub_model
1845
1846!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1847
1848end module camp_camp_core
@ true
Definition: camp_common.h:56
@ false
Definition: camp_common.h:56
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 species id on the camp_camp_state::camp_state_t::state_var array by unique name....
Get a list of unique names for each element on the camp_camp_state::camp_state_t::state_var array for...
Update aerosol representation data.
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.
subroutine bin_unpack(this, buffer, pos, comm)
Unpack the given value from the buffer, advancing position.
type(aero_phase_data_t) function, pointer constructor(phase_name, init_size)
Constructor for aero_phase_data_t.
subroutine bin_pack(this, buffer, pos, comm)
Pack the given value to the buffer, advancing position.
subroutine load(this, json, j_obj)
Load species from an input file.
integer(kind=i_kind) function pack_size(this, comm)
Determine the size of a binary required to pack the aerosol representation data.
subroutine do_print(this, file_unit)
Print out the aerosol phase data.
The abstract aero_rep_data_t structure and associated subroutines.
The aero_rep_factory_t type and associated subroutines.
The camp_core_t structure and associated subroutines.
Definition: camp_core.F90:88
subroutine initialize_aero_rep_update_object(this, aero_rep, update_data)
Initialize an update data object for an aerosol representation.
Definition: camp_core.F90:1227
subroutine add_aero_rep(this, rep_name)
Add a aerosol representation to the model data.
Definition: camp_core.F90:1774
subroutine add_sub_model(this, sub_model_name)
Add a sub-model to the model data.
Definition: camp_core.F90:1825
logical function get_aero_phase(this, aero_phase_name, aero_phase)
Get a pointer to an aerosol phase by name.
Definition: camp_core.F90:826
subroutine free_solver(this)
Free the solver memory.
Definition: camp_core.F90:1210
real(kind=dp) function get_rel_tol(this)
Get the relative tolerance for the solver.
Definition: camp_core.F90:951
integer(kind=i_kind) function state_size(this)
Get the size of the state array.
Definition: camp_core.F90:1036
subroutine solve(this, camp_state, time_step, rxn_phase, solver_stats)
Integrate the chemical mechanism.
Definition: camp_core.F90:1355
subroutine add_mechanism(this, mech_name)
Add a chemical mechanism to the model data.
Definition: camp_core.F90:1800
logical function is_initialized(this)
Inidicate whether the core has been initialized.
Definition: camp_core.F90:801
subroutine add_aero_phase(this, phase_name)
Add a aerosol phase to the model data.
Definition: camp_core.F90:1750
subroutine initialize_rxn_update_object(this, rxn, update_data)
Initialize an update data object for a reaction.
Definition: camp_core.F90:1248
logical function get_mechanism(this, mech_name, mechanism)
Get a pointer to a mechanism by name.
Definition: camp_core.F90:896
logical function get_aero_rep(this, aero_rep_name, aero_rep)
Get a pointer to an aerosol representation by name.
Definition: camp_core.F90:853
real(kind=dp) function get_abs_tol(this, spec_id)
Get the absolute tolerance for a species on the state array.
Definition: camp_core.F90:965
logical function get_sub_model(this, sub_model_name, sub_model)
Find an sub-model by name.
Definition: camp_core.F90:924
type(camp_state_t) function, pointer new_state_one_cell(this, env_state)
Get a model state variable based on the this set of model data This is also called for multi-cell sys...
Definition: camp_core.F90:1008
logical function is_solver_initialized(this)
Inidicate whether the solver has been initialized.
Definition: camp_core.F90:813
type(camp_state_t) function, pointer new_state_multi_cell(this, env_states)
Get a model state variable based on the this set of model data.
Definition: camp_core.F90:985
logical function get_chem_spec_data(this, chem_spec_data)
Get a pointer to the chemical species data.
Definition: camp_core.F90:880
subroutine initialize_sub_model_update_object(this, sub_model, update_data)
Initialize an update data object for a sub model.
Definition: camp_core.F90:1269
subroutine load_files(this, input_file_path)
Load a set of model data files.
Definition: camp_core.F90:304
integer(kind=i_kind) function state_size_per_cell(this)
Get the size of the state array for each grid cell.
Definition: camp_core.F90:1054
The camp_solver_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
integer(kind=i_kind), parameter, public chem_spec_gas_phase
integer(kind=i_kind), parameter, public chem_spec_variable
Physical constants.
Definition: constants.F90:9
integer, parameter dp
Kind of a double precision real number.
Definition: constants.F90:16
integer, parameter i_kind
Kind of an integer.
Definition: constants.F90:21
The env_state_t structure and associated subroutines.
Definition: env_state.F90:9
The mechanism_data_t structure and associated subroutines.
Wrapper functions for MPI.
Definition: mpi.F90:13
subroutine camp_mpi_pack_integer_array(buffer, position, val, comm)
Packs the given value into the buffer, advancing position.
Definition: mpi.F90:858
subroutine camp_mpi_pack_real(buffer, position, val, comm)
Packs the given value into the buffer, advancing position.
Definition: mpi.F90:724
subroutine camp_mpi_pack_real_array(buffer, position, val, comm)
Packs the given value into the buffer, advancing position.
Definition: mpi.F90:899
subroutine camp_mpi_unpack_integer_array(buffer, position, val, comm)
Unpacks the given value from the buffer, advancing position.
Definition: mpi.F90:1201
subroutine camp_mpi_pack_logical(buffer, position, val, comm)
Packs the given value into the buffer, advancing position.
Definition: mpi.F90:792
subroutine camp_mpi_unpack_integer(buffer, position, val, comm)
Unpacks the given value from the buffer, advancing position.
Definition: mpi.F90:1023
subroutine camp_mpi_unpack_logical(buffer, position, val, comm)
Unpacks the given value from the buffer, advancing position.
Definition: mpi.F90:1131
integer function camp_mpi_pack_size_real_array(val, comm)
Determines the number of bytes required to pack the given value.
Definition: mpi.F90:578
integer function camp_mpi_pack_size_integer_array(val, comm)
Determines the number of bytes required to pack the given value.
Definition: mpi.F90:540
integer function camp_mpi_pack_size_logical(val, comm)
Determines the number of bytes required to pack the given value.
Definition: mpi.F90:484
subroutine camp_mpi_pack_integer(buffer, position, val, comm)
Packs the given value into the buffer, advancing position.
Definition: mpi.F90:691
integer function camp_mpi_pack_size_integer(val, comm)
Determines the number of bytes required to pack the given value.
Definition: mpi.F90:398
subroutine camp_mpi_unpack_real(buffer, position, val, comm)
Unpacks the given value from the buffer, advancing position.
Definition: mpi.F90:1058
integer function camp_mpi_pack_size_real(val, comm)
Determines the number of bytes required to pack the given value.
Definition: mpi.F90:426
subroutine camp_mpi_unpack_real_array(buffer, position, val, comm)
Unpacks the given value from the buffer, advancing position.
Definition: mpi.F90:1242
The rxn_data_t structure and associated subroutines.
Definition: rxn_data.F90:60
integer(kind=i_kind), parameter, public gas_rxn
Gas-phase reaction.
Definition: rxn_data.F90:84
integer(kind=i_kind), parameter, public gas_aero_rxn
Mixed-phase (gas and aerosol) reaction.
Definition: rxn_data.F90:86
integer(kind=i_kind), parameter, public aero_rxn
Aerosol-phase reaction.
Definition: rxn_data.F90:88
The abstract rxn_factory_t structure and associated subroutines.
The solver_stats_t type and associated subroutines.
Definition: solver_stats.F90:9
The abstract sub_model_data_t structure and associated subroutines.
The sub_model_factory_t type and associated 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
subroutine warn_msg(code, warning_msg, already_warned)
Prints a warning message.
Definition: util.F90:90
Pointer type for building arrays.
Pointer to aero_rep_data_t extending types.
Abstract aerosol representation data type.
Factory type for aerosol representations.
Part-MC model data.
Definition: camp_core.F90:122
Pointer for env_state_t.
Definition: env_state.F90:57
Current environment state.
Definition: env_state.F90:24
Pointer type for building arrays.
Abstract reaction data type.
Definition: rxn_data.F90:98
Factory type for chemical reactions.
Pointer to sub_model_data_t extending types.
Abstract sub-model data type.
String type for building arrays of string of various size.
Definition: util.F90:38