PartMC  2.8.0
aero_state.F90
Go to the documentation of this file.
1 ! Copyright (C) 2005-2021 Nicole Riemer and Matthew West
2 ! Licensed under the GNU General Public License version 2 or (at your
3 ! option) any later version. See the file COPYING for details.
4 
5 !> \file
6 !> The pmc_aero_state module.
7 
8 !> The aero_state_t structure and assocated subroutines.
10 
12  use pmc_aero_sorted
14  use pmc_bin_grid
15  use pmc_aero_data
17  use pmc_aero_dist
18  use pmc_util
19  use pmc_rand
20  use pmc_aero_binned
21  use pmc_mpi
22  use pmc_spec_file
23  use pmc_aero_info
25  use pmc_aero_weight
27 #ifdef PMC_USE_MPI
28  use mpi
29 #endif
30 
31  !> MPI tag for mixing particles between processes.
32  integer, parameter :: aero_state_tag_mix = 4987
33  !> MPI tag for gathering between processes.
34  integer, parameter :: aero_state_tag_gather = 4988
35  !> MPI tag for scattering between processes.
36  integer, parameter :: aero_state_tag_scatter = 4989
37 
38  !> Single flat weighting scheme.
39  integer, parameter :: aero_state_weight_none = 1
40  !> Single flat weighting scheme.
41  integer, parameter :: aero_state_weight_flat = 2
42  !> Power-law weighting scheme.
43  integer, parameter :: aero_state_weight_power = 3
44  !> Coupled number/mass weighting scheme.
45  integer, parameter :: aero_state_weight_nummass = 4
46  !> Flat weighting by source.
47  integer, parameter :: aero_state_weight_flat_source = 5
48  !> Power-law weighting by source.
49  integer, parameter :: aero_state_weight_power_source = 6
50  !> Coupled number/mass weighting by source.
51  integer, parameter :: aero_state_weight_nummass_source = 7
52  !> Flat weighting by specified weight classes.
53  integer, parameter :: aero_state_weight_flat_specified = 8
54  !> Power-law weighting by specified weight classes.
55  integer, parameter :: aero_state_weight_power_specified = 9
56  !> Coupled number/mass weighting by specific weight classes.
57  integer, parameter :: aero_state_weight_nummass_specified = 10
58 
59  !> The current collection of aerosol particles.
60  !!
61  !! The particles in \c aero_state_t are stored in a single flat
62  !! array (the \c apa data member), with a sorting into size bins and
63  !! weight groups/classes possibly stored in the \c aero_sorted data
64  !! member (if \c valid_sort is true).
65  !!
66  !! Every time we remove particles we keep track of the particle ID
67  !! and the action performed in the aero_info_array_t structure. This
68  !! is typically cleared each time we output data to disk.
70  !> Linear array of particles.
71  type(aero_particle_array_t) :: apa
72  !> Sorting of particles into size bins and weight groups/classes.
73  type(aero_sorted_t) :: aero_sorted
74  !> Whether the \c aero_sorted is a correct sorting.
75  logical :: valid_sort
76  !> Weighting functions.
77  type(aero_weight_array_t) :: awa
78  !> Ideal number of computational particles, per weight group and class.
79  real(kind=dp), allocatable :: n_part_ideal(:, :)
80  !> Information on removed particles.
81  type(aero_info_array_t) :: aero_info_array
82 #ifdef PMC_USE_CAMP
83  !> CAMP update number conc cookie
84  type(aero_rep_update_data_single_particle_number_t) :: update_number
85 #endif
86  end type aero_state_t
87 
88 contains
89 
90 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
91 
92  !> Return the current number of particles.
93  elemental integer function aero_state_n_part(aero_state)
94 
95  !> Aerosol state.
96  type(aero_state_t), intent(in) :: aero_state
97 
99 
100  end function aero_state_n_part
101 
102 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
103 
104  !> Read the specification for a weighting type from a spec file.
105  subroutine spec_file_read_aero_state_weighting_type(file, weighting_type, &
106  exponent)
107 
108  !> Spec file.
109  type(spec_file_t), intent(inout) :: file
110  !> Aerosol weighting scheme.
111  integer, intent(out) :: weighting_type
112  !> Exponent for power-law weighting (only used if \c weight_type
113  !> is \c AERO_STATE_WEIGHT_POWER).
114  real(kind=dp), intent(out) :: exponent
115 
116  character(len=SPEC_LINE_MAX_VAR_LEN) :: weighting_name
117 
118  !> \page input_format_weight_type Input File Format: Type of aerosol size distribution weighting functions.
119  !!
120  !! The weighting function is specified by the parameters:
121  !! - \b weight_type (string): the type of weighting function ---
122  !! must be one of: \c flat for flat weighting, \c flat_source for
123  !! flat weighting by source, \c power for power weighting,
124  !! \c power_source for power source weighting, \c nummass for number
125  !! and mass weighting, and \c nummass_source for number and mass
126  !! weighting by source. If \c weight_type is \c power or \c
127  !! power_source then the next parameter must also be provided:
128  !! - \b weighting_exponent (real): the exponent for \c power or
129  !! \c power_source. Setting the \c exponent to 0 is equivalent to no
130  !! weighting, while setting the exponent negative uses more
131  !! computational particles at larger diameters and setting the
132  !! exponent positive uses more computaitonal partilces at smaller
133  !! diameters; in practice exponents between 0 and -3 are most useful.
134  !!
135  !! See also:
136  !! - \ref spec_file_format --- the input file text format
137 
138  call spec_file_read_string(file, 'weight_type', weighting_name)
139  if (trim(weighting_name) == 'flat') then
140  weighting_type = aero_state_weight_flat
141  elseif (trim(weighting_name) == 'power') then
142  weighting_type = aero_state_weight_power
143  call spec_file_read_real(file, 'weighting_exponent', exponent)
144  elseif (trim(weighting_name) == 'nummass') then
145  weighting_type = aero_state_weight_nummass
146  elseif (trim(weighting_name) == 'flat_source') then
147  weighting_type = aero_state_weight_flat_source
148  elseif (trim(weighting_name) == 'power_source') then
149  weighting_type = aero_state_weight_power_source
150  call spec_file_read_real(file, 'weighting_exponent', exponent)
151  elseif (trim(weighting_name) == 'nummass_source') then
152  weighting_type = aero_state_weight_nummass_source
153  elseif (trim(weighting_name) == 'flat_specified') then
154  weighting_type = aero_state_weight_flat_specified
155  elseif (trim(weighting_name) == 'power_specified') then
156  weighting_type = aero_state_weight_power_specified
157  call spec_file_read_real(file, 'weighting_exponent', exponent)
158  elseif (trim(weighting_name) == 'nummass_specified') then
159  weighting_type = aero_state_weight_nummass_specified
160  else
161  call spec_file_die_msg(920321729, file, &
162  "Unknown weighting type: " // trim(weighting_name))
163  end if
164 
165  end subroutine spec_file_read_aero_state_weighting_type
166 
167 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
168 
169  !> Copies weighting information for an \c aero_state.
170  subroutine aero_state_copy_weight(aero_state_from, aero_state_to)
171 
172  !> Reference aerosol.
173  type(aero_state_t), intent(in) :: aero_state_from
174  !> Already allocated.
175  type(aero_state_t), intent(inout) :: aero_state_to
176 
177  aero_state_to%awa = aero_state_from%awa
178 
179  end subroutine aero_state_copy_weight
180 
181 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
182 
183  !> Sets the weighting functions for an \c aero_state.
184  subroutine aero_state_set_weight(aero_state, aero_data, weight_type, &
185  exponent)
186 
187  !> Aerosol to set the weights on.
188  type(aero_state_t), intent(inout) :: aero_state
189  !> Aerosol data.
190  type(aero_data_t), intent(in) :: aero_data
191  !> Type of weighting scheme to use.
192  integer, intent(in) :: weight_type
193  !> Exponent for power-law weighting (only used if \c weight_type
194  !> is \c AERO_STATE_WEIGHT_POWER).
195  real(kind=dp), intent(in), optional :: exponent
196 
197  aero_state%valid_sort = .false.
198  select case(weight_type)
201  call aero_weight_array_set_flat(aero_state%awa, 1)
203  call assert_msg(656670336, present(exponent), &
204  "exponent parameter required for AERO_STATE_WEIGHT_POWER")
205  call aero_weight_array_set_power(aero_state%awa, 1, exponent)
207  call aero_weight_array_set_nummass(aero_state%awa, 1)
209  call aero_weight_array_set_flat(aero_state%awa, &
210  aero_data_n_source(aero_data))
212  call assert_msg(102143848, present(exponent), &
213  "exponent parameter required for AERO_STATE_WEIGHT_POWER")
214  call aero_weight_array_set_power(aero_state%awa, &
215  aero_data_n_source(aero_data), exponent)
217  call aero_weight_array_set_nummass(aero_state%awa, &
218  aero_data_n_source(aero_data))
220  call aero_weight_array_set_flat(aero_state%awa, &
221  aero_data_n_weight_class(aero_data))
223  call assert_msg(430273501, present(exponent), &
224  "exponent parameter required for AERO_STATE_WEIGHT_POWER")
225  call aero_weight_array_set_power(aero_state%awa, &
226  aero_data_n_weight_class(aero_data), exponent)
228  call aero_weight_array_set_nummass(aero_state%awa, &
229  aero_data_n_weight_class(aero_data))
230  case default
231  call die_msg(969076992, "unknown weight_type: " &
232  // trim(integer_to_string(weight_type)))
233  end select
234 
235  end subroutine aero_state_set_weight
236 
237 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
238 
239  !> Set the ideal number of particles to the given value. The \c
240  !> aero_state%%awa must be already set correctly.
241  subroutine aero_state_set_n_part_ideal(aero_state, n_part)
242 
243  !> Aerosol state (with \c aero_state%%awa set).
244  type(aero_state_t), intent(inout) :: aero_state
245  !> Ideal total number of particles.
246  real(kind=dp), intent(in) :: n_part
247 
248  integer :: n_group, n_class
249 
250  n_group = aero_weight_array_n_group(aero_state%awa)
251  n_class = aero_weight_array_n_class(aero_state%awa)
252  if (allocated(aero_state%n_part_ideal)) then
253  deallocate(aero_state%n_part_ideal)
254  end if
255  allocate(aero_state%n_part_ideal(n_group, n_class))
256  aero_state%n_part_ideal = n_part / real(n_group * n_class, kind=dp)
257 
258  end subroutine aero_state_set_n_part_ideal
259 
260 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
261 
262  !> Determine the appropriate weight class for a source.
263  integer function aero_state_weight_class_for_source(aero_state, source)
264 
265  !> Aerosol state.
266  type(aero_state_t), intent(in) :: aero_state
267  !> Source to find the class for.
268  integer, intent(in) :: source
269 
270  integer :: n_class
271 
272  call assert(932390238, source >= 1)
273  n_class = aero_weight_array_n_class(aero_state%awa)
274  ! we are either using i_class = i_source or always i_class = n_class = 1
275  if (n_class > 1) then
276  call assert(765048788, source <= n_class)
278  else
280  end if
281 
283 
284 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
285 
286  !> Returns the total number of particles in an aerosol distribution.
287  integer function aero_state_total_particles(aero_state, i_group, i_class)
288 
289  !> Aerosol state.
290  type(aero_state_t), intent(in) :: aero_state
291  !> Weight group.
292  integer, optional, intent(in) :: i_group
293  !> Weight class.
294  integer, optional, intent(in) :: i_class
295 
296  integer :: i_part
297 
298  if (present(i_group)) then
299  call assert(908743823, present(i_class))
300  if (aero_state%valid_sort) then
303  aero_state%aero_sorted%group_class%inverse(i_group, i_class))
304  else
305  ! FIXME: should we just sort?
307  do i_part = 1,aero_state_n_part(aero_state)
308  if ((aero_state%apa%particle(i_part)%weight_group == i_group) &
309  .and. &
310  (aero_state%apa%particle(i_part)%weight_class == i_class)) &
311  then
313  end if
314  end do
315  end if
316  else
318  end if
319 
320  end function aero_state_total_particles
321 
322 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
323 
324  !> Returns the total number of particles across all processes.
325  integer function aero_state_total_particles_all_procs(aero_state, i_group, &
326  i_class)
327 
328  !> Aerosol state.
329  type(aero_state_t), intent(in) :: aero_state
330  !> Weight group.
331  integer, optional, intent(in) :: i_group
332  !> Weight class.
333  integer, optional, intent(in) :: i_class
334 
335 #ifdef PMC_USE_WRF
337  aero_state, i_group, i_class)
338 #else
340  aero_state_total_particles(aero_state, i_group, i_class), &
342 #endif
343 
345 
346 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
347 
348  !> Resets an aero_state to have zero particles per bin.
349  subroutine aero_state_zero(aero_state)
350 
351  !> State to zero.
352  type(aero_state_t), intent(inout) :: aero_state
353 
354  integer :: i, n_bin
355 
356  call aero_particle_array_zero(aero_state%apa)
357  aero_state%valid_sort = .false.
358  call aero_info_array_zero(aero_state%aero_info_array)
359 
360  end subroutine aero_state_zero
361 
362 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
363 
364  !> Add the given particle.
365  subroutine aero_state_add_particle(aero_state, aero_particle, aero_data, &
366  allow_resort)
367 
368  !> Aerosol state.
369  type(aero_state_t), intent(inout) :: aero_state
370  !> Particle to add.
371  type(aero_particle_t), intent(in) :: aero_particle
372  !> Aerosol data.
373  type(aero_data_t), intent(in) :: aero_data
374  !> Whether to allow a resort due to the add.
375  logical, optional, intent(in) :: allow_resort
376 
377  if (aero_state%valid_sort) then
378  call aero_sorted_add_particle(aero_state%aero_sorted, aero_state%apa, &
379  aero_particle, aero_data, allow_resort)
380  else
381  call aero_particle_array_add_particle(aero_state%apa, aero_particle)
382  end if
383 
384  end subroutine aero_state_add_particle
385 
386 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
387 
388  !> Remove the given particle without recording it.
389  subroutine aero_state_remove_particle_no_info(aero_state, i_part)
390 
391  !> Aerosol state.
392  type(aero_state_t), intent(inout) :: aero_state
393  !> Index of particle to remove.
394  integer, intent(in) :: i_part
395 
396  if (aero_state%valid_sort) then
397  call aero_sorted_remove_particle(aero_state%aero_sorted, &
398  aero_state%apa, i_part)
399  else
400  call aero_particle_array_remove_particle(aero_state%apa, i_part)
401  end if
402 
404 
405 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
406 
407  !> Remove the given particle and record the removal.
408  subroutine aero_state_remove_particle_with_info(aero_state, i_part, &
409  aero_info)
410 
411  !> Aerosol state.
412  type(aero_state_t), intent(inout) :: aero_state
413  !> Index of particle to remove.
414  integer, intent(in) :: i_part
415  !> Removal info.
416  type(aero_info_t), intent(in) :: aero_info
417 
418  call aero_state_remove_particle_no_info(aero_state, i_part)
419  call aero_info_array_add_aero_info(aero_state%aero_info_array, &
420  aero_info)
421 
423 
424 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
425 
426  !> Remove the given particle and possibly record the removal.
427  subroutine aero_state_remove_particle(aero_state, i_part, &
428  record_removal, aero_info)
429 
430  !> Aerosol state.
431  type(aero_state_t), intent(inout) :: aero_state
432  !> Index of particle to remove.
433  integer, intent(in) :: i_part
434  !> Whether to record the removal in the aero_info_array.
435  logical, intent(in) :: record_removal
436  !> Removal info.
437  type(aero_info_t), intent(in) :: aero_info
438 
439  if (record_removal) then
440  call aero_state_remove_particle_with_info(aero_state, i_part, &
441  aero_info)
442  else
443  call aero_state_remove_particle_no_info(aero_state, i_part)
444  end if
445 
446  end subroutine aero_state_remove_particle
447 
448 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
449 
450  !> Remove a randomly chosen particle from the given bin and return
451  !> it.
453  i_bin, i_class, aero_particle)
454 
455  !> Aerosol state.
456  type(aero_state_t), intent(inout) :: aero_state
457  !> Bin number to remove particle from.
458  integer, intent(in) :: i_bin
459  !> Weight class to remove particle from.
460  integer, intent(in) :: i_class
461  !> Removed particle.
462  type(aero_particle_t), intent(inout) :: aero_particle
463 
464  integer :: i_entry, i_part
465 
466  call assert(742996300, aero_state%valid_sort)
467  call assert(392182617, integer_varray_n_entry( &
468  aero_state%aero_sorted%size_class%inverse(i_bin, i_class)) > 0)
470  aero_state%aero_sorted%size_class%inverse(i_bin, i_class)))
471  i_part = aero_state%aero_sorted%size_class%inverse(i_bin, &
472  i_class)%entry(i_entry)
473  aero_particle = aero_state%apa%particle(i_part)
474  call aero_state_remove_particle_no_info(aero_state, i_part)
475 
477 
478 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
479 
480  !> Add copies or remove a particle, with a given mean number of
481  !> resulting particles.
482  !!
483  !! The particle number \c i_part is either removed, or zero or more
484  !! copies are added, with a random number of copies with the given
485  !! mean \c n_part_mean. The final number of particles is either
486  !! <tt>floor(n_part_mean)</tt> or <tt>ceiling(n_part_mean)</tt>,
487  !! chosen randomly so the mean is \c n_part_mean.
488  subroutine aero_state_dup_particle(aero_state, aero_data, i_part, &
489  n_part_mean, random_weight_group)
490 
491  !> Aerosol state.
492  type(aero_state_t), intent(inout) :: aero_state
493  !> Aerosol data.
494  type(aero_data_t), intent(in) :: aero_data
495  !> Particle number.
496  integer, intent(in) :: i_part
497  !> Mean number of resulting particles.
498  real(kind=dp), intent(in) :: n_part_mean
499  !> Whether particle copies should be placed in a randomly chosen
500  !> weight group.
501  logical, optional, intent(in) :: random_weight_group
502 
503  integer :: n_copies, i_dup, new_group
504  type(aero_particle_t) :: new_aero_particle
505  type(aero_info_t) :: aero_info
506  logical :: record_removal
507 
508  n_copies = prob_round(n_part_mean)
509  if (n_copies == 0) then
510  aero_info%id = aero_state%apa%particle(i_part)%id
511  aero_info%action = aero_info_weight
512  aero_info%other_id = 0
513 #ifdef PMC_USE_WRF
514  record_removal = .false.
515 #else
516  record_removal = .true.
517 #endif
518  call aero_state_remove_particle(aero_state, i_part, &
519  record_removal, aero_info)
520  elseif (n_copies > 1) then
521  do i_dup = 1,(n_copies - 1)
522  new_aero_particle = aero_state%apa%particle(i_part)
523  call aero_particle_new_id(new_aero_particle)
524  if (present(random_weight_group)) then
525  if (random_weight_group) then
526  new_group &
527  = aero_weight_array_rand_group(aero_state%awa, &
528  aero_state%apa%particle(i_part)%weight_class, &
529  aero_particle_radius(aero_state%apa%particle(i_part), &
530  aero_data))
531  call aero_particle_set_weight(new_aero_particle, new_group)
532  end if
533  end if
534  call aero_state_add_particle(aero_state, new_aero_particle, &
535  aero_data)
536  end do
537  end if
538 
539  end subroutine aero_state_dup_particle
540 
541 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
542 
543  !> The number concentration of a single particle (m^{-3}).
544  real(kind=dp) function aero_state_particle_num_conc(aero_state, &
545  aero_particle, aero_data)
546 
547  !> Aerosol state containing the particle.
548  type(aero_state_t), intent(in) :: aero_state
549  !> Aerosol particle.
550  type(aero_particle_t), intent(in) :: aero_particle
551  !> Aerosol data.
552  type(aero_data_t), intent(in) :: aero_data
553 
555  = aero_weight_array_num_conc(aero_state%awa, aero_particle, &
556  aero_data)
557 
558  end function aero_state_particle_num_conc
559 
560 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
561 
562  !> Save the correct number concentrations for later use by
563  !> aero_state_reweight().
564  subroutine aero_state_num_conc_for_reweight(aero_state, aero_data, &
565  reweight_num_conc)
566 
567  !> Aerosol state.
568  type(aero_state_t), intent(in) :: aero_state
569  !> Aerosol data.
570  type(aero_data_t), intent(in) :: aero_data
571  !> Number concentrations for later use by aero_state_reweight().
572  real(kind=dp), intent(out) &
573  :: reweight_num_conc(aero_state_n_part(aero_state))
574 
575  integer :: i_part
576 
577  do i_part = 1,aero_state_n_part(aero_state)
578  reweight_num_conc(i_part) &
579  = aero_weight_array_single_num_conc(aero_state%awa, &
580  aero_state%apa%particle(i_part), aero_data)
581  end do
582 
583  end subroutine aero_state_num_conc_for_reweight
584 
585 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
586 
587  !> Reweight all particles after their constituent volumes have been
588  !> altered.
589  !!
590  !! The pattern for use should be like:
591  !! <pre>
592  !! call aero_state_num_conc_for_reweight(aero_state, aero_data,
593  !! reweight_num_conc)
594  !! ... alter particle species volumes in aero_state ...
595  !! call aero_state_reweight(aero_state, aero_data, reweight_num_conc)
596  !! </pre>
597  subroutine aero_state_reweight(aero_state, aero_data, reweight_num_conc)
598 
599  !> Aerosol state.
600  type(aero_state_t), intent(inout) :: aero_state
601  !> Aerosol data.
602  type(aero_data_t), intent(in) :: aero_data
603  !> Number concentrations previously computed by
604  !> aero_state_num_conc_for_reweight().
605  real(kind=dp), intent(in) &
606  :: reweight_num_conc(aero_state_n_part(aero_state))
607 
608  integer :: i_part, i_group, i_class
609  real(kind=dp) :: n_part_old(size(aero_state%awa%weight, 1), &
610  size(aero_state%awa%weight, 2))
611  real(kind=dp) :: n_part_new(size(aero_state%awa%weight, 1), &
612  size(aero_state%awa%weight, 2))
613  real(kind=dp) :: old_num_conc, new_num_conc, n_part_mean
614 
615  ! find average number of new particles in each weight group, if
616  ! weight is not changed
617  n_part_old = 0d0
618  n_part_new = 0d0
619  do i_part = 1,aero_state_n_part(aero_state)
620  old_num_conc = reweight_num_conc(i_part)
621  new_num_conc = aero_weight_array_single_num_conc(aero_state%awa, &
622  aero_state%apa%particle(i_part), aero_data)
623  n_part_mean = old_num_conc / new_num_conc
624  i_group = aero_state%apa%particle(i_part)%weight_group
625  i_class = aero_state%apa%particle(i_part)%weight_class
626  n_part_new(i_group, i_class) = n_part_new(i_group, i_class) &
627  + n_part_mean
628  n_part_old(i_group, i_class) = n_part_old(i_group, i_class) + 1d0
629  end do
630 
631  ! alter weight to leave the number of computational particles
632  ! per weight bin unchanged
633  do i_group = 1,size(aero_state%awa%weight, 1)
634  do i_class = 1,size(aero_state%awa%weight, 2)
635  if (n_part_old(i_group, i_class) == 0d0) cycle
636  call aero_weight_scale(aero_state%awa%weight(i_group, i_class), &
637  n_part_new(i_group, i_class) / n_part_old(i_group, i_class))
638  end do
639  end do
640 
641  ! work backwards so any additions and removals will only affect
642  ! particles that we've already dealt with
643  do i_part = aero_state_n_part(aero_state),1,-1
644  old_num_conc = reweight_num_conc(i_part)
645  new_num_conc &
646  = aero_weight_array_single_num_conc(aero_state%awa, &
647  aero_state%apa%particle(i_part), aero_data)
648  n_part_mean = old_num_conc / new_num_conc
649  call aero_state_dup_particle(aero_state, aero_data, i_part, &
650  n_part_mean)
651  end do
652 
653  end subroutine aero_state_reweight
654 
655 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
656 
657  !> <tt>aero_state += aero_state_delta</tt>, including combining the
658  !> weights, so the new concentration is the weighted average of the
659  !> two concentrations.
660  subroutine aero_state_add(aero_state, aero_state_delta, aero_data)
661 
662  !> Aerosol state.
663  type(aero_state_t), intent(inout) :: aero_state
664  !> Increment.
665  type(aero_state_t), intent(in) :: aero_state_delta
666  !> Aerosol data.
667  type(aero_data_t), intent(in) :: aero_data
668 
669  call aero_state_add_particles(aero_state, aero_state_delta, aero_data)
670  call aero_weight_array_combine(aero_state%awa, aero_state_delta%awa)
671 
672  end subroutine aero_state_add
673 
674 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
675 
676  !> <tt>aero_state += aero_state_delta</tt>, with the weight
677  !> of \c aero_state left unchanged, so the new concentration is the
678  !> sum of the two concentrations, computed with \c aero_state%%awa.
679  subroutine aero_state_add_particles(aero_state, aero_state_delta, aero_data)
680 
681  !> Aerosol state.
682  type(aero_state_t), intent(inout) :: aero_state
683  !> Increment.
684  type(aero_state_t), intent(in) :: aero_state_delta
685  !> Aerosol data.
686  type(aero_data_t), intent(in) :: aero_data
687 
688  integer :: i_part, i_bin
689 
690  do i_part = 1,aero_state_delta%apa%n_part
691  call aero_state_add_particle(aero_state, &
692  aero_state_delta%apa%particle(i_part), aero_data)
693  end do
694  call aero_info_array_add(aero_state%aero_info_array, &
695  aero_state_delta%aero_info_array)
696 
697  end subroutine aero_state_add_particles
698 
699 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
700 
701  !> Change the weight if necessary to ensure that the addition of
702  !> about \c n_add computational particles will give the correct
703  !> final particle number.
704  subroutine aero_state_prepare_weight_for_add(aero_state, aero_data, &
705  i_group, i_class, n_add, allow_doubling, allow_halving)
706 
707  !> Aero state to add to.
708  type(aero_state_t), intent(inout) :: aero_state
709  !> Aerosol data.
710  type(aero_data_t), intent(in) :: aero_data
711  !> Weight group number to add to.
712  integer, intent(in) :: i_group
713  !> Weight class number to add to.
714  integer, intent(in) :: i_class
715  !> Approximate number of particles to be added at current weighting.
716  real(kind=dp), intent(in) :: n_add
717  !> Whether to allow doubling of the population.
718  logical, intent(in) :: allow_doubling
719  !> Whether to allow halving of the population.
720  logical, intent(in) :: allow_halving
721 
722  integer :: global_n_part, n_group, n_class
723  real(kind=dp) :: mean_n_part, n_part_new, weight_ratio
724  real(kind=dp) :: n_part_ideal_local_group
725 
726  n_group = aero_weight_array_n_group(aero_state%awa)
727  n_class = aero_weight_array_n_class(aero_state%awa)
728 #ifdef PMC_USE_WRF
729  global_n_part = aero_state_total_particles(aero_state, i_group, i_class)
730  mean_n_part = real(global_n_part, kind=dp)
731 #else
732  global_n_part = aero_state_total_particles_all_procs(aero_state, &
733  i_group, i_class)
734  mean_n_part = real(global_n_part, kind=dp) / real(pmc_mpi_size(), kind=dp)
735 #endif
736  n_part_new = mean_n_part + n_add
737  if (n_part_new == 0d0) return
738 #ifdef PMC_USE_WRF
739  n_part_ideal_local_group = aero_state%n_part_ideal(i_group, i_class)
740 #else
741  n_part_ideal_local_group = aero_state%n_part_ideal(i_group, i_class) &
742  / real(pmc_mpi_size(), kind=dp)
743 #endif
744  if ((n_part_new < n_part_ideal_local_group / 2d0) &
745  .or. (n_part_new > n_part_ideal_local_group * 2d0)) &
746  then
747  weight_ratio = n_part_new / n_part_ideal_local_group
748  call aero_state_scale_weight(aero_state, aero_data, i_group, &
749  i_class, weight_ratio, allow_doubling, allow_halving)
750  end if
751 
752  end subroutine aero_state_prepare_weight_for_add
753 
754 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
755 
756  !> Generates a Poisson sample of an \c aero_dist, adding to \c
757  !> aero_state, with the given sample proportion.
758  subroutine aero_state_add_aero_dist_sample(aero_state, aero_data, &
759  aero_dist, sample_prop, characteristic_factor, create_time, &
760  allow_doubling, allow_halving, n_part_add)
761 
762  !> Aero state to add to.
763  type(aero_state_t), intent(inout) :: aero_state
764  !> Aero data values.
765  type(aero_data_t), intent(in) :: aero_data
766  !> Distribution to sample.
767  type(aero_dist_t), intent(in) :: aero_dist
768  !> Fraction to sample (1).
769  real(kind=dp), intent(in) :: sample_prop
770  !> Factor to scale current sample to achieve characteristic sample.
771  real(kind=dp), intent(in) :: characteristic_factor
772  !> Creation time for new particles (s).
773  real(kind=dp), intent(in) :: create_time
774  !> Whether to allow doubling of the population.
775  logical, intent(in) :: allow_doubling
776  !> Whether to allow halving of the population.
777  logical, intent(in) :: allow_halving
778  !> Number of particles added.
779  integer, intent(out), optional :: n_part_add
780 
781  real(kind=dp) :: n_samp_avg, radius, total_vol
782  real(kind=dp) :: vols(aero_data_n_spec(aero_data))
783  real(kind=dp) :: size_factor
784  integer :: n_samp, i_mode, i_samp, i_group, i_class, n_group, n_class
785  type(aero_particle_t) :: aero_particle
786 
787  n_group = size(aero_state%awa%weight, 1)
788  n_class = size(aero_state%awa%weight, 2)
789  if (present(n_part_add)) then
790  n_part_add = 0
791  end if
792  do i_group = 1,n_group
793  do i_mode = 1,aero_dist_n_mode(aero_dist)
794  i_class = aero_state_weight_class_for_source(aero_state, &
795  aero_mode_get_weight_class(aero_dist%mode(i_mode)))
796  ! adjust weight if necessary
797  n_samp_avg = sample_prop * aero_mode_number(aero_dist%mode(i_mode), &
798  aero_state%awa%weight(i_group, i_class))
799  call aero_state_prepare_weight_for_add(aero_state, aero_data, &
800  i_group, i_class, n_samp_avg, allow_doubling, allow_halving)
801  if (n_samp_avg == 0d0) cycle
802 
803  ! sample particles
804  n_samp_avg = sample_prop * aero_mode_number(aero_dist%mode(i_mode), &
805  aero_state%awa%weight(i_group, i_class))
806  n_samp = rand_poisson(n_samp_avg)
807  size_factor = min((1.0d0 / (characteristic_factor * n_samp_avg)), &
808  0.32d0)
809 
810  if (present(n_part_add)) then
811  n_part_add = n_part_add + n_samp
812  end if
813  do i_samp = 1,n_samp
814  call aero_particle_zero(aero_particle, aero_data)
815  call aero_mode_sample_radius(aero_dist%mode(i_mode), aero_data, &
816  aero_state%awa%weight(i_group, i_class), radius, size_factor)
817  total_vol = aero_data_rad2vol(aero_data, radius)
818  call aero_mode_sample_vols(aero_dist%mode(i_mode), total_vol, &
819  vols)
820  call aero_particle_set_vols(aero_particle, vols)
821  call aero_particle_new_id(aero_particle)
822  call aero_particle_set_weight(aero_particle, i_group, i_class)
823  call aero_particle_set_component(aero_particle, &
824  aero_dist%mode(i_mode)%source, create_time)
825  aero_particle%n_primary_parts = 1
826  call aero_state_add_particle(aero_state, aero_particle, aero_data)
827  end do
828  end do
829  end do
830 
831  end subroutine aero_state_add_aero_dist_sample
832 
833 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
834 
835  !> Choose a random particle from the aero_state.
836  subroutine aero_state_rand_particle(aero_state, i_part)
837 
838  !> Original state.
839  type(aero_state_t), intent(in) :: aero_state
840  !> Chosen random particle number.
841  integer, intent(out) :: i_part
842 
843  call assert(950725003, aero_state_n_part(aero_state) > 0)
844  i_part = pmc_rand_int(aero_state_n_part(aero_state))
845 
846  end subroutine aero_state_rand_particle
847 
848 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
849 
850  !> Generates a random sample by removing particles from
851  !> aero_state_from and adding them to aero_state_to, which must be
852  !> already allocated (and should have its weight set).
853  !!
854  !! None of the weights are altered by this sampling, making this the
855  !! equivalent of aero_state_add_particles().
856  subroutine aero_state_sample_particles(aero_state_from, aero_state_to, &
857  aero_data, sample_prob, removal_action)
858 
859  !> Original state.
860  type(aero_state_t), intent(inout) :: aero_state_from
861  !> Destination state.
862  type(aero_state_t), intent(inout) :: aero_state_to
863  !> Aerosol data.
864  type(aero_data_t), intent(in) :: aero_data
865  !> Probability of sampling each particle.
866  real(kind=dp), intent(in) :: sample_prob
867  !> Action for removal (see pmc_aero_info module for action
868  !> parameters). Set to AERO_INFO_NONE to not log removal.
869  integer, intent(in) :: removal_action
870 
871  integer :: n_transfer, i_transfer, i_part
872  integer :: i_group, i_class
873  logical :: do_add, do_remove
874  real(kind=dp) :: num_conc_from, num_conc_to
875  type(aero_info_t) :: aero_info
876  integer :: n_parts_before
877 
878  call assert(721006962, (sample_prob >= 0d0) .and. (sample_prob <= 1d0))
879 
880  n_transfer = rand_binomial(aero_state_total_particles(aero_state_from), &
881  sample_prob)
882  i_transfer = 0
883  do while (i_transfer < n_transfer)
884  if (aero_state_total_particles(aero_state_from) <= 0) exit
885  call aero_state_rand_particle(aero_state_from, i_part)
886  num_conc_from = aero_weight_array_num_conc(aero_state_from%awa, &
887  aero_state_from%apa%particle(i_part), aero_data)
888  num_conc_to = aero_weight_array_num_conc(aero_state_to%awa, &
889  aero_state_from%apa%particle(i_part), aero_data)
890  if (num_conc_to == num_conc_from) then ! add and remove
891  do_add = .true.
892  do_remove = .true.
893  elseif (num_conc_to < num_conc_from) then ! always add, maybe remove
894  do_add = .true.
895  do_remove = .false.
896  if (pmc_random() < num_conc_to / num_conc_from) then
897  do_remove = .true.
898  end if
899  else ! always remove, maybe add
900  do_add = .false.
901  if (pmc_random() < num_conc_from / num_conc_to) then
902  do_add = .true.
903  end if
904  do_remove = .true.
905  end if
906  if (do_add) then
907  call aero_state_add_particle(aero_state_to, &
908  aero_state_from%apa%particle(i_part), aero_data)
909  ! If we don't remove, we need to change the ID
910  if (.not. do_remove) then
911  call aero_particle_new_id(aero_state_to%apa% &
912  particle(aero_state_to%apa%n_part))
913  end if
914  end if
915  if (do_remove) then
916  if (removal_action /= aero_info_none) then
917  aero_info%id = aero_state_from%apa%particle(i_part)%id
918  aero_info%action = removal_action
919  call aero_state_remove_particle_with_info(aero_state_from, &
920  i_part, aero_info)
921  else
922  call aero_state_remove_particle_no_info(aero_state_from, &
923  i_part)
924  end if
925  i_transfer = i_transfer + 1
926  end if
927  end do
928 
929  end subroutine aero_state_sample_particles
930 
931 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
932 
933  !> Generates a random sample by removing particles from
934  !> aero_state_from and adding them to aero_state_to, transfering
935  !> weight as well. This is the equivalent of aero_state_add().
936  subroutine aero_state_sample(aero_state_from, aero_state_to, &
937  aero_data, sample_prob, removal_action)
938 
939  !> Original state.
940  type(aero_state_t), intent(inout) :: aero_state_from
941  !> Destination state (previous contents will be lost).
942  type(aero_state_t), intent(inout) :: aero_state_to
943  !> Aerosol data.
944  type(aero_data_t), intent(in) :: aero_data
945  !> Probability of sampling each particle.
946  real(kind=dp), intent(in) :: sample_prob
947  !> Action for removal (see pmc_aero_info module for action
948  !> parameters). Set to AERO_INFO_NONE to not log removal.
949  integer, intent(in) :: removal_action
950 
951  integer :: n_transfer, i_transfer, i_part
952  logical :: do_add, do_remove, overwrite_to
953  real(kind=dp) :: num_conc_from, num_conc_to
954  type(aero_info_t) :: aero_info
955 
956  call assert(393205561, (sample_prob >= 0d0) .and. (sample_prob <= 1d0))
957  call aero_state_zero(aero_state_to)
958  call aero_state_copy_weight(aero_state_from, aero_state_to)
959  call aero_weight_array_normalize(aero_state_to%awa)
960  n_transfer = rand_binomial(aero_state_total_particles(aero_state_from), &
961  sample_prob)
962  do i_transfer = 1,n_transfer
963  if (aero_state_total_particles(aero_state_from) <= 0) exit
964  call aero_state_rand_particle(aero_state_from, i_part)
965 
966  call aero_state_add_particle(aero_state_to, &
967  aero_state_from%apa%particle(i_part), aero_data)
968  if (removal_action /= aero_info_none) then
969  aero_info%id = aero_state_from%apa%particle(i_part)%id
970  aero_info%action = removal_action
971  call aero_state_remove_particle_with_info(aero_state_from, &
972  i_part, aero_info)
973  else
974  call aero_state_remove_particle_no_info(aero_state_from, &
975  i_part)
976  end if
977  end do
978  overwrite_to = .true.
979  call aero_weight_array_shift(aero_state_from%awa, aero_state_to%awa, &
980  sample_prob, overwrite_to)
981 
982  end subroutine aero_state_sample
983 
984 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
985 
986  !> Create binned number and mass arrays.
987  subroutine aero_state_to_binned(bin_grid, aero_data, aero_state, &
988  aero_binned)
989 
990  !> Bin grid.
991  type(bin_grid_t), intent(in) :: bin_grid
992  !> Aerosol data.
993  type(aero_data_t), intent(in) :: aero_data
994  !> Aerosol state.
995  type(aero_state_t), intent(in) :: aero_state
996  !> Binned distributions.
997  type(aero_binned_t), intent(inout) :: aero_binned
998 
999  integer :: i_part, i_bin
1000  real(kind=dp) :: factor
1001 
1002  aero_binned%num_conc = 0d0
1003  aero_binned%vol_conc = 0d0
1004  do i_part = 1,aero_state_n_part(aero_state)
1005  i_bin = bin_grid_find(bin_grid, &
1006  aero_particle_radius(aero_state%apa%particle(i_part), aero_data))
1007  if ((i_bin < 1) .or. (i_bin > bin_grid_size(bin_grid))) then
1008  call warn_msg(980232449, "particle ID " // trim( &
1009  integer64_to_string(aero_state%apa%particle(i_part)%id)) &
1010  // " outside of bin_grid, discarding")
1011  else
1012  factor = aero_weight_array_num_conc(aero_state%awa, &
1013  aero_state%apa%particle(i_part), aero_data) &
1014  / bin_grid%widths(i_bin)
1015  aero_binned%vol_conc(i_bin,:) = aero_binned%vol_conc(i_bin,:) &
1016  + aero_state%apa%particle(i_part)%vol * factor
1017  aero_binned%num_conc(i_bin) = aero_binned%num_conc(i_bin) &
1018  + factor
1019  end if
1020  end do
1021 
1022  end subroutine aero_state_to_binned
1023 
1024 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1025 
1026  !> Returns the IDs of all particles.
1027  function aero_state_ids(aero_state)
1028 
1029  !> Aerosol state.
1030  type(aero_state_t), intent(in) :: aero_state
1031 
1032  !> Return value.
1033  integer(kind=8) :: aero_state_ids(aero_state_n_part(aero_state))
1034 
1035  integer :: i_part
1036 
1037  do i_part = 1,aero_state_n_part(aero_state)
1038  aero_state_ids(i_part) = aero_state%apa%particle(i_part)%id
1039  end do
1040 
1041  end function aero_state_ids
1042 
1043 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1044 
1045  !> Returns the diameters of all particles.
1046  function aero_state_diameters(aero_state, aero_data, include, exclude)
1047 
1048  !> Aerosol state.
1049  type(aero_state_t), intent(in) :: aero_state
1050  !> Aerosol data.
1051  type(aero_data_t), intent(in) :: aero_data
1052  !> Species names to include in the diameter.
1053  character(len=*), optional, intent(in) :: include(:)
1054  !> Species names to exclude in the diameter.
1055  character(len=*), optional, intent(in) :: exclude(:)
1056 
1057  !> Return diameters array (m).
1058  real(kind=dp) :: aero_state_diameters(aero_state_n_part(aero_state))
1059 
1060  !> Per-particle volume of included components
1061  real(kind=dp) :: volumes(aero_state_n_part(aero_state))
1062 
1063  volumes = aero_state_volumes(aero_state, aero_data, include, exclude)
1065 
1066  end function aero_state_diameters
1067 
1068 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1069 
1070  !> Returns the dry diameters of all particles.
1071  function aero_state_dry_diameters(aero_state, aero_data)
1072 
1073  !> Aerosol state.
1074  type(aero_state_t), intent(in) :: aero_state
1075  !> Aerosol data.
1076  type(aero_data_t), intent(in) :: aero_data
1077 
1078  !> Return value (m).
1079  real(kind=dp) :: aero_state_dry_diameters(aero_state_n_part(aero_state))
1080 
1082  aero_state%apa%particle(1:aero_state_n_part(aero_state)), aero_data)
1083 
1084  end function aero_state_dry_diameters
1085 
1086 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1087 
1088  !> Returns the mobility diameters of all particles.
1089  function aero_state_mobility_diameters(aero_state, aero_data, env_state)
1090 
1091  !> Aerosol state.
1092  type(aero_state_t), intent(in) :: aero_state
1093  !> Aerosol data.
1094  type(aero_data_t), intent(in) :: aero_data
1095  !> Environment state.
1096  type(env_state_t), intent(in) :: env_state
1097 
1098  !> Return value (m).
1099  real(kind=dp) :: aero_state_mobility_diameters( &
1100  aero_state_n_part(aero_state))
1101 
1102  integer :: i_part
1103 
1104  do i_part = 1,aero_state_n_part(aero_state)
1107  aero_state%apa%particle(i_part), aero_data, env_state)
1108  end do
1109 
1110  end function aero_state_mobility_diameters
1111 
1112 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1113 
1114  !> Returns the volumes of all particles.
1115  !!
1116  !! If \c include is specified then only those species are included
1117  !! in computing the volumes. If \c exclude is specified then all
1118  !! species except those species are included. If both \c include and
1119  !! \c exclude arguments are specified then only those species in \c
1120  !! include but not in \c exclude are included.
1121  function aero_state_volumes(aero_state, aero_data, include, exclude)
1122 
1123  !> Aerosol state.
1124  type(aero_state_t), intent(in) :: aero_state
1125  !> Aerosol data.
1126  type(aero_data_t), optional, intent(in) :: aero_data
1127  !> Species names to include in the mass.
1128  character(len=*), optional, intent(in) :: include(:)
1129  !> Species names to exclude in the mass.
1130  character(len=*), optional, intent(in) :: exclude(:)
1131 
1132  !> Return volumes array (m^3).
1133  real(kind=dp) :: aero_state_volumes(aero_state_n_part(aero_state))
1134 
1135  logical, allocatable :: use_species(:)
1136  integer :: i_name, i_spec
1137 
1138  if ((.not. present(include)) .and. (.not. present(exclude))) then
1140  aero_state%apa%particle(1:aero_state_n_part(aero_state)))
1141  else
1142  call assert_msg(599558703, present(aero_data), &
1143  "must provide 'aero_data' if using 'include' or 'exclude'")
1144  allocate(use_species(aero_data_n_spec(aero_data)))
1145  if (present(include)) then
1146  use_species = .false.
1147  do i_name = 1, size(include)
1148  i_spec = aero_data_spec_by_name(aero_data, include(i_name))
1149  call assert_msg(111852070, i_spec > 0, &
1150  "unknown species: " // trim(include(i_name)))
1151  use_species(i_spec) = .true.
1152  end do
1153  else
1154  use_species = .true.
1155  end if
1156  if (present(exclude)) then
1157  do i_name = 1, size(exclude)
1158  i_spec = aero_data_spec_by_name(aero_data, exclude(i_name))
1159  call assert_msg(182075590, i_spec > 0, &
1160  "unknown species: " // trim(exclude(i_name)))
1161  use_species(i_spec) = .false.
1162  end do
1163  end if
1164  aero_state_volumes = 0d0
1165  do i_spec = 1,aero_data_n_spec(aero_data)
1166  if (use_species(i_spec)) then
1169  aero_state%apa%particle(1:aero_state_n_part(aero_state)), &
1170  i_spec)
1171  end if
1172  end do
1173  end if
1174 
1175  end function aero_state_volumes
1176 
1177 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1178 
1179  !> Returns the masses of all particles.
1180  !!
1181  !! If \c include is specified then only those species are included
1182  !! in computing the masses. If \c exclude is specified then all
1183  !! species except those species are included. If both \c include and
1184  !! \c exclude arguments are specified then only those species in \c
1185  !! include but not in \c exclude are included.
1186  function aero_state_masses(aero_state, aero_data, include, exclude)
1187 
1188  !> Aerosol state.
1189  type(aero_state_t), intent(in) :: aero_state
1190  !> Aerosol data.
1191  type(aero_data_t), intent(in) :: aero_data
1192  !> Species names to include in the mass.
1193  character(len=*), optional, intent(in) :: include(:)
1194  !> Species names to exclude in the mass.
1195  character(len=*), optional, intent(in) :: exclude(:)
1196 
1197  !> Return masses array (kg).
1198  real(kind=dp) :: aero_state_masses(aero_state_n_part(aero_state))
1199 
1200  logical :: use_species(aero_data_n_spec(aero_data))
1201  integer :: i_name, i_spec
1202 
1203  if ((.not. present(include)) .and. (.not. present(exclude))) then
1205  aero_state%apa%particle(1:aero_state_n_part(aero_state)), &
1206  aero_data)
1207  else
1208  if (present(include)) then
1209  use_species = .false.
1210  do i_name = 1, size(include)
1211  i_spec = aero_data_spec_by_name(aero_data, include(i_name))
1212  call assert_msg(963163690, i_spec > 0, &
1213  "unknown species: " // trim(include(i_name)))
1214  use_species(i_spec) = .true.
1215  end do
1216  else
1217  use_species = .true.
1218  end if
1219  if (present(exclude)) then
1220  do i_name = 1, size(exclude)
1221  i_spec = aero_data_spec_by_name(aero_data, exclude(i_name))
1222  call assert_msg(950847713, i_spec > 0, &
1223  "unknown species: " // trim(exclude(i_name)))
1224  use_species(i_spec) = .false.
1225  end do
1226  end if
1227  aero_state_masses = 0d0
1228  do i_spec = 1,aero_data_n_spec(aero_data)
1229  if (use_species(i_spec)) then
1232  aero_state%apa%particle(1:aero_state_n_part(aero_state)), &
1233  i_spec, aero_data)
1234  end if
1235  end do
1236  end if
1237 
1238  end function aero_state_masses
1239 
1240 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1241 
1242  !> Returns the number concentrations of all particles.
1243  function aero_state_num_concs(aero_state, aero_data)
1244 
1245  !> Aerosol state.
1246  type(aero_state_t), intent(in) :: aero_state
1247  !> Aerosol data.
1248  type(aero_data_t), intent(in) :: aero_data
1249 
1250  !> Return number concentrations array (m^{-3}).
1251  real(kind=dp) :: aero_state_num_concs(aero_state_n_part(aero_state))
1252 
1253  integer :: i_part
1254 
1255  do i_part = 1,aero_state_n_part(aero_state)
1256  aero_state_num_concs(i_part) &
1257  = aero_state_particle_num_conc(aero_state, &
1258  aero_state%apa%particle(i_part), aero_data)
1259  end do
1260 
1261  end function aero_state_num_concs
1262 
1263 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1264 
1265  !> Returns the total number concentration.
1266  real(kind=dp) function aero_state_total_num_conc(aero_state, aero_data)
1267 
1268  !> Aerosol state.
1269  type(aero_state_t), intent(in) :: aero_state
1270  !> Aerosol data.
1271  type(aero_data_t), intent(in) :: aero_data
1272 
1273  integer :: i_part
1274 
1276  do i_part = 1,aero_state_n_part(aero_state)
1278  + aero_state_particle_num_conc(aero_state, &
1279  aero_state%apa%particle(i_part), aero_data)
1280  end do
1281 
1282  end function aero_state_total_num_conc
1283 
1284 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1285 
1286  !> Returns the total number concentration of wet particles.
1287  real(kind=dp) function aero_state_total_num_conc_wet(aero_state, aero_data)
1288 
1289  !> Aerosol state.
1290  type(aero_state_t), intent(in) :: aero_state
1291  !> Aerosol data.
1292  type(aero_data_t), intent(in) :: aero_data
1293 
1294  integer :: i_part
1295 
1297  if (aero_data%i_water > 0) then
1298  do i_part = 1,aero_state_n_part(aero_state)
1299  if (aero_state%apa%particle(i_part)%vol(aero_data%i_water) > 0d0) &
1300  then
1302  + aero_state_particle_num_conc(aero_state, &
1303  aero_state%apa%particle(i_part), aero_data)
1304  end if
1305  end do
1306  end if
1307 
1308  end function aero_state_total_num_conc_wet
1309 
1310 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1311 
1312  !> Returns the total number concentration associated with each aerosol
1313  !> source category. The amount of concentration of each particle assigned to
1314  !> a source is proportional to the number of primary componenets consisting of
1315  !> that source and the total number of components. Number concentration may be
1316  !> counted more than once for a source if the source appears more than once.
1317  function aero_state_num_concs_by_source(aero_state, aero_data)
1318 
1319  !> Aerosol state.
1320  type(aero_state_t) :: aero_state
1321  !> Aerosol data.
1322  type(aero_data_t) :: aero_data
1323 
1324  !> Return number concentrations array (m^{-3}).
1325  real(kind=dp) :: aero_state_num_concs_by_source( &
1326  aero_data_n_source(aero_data))
1327 
1328  real(kind=dp) :: num_concs(aero_state_n_part(aero_state))
1329  integer :: i_part, i_source, i_comp
1330  real(kind=dp) :: source_weighted_conc
1331 
1333  num_concs = aero_state_num_concs(aero_state, aero_data)
1334  do i_part = 1,aero_state_n_part(aero_state)
1335  do i_comp = 1,aero_particle_n_components( &
1336  aero_state%apa%particle(i_part))
1337  i_source = aero_state%apa%particle(i_part)%component(i_comp)% &
1338  source_id
1339  aero_state_num_concs_by_source(i_source) = &
1340  aero_state_num_concs_by_source(i_source) + num_concs(i_part) &
1341  * (aero_state%apa%particle(i_part)%n_primary_parts &
1342  / aero_particle_n_components(aero_state%apa%particle(i_part)))
1343  end do
1344  end do
1345 
1346  end function aero_state_num_concs_by_source
1347 
1348 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1349 
1350  !> Returns the frozen fraction (unitless) for whole aerosol population..
1351  !> frozen fraction = number concentration of frozen particles /
1352  !> total number concentration.
1353  real(kind=dp) function aero_state_frozen_fraction(aero_state, aero_data)
1354  !> Aerosol state.
1355  type(aero_state_t), intent(in) :: aero_state
1356  !> Aerosol data.
1357  type(aero_data_t), intent(in) :: aero_data
1358 
1359  real(kind=dp) :: particle_num_concs(aero_state_n_part(aero_state))
1360  logical :: particle_frozen(aero_state_n_part(aero_state))
1361 
1362  integer :: i_part
1363 
1364  particle_num_concs = aero_state_num_concs(aero_state, aero_data)
1365 
1366  do i_part = 1,aero_state_n_part(aero_state)
1367  particle_frozen(i_part) = aero_state%apa%particle(i_part)%frozen
1368  end do
1369 
1370  aero_state_frozen_fraction = sum(particle_num_concs, mask=particle_frozen) &
1371  / sum(particle_num_concs)
1372 
1373  end function aero_state_frozen_fraction
1374 
1375 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1376 
1377  !> Returns the number concentration of a given weight group and class.
1378  real(kind=dp) function aero_state_group_class_num_conc(aero_state, &
1379  aero_data, i_group, i_class)
1380 
1381  !> Aerosol state
1382  type(aero_state_t) :: aero_state
1383  !> Aerosol data.
1384  type(aero_data_t) :: aero_data
1385  !> Weight group.
1386  integer :: i_group
1387  !> Weight class.
1388  integer :: i_class
1389 
1390  integer :: i_part, i_entry, n_entry
1391 
1393  n_entry = integer_varray_n_entry( &
1394  aero_state%aero_sorted%group_class%inverse(i_group,i_class))
1395  do i_entry = 1,n_entry
1396  i_part = aero_state%aero_sorted%group_class%inverse(i_group, &
1397  i_class)%entry(i_entry)
1399  + aero_state_particle_num_conc(aero_state, &
1400  aero_state%apa%particle(i_part), aero_data)
1401  end do
1402 
1403  end function aero_state_group_class_num_conc
1404 
1405 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1406 
1407  !> Returns the mass-entropies of all particles.
1408  !!
1409  !! If \c include is specified then only those species are included
1410  !! in computing the entropy. If \c exclude is specified then all
1411  !! species except those species are included. If both \c include and
1412  !! \c exclude arguments are specified then only those species in \c
1413  !! include but not in \c exclude are included. If \c group is
1414  !! specified then the species are divided into two sets, given by
1415  !! those in the group and those not in the group. The entropy is
1416  !! then computed using the total mass of each set. Alternatively \c
1417  !! groups can be specified, which lists several groups of species.
1418  function aero_state_mass_entropies(aero_state, aero_data, include, exclude, &
1419  group, groups)
1420 
1421  !> Aerosol state.
1422  type(aero_state_t), intent(in) :: aero_state
1423  !> Aerosol data.
1424  type(aero_data_t), intent(in) :: aero_data
1425  !> Species names to include in the mass.
1426  character(len=*), optional :: include(:)
1427  !> Species names to exclude in the mass.
1428  character(len=*), optional :: exclude(:)
1429  !> Species names to group together.
1430  character(len=*), optional :: group(:)
1431  !> Sets of species names to group together.
1432  character(len=*), optional :: groups(:,:)
1433 
1434  !> Return value.
1435  real(kind=dp) :: aero_state_mass_entropies(aero_state_n_part(aero_state))
1436 
1437  logical :: use_species(aero_data_n_spec(aero_data))
1438  logical :: group_species(aero_data_n_spec(aero_data))
1439  integer :: i_name, i_spec, i_part, i_group, n_group
1440  integer :: species_group_numbers(aero_data_n_spec(aero_data))
1441  real(kind=dp) :: group_mass, non_group_mass, mass
1442  real(kind=dp), allocatable :: group_masses(:)
1443 
1444  if (present(include)) then
1445  use_species = .false.
1446  do i_name = 1, size(include)
1447  i_spec = aero_data_spec_by_name(aero_data, include(i_name))
1448  call assert_msg(890212002, i_spec > 0, &
1449  "unknown species: " // trim(include(i_name)))
1450  use_species(i_spec) = .true.
1451  end do
1452  else
1453  use_species = .true.
1454  end if
1455  if (present(exclude)) then
1456  do i_name = 1, size(exclude)
1457  i_spec = aero_data_spec_by_name(aero_data, exclude(i_name))
1458  call assert_msg(859945006, i_spec > 0, &
1459  "unknown species: " // trim(exclude(i_name)))
1460  use_species(i_spec) = .false.
1461  end do
1462  end if
1463  if (present(group)) then
1464  group_species = .false.
1465  do i_name = 1, size(group)
1466  i_spec = aero_data_spec_by_name(aero_data, group(i_name))
1467  call assert_msg(376359046, i_spec > 0, &
1468  "unknown species: " // trim(group(i_name)))
1469  group_species(i_spec) = .true.
1470  end do
1471  do i_part = 1,aero_state_n_part(aero_state)
1472  group_mass = 0d0
1473  non_group_mass = 0d0
1474  do i_spec = 1,aero_data_n_spec(aero_data)
1475  if (use_species(i_spec)) then
1476  mass = aero_particle_species_mass( &
1477  aero_state%apa%particle(i_part), i_spec, aero_data)
1478  if (group_species(i_spec)) then
1479  group_mass = group_mass + mass
1480  else
1481  non_group_mass = non_group_mass + mass
1482  end if
1483  end if
1484  end do
1485  aero_state_mass_entropies(i_part) &
1486  = entropy([group_mass, non_group_mass])
1487  end do
1488  else if (present(groups)) then
1489  call assert_msg(161633285, .not. present(include), &
1490  "cannot specify both 'include' and 'groups' arguments")
1491  call assert_msg(273540426, .not. present(exclude), &
1492  "cannot specify both 'exclude' and 'groups' arguments")
1493  call assert_msg(499993914, .not. present(group), &
1494  "cannot specify both 'group' and 'groups' arguments")
1495 
1496  n_group = size(groups, 1)
1497  ! species_group_numbers(i_spec) will give the group number for
1498  ! each species, or zero for non-included
1499  species_group_numbers = 0 ! extra species go into zero (unuesd) group
1500  do i_group = 1, n_group
1501  do i_name = 1, size(groups, 2)
1502  if (len_trim(groups(i_group, i_name)) > 0) then
1503  i_spec = aero_data_spec_by_name(aero_data, &
1504  groups(i_group, i_name))
1505  call assert_msg(926066826, i_spec > 0, &
1506  "unknown species: " // trim(groups(i_group, i_name)))
1507  if (use_species(i_spec)) then
1508  species_group_numbers(i_spec) = i_group
1509  end if
1510  end if
1511  end do
1512  end do
1513 
1514  allocate(group_masses(n_group))
1515  do i_part = 1,aero_state_n_part(aero_state)
1516  group_masses = 0d0
1517  do i_spec = 1,aero_data_n_spec(aero_data)
1518  mass = aero_particle_species_mass( &
1519  aero_state%apa%particle(i_part), i_spec, aero_data)
1520  i_group = species_group_numbers(i_spec)
1521  if (i_group > 0) then
1522  group_masses(i_group) = group_masses(i_group) + mass
1523  end if
1524  end do
1525  aero_state_mass_entropies(i_part) = entropy(group_masses)
1526  end do
1527  else
1528  do i_part = 1,aero_state_n_part(aero_state)
1529  aero_state_mass_entropies(i_part) = entropy(pack( &
1530  aero_particle_species_masses(aero_state%apa%particle(i_part), &
1531  aero_data), use_species))
1532  end do
1533  end if
1534 
1535  end function aero_state_mass_entropies
1536 
1537 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1538 
1539  !> Returns the mixing state metrics of the population.
1540  !!
1541  !! If \c include is specified then only those species are included
1542  !! in computing the entropies. If \c exclude is specified then all
1543  !! species except those species are included. If both \c include and
1544  !! \c exclude arguments are specified then only those species in \c
1545  !! include but not in \c exclude are included. If \c group is
1546  !! specified then the species are divided into two sets, given by
1547  !! those in the group and those not in the group. The entropies are
1548  !! then computed using the total mass of each set. Alternatively \c
1549  !! groups can be specified, which lists several groups of
1550  !! species. If \c groups is provided, only species explicitly listed
1551  !! will be included.
1552  subroutine aero_state_mixing_state_metrics(aero_state, aero_data, d_alpha, &
1553  d_gamma, chi, include, exclude, group, groups)
1554 
1555  !> Aerosol state.
1556  type(aero_state_t), intent(in) :: aero_state
1557  !> Aerosol data.
1558  type(aero_data_t), intent(in) :: aero_data
1559  !> Average particle diversity.
1560  real(kind=dp), intent(out) :: d_alpha
1561  !> Bulk diversity.
1562  real(kind=dp), intent(out) :: d_gamma
1563  !> Mixing state index.
1564  real(kind=dp), intent(out) :: chi
1565  !> Species names to include in the mass.
1566  character(len=*), optional :: include(:)
1567  !> Species names to exclude in the mass.
1568  character(len=*), optional :: exclude(:)
1569  !> Species names to group together.
1570  character(len=*), optional :: group(:)
1571  !> Sets of species names to group together.
1572  character(len=*), optional :: groups(:,:)
1573 
1574  real(kind=dp), allocatable :: entropies(:), entropies_of_avg_part(:)
1575  real(kind=dp), allocatable :: masses(:), num_concs(:), &
1576  num_concs_of_avg_part(:), masses_of_avg_part(:)
1577  type(aero_state_t) :: aero_state_averaged
1578  type(bin_grid_t) :: avg_bin_grid
1579 
1580  ! per-particle masses need to take groups into account
1581 
1582  if (present(groups)) then
1583  call assert_msg(726652236, .not. present(include), &
1584  "cannot specify both 'include' and 'groups' arguments")
1585  call assert_msg(891097454, .not. present(exclude), &
1586  "cannot specify both 'exclude' and 'groups' arguments")
1587  call assert_msg(938789093, .not. present(group), &
1588  "cannot specify both 'group' and 'groups' arguments")
1589  masses = aero_state_masses(aero_state, aero_data, &
1590  include=pack(groups, len_trim(groups) > 0))
1591  else
1592  masses = aero_state_masses(aero_state, aero_data, include, exclude)
1593  end if
1594 
1595  ! other per-particle properties
1596  num_concs = aero_state_num_concs(aero_state, aero_data)
1597  entropies = aero_state_mass_entropies(aero_state, aero_data, &
1598  include, exclude, group, groups)
1599 
1600  d_alpha = exp(sum(entropies * masses * num_concs) &
1601  / sum(masses * num_concs))
1602 
1603  ! per-particle properties of averaged particles
1604  call bin_grid_make(avg_bin_grid, bin_grid_type_log, 1, 1d-30, 1d10)
1605  aero_state_averaged = aero_state
1606  call aero_state_bin_average_comp(aero_state_averaged, avg_bin_grid, &
1607  aero_data)
1608  num_concs_of_avg_part = aero_state_num_concs(aero_state_averaged, &
1609  aero_data)
1610  if (present(groups)) then
1611  masses_of_avg_part = aero_state_masses(aero_state_averaged, aero_data, &
1612  include=pack(groups, len_trim(groups) > 0))
1613  else
1614  masses_of_avg_part = aero_state_masses(aero_state_averaged, aero_data, &
1615  include, exclude)
1616  end if
1617  entropies_of_avg_part = aero_state_mass_entropies(aero_state_averaged, &
1618  aero_data, include, exclude, group, groups)
1619 
1620  d_gamma = exp(sum(entropies_of_avg_part * masses_of_avg_part &
1621  * num_concs_of_avg_part) &
1622  / sum(masses_of_avg_part * num_concs_of_avg_part))
1623 
1624  chi = (d_alpha - 1) / (d_gamma - 1)
1625 
1626  end subroutine aero_state_mixing_state_metrics
1627 
1628 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1629 
1630  !> Returns the approximate critical relative humidity for all particles (1).
1631  function aero_state_approx_crit_rel_humids(aero_state, aero_data, env_state)
1632 
1633  !> Aerosol state.
1634  type(aero_state_t), intent(in) :: aero_state
1635  !> Aerosol data.
1636  type(aero_data_t), intent(in) :: aero_data
1637  !> Environment state.
1638  type(env_state_t), intent(in) :: env_state
1639 
1640  !> Return value.
1641  real(kind=dp) &
1643 
1644  integer :: i_part
1645 
1646  do i_part = 1,aero_state_n_part(aero_state)
1649  aero_state%apa%particle(i_part), aero_data, env_state)
1650  end do
1651 
1653 
1654 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1655 
1656  !> Returns the critical relative humidity for all particles (1).
1657  function aero_state_crit_rel_humids(aero_state, aero_data, env_state)
1658 
1659  !> Aerosol state.
1660  type(aero_state_t), intent(in) :: aero_state
1661  !> Aerosol data.
1662  type(aero_data_t), intent(in) :: aero_data
1663  !> Environment state.
1664  type(env_state_t), intent(in) :: env_state
1665 
1666  !> Return value.
1667  real(kind=dp) :: aero_state_crit_rel_humids(aero_state_n_part(aero_state))
1668 
1669  integer :: i_part
1670 
1671  do i_part = 1,aero_state_n_part(aero_state)
1673  aero_state%apa%particle(i_part), aero_data, env_state)
1674  end do
1675 
1676  end function aero_state_crit_rel_humids
1677 
1678 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1679 
1680  !> Does the same thing as aero_state_to_bin() but based on dry radius.
1681  subroutine aero_state_to_binned_dry(bin_grid, aero_data, aero_state, &
1682  aero_binned)
1683 
1684  !> Bin grid.
1685  type(bin_grid_t), intent(in) :: bin_grid
1686  !> Aerosol data.
1687  type(aero_data_t), intent(in) :: aero_data
1688  !> Aerosol state.
1689  type(aero_state_t), intent(in) :: aero_state
1690  !> Binned distributions.
1691  type(aero_binned_t), intent(inout) :: aero_binned
1692 
1693  integer :: i_part, i_bin
1694  real(kind=dp) :: factor
1695 
1696  aero_binned%num_conc = 0d0
1697  aero_binned%vol_conc = 0d0
1698  do i_part = 1,aero_state_n_part(aero_state)
1699  i_bin = bin_grid_find(bin_grid, &
1700  aero_particle_solute_radius(aero_state%apa%particle(i_part), &
1701  aero_data))
1702  if ((i_bin < 1) .or. (i_bin > bin_grid_size(bin_grid))) then
1703  call warn_msg(503871022, "particle ID " // trim( &
1704  integer64_to_string(aero_state%apa%particle(i_part)%id)) &
1705  // " outside of bin_grid, discarding")
1706  else
1707  factor = aero_weight_array_num_conc(aero_state%awa, &
1708  aero_state%apa%particle(i_part), aero_data) &
1709  / bin_grid%widths(i_bin)
1710  aero_binned%vol_conc(i_bin,:) = aero_binned%vol_conc(i_bin,:) &
1711  + aero_state%apa%particle(i_part)%vol * factor
1712  aero_binned%num_conc(i_bin) = aero_binned%num_conc(i_bin) &
1713  + factor
1714  end if
1715  end do
1716 
1717  end subroutine aero_state_to_binned_dry
1718 
1719 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1720 
1721  !> Doubles number of particles in the given weight group.
1722  subroutine aero_state_double(aero_state, aero_data, i_group, i_class)
1723 
1724  !> Aerosol state.
1725  type(aero_state_t), intent(inout) :: aero_state
1726  !> Aerosol data.
1727  type(aero_data_t), intent(in) :: aero_data
1728  !> Weight group to double.
1729  integer, intent(in) :: i_group
1730  !> Weight class to double.
1731  integer, intent(in) :: i_class
1732 
1733  integer :: i_part
1734  type(aero_particle_t) :: aero_particle
1735 
1736  do i_part = 1,aero_state_n_part(aero_state)
1737  if ((aero_state%apa%particle(i_part)%weight_group == i_group) &
1738  .and. (aero_state%apa%particle(i_part)%weight_class == i_class)) &
1739  then
1740  aero_particle = aero_state%apa%particle(i_part)
1741  call aero_particle_new_id(aero_particle)
1742  call aero_state_add_particle(aero_state, aero_particle, aero_data)
1743  end if
1744  end do
1745  aero_state%valid_sort = .false.
1746  call aero_weight_scale(aero_state%awa%weight(i_group, i_class), 0.5d0)
1747 
1748  end subroutine aero_state_double
1749 
1750 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1751 
1752  !> Remove approximately half of the particles in the given weight group.
1753  subroutine aero_state_halve(aero_state, i_group, i_class)
1754 
1755  !> Aerosol state.
1756  type(aero_state_t), intent(inout) :: aero_state
1757  !> Weight group to halve.
1758  integer, intent(in) :: i_group
1759  !> Weight class to halve.
1760  integer, intent(in) :: i_class
1761 
1762  integer :: i_part
1763  type(aero_info_t) :: aero_info
1764  logical :: record_removal
1765 
1766  do i_part = aero_state_n_part(aero_state),1,-1
1767  if ((aero_state%apa%particle(i_part)%weight_group == i_group) &
1768  .and. (aero_state%apa%particle(i_part)%weight_class == i_class)) &
1769  then
1770  if (pmc_random() < 0.5d0) then
1771  aero_info%id = aero_state%apa%particle(i_part)%id
1772  aero_info%action = aero_info_halved
1773 #ifdef PMC_USE_WRF
1774  record_removal = .false.
1775 #else
1776  record_removal = .true.
1777 #endif
1778  call aero_state_remove_particle(aero_state, i_part, &
1779  record_removal, aero_info)
1780  end if
1781  end if
1782  end do
1783  call aero_weight_scale(aero_state%awa%weight(i_group, i_class), 2d0)
1784 
1785  end subroutine aero_state_halve
1786 
1787 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1788 
1789  !> Double or halve the particle population in each weight group to
1790  !> maintain close to \c n_part_ideal particles per process,
1791  !> allocated equally amongst the weight groups.
1792  subroutine aero_state_rebalance(aero_state, aero_data, allow_doubling, &
1793  allow_halving, initial_state_warning)
1794 
1795  !> Aerosol state.
1796  type(aero_state_t), intent(inout) :: aero_state
1797  !> Aerosol data.
1798  type(aero_data_t), intent(in) :: aero_data
1799  !> Whether to allow doubling of the population.
1800  logical, intent(in) :: allow_doubling
1801  !> Whether to allow halving of the population.
1802  logical, intent(in) :: allow_halving
1803  !> Whether to warn due to initial state doubling/halving.
1804  logical, intent(in) :: initial_state_warning
1805 
1806  integer :: i_group, i_class, n_group, n_class, global_n_part
1807 
1808  n_group = size(aero_state%awa%weight, 1)
1809  n_class = size(aero_state%awa%weight, 2)
1810 
1811  ! if we have less than half the maximum number of particles then
1812  ! double until we fill up the array
1813  if (allow_doubling) then
1814  do i_group = 1,n_group
1815  do i_class = 1,n_class
1816  global_n_part &
1817  = aero_state_total_particles_all_procs(aero_state, i_group, &
1818  i_class)
1819  do while ((real(global_n_part, kind=dp) &
1820  < aero_state%n_part_ideal(i_group, i_class) / 2d0) &
1821  .and. (global_n_part > 0))
1822  if (initial_state_warning) then
1823  call warn_msg(716882783, "doubling particles in initial " &
1824  // "condition")
1825  end if
1826  call aero_state_double(aero_state, aero_data, i_group, i_class)
1827  global_n_part &
1828  = aero_state_total_particles_all_procs(aero_state, &
1829  i_group, i_class)
1830  end do
1831  end do
1832  end do
1833  end if
1834 
1835  ! same for halving if we have too many particles
1836  if (allow_halving) then
1837  do i_group = 1,n_group
1838  do i_class = 1,n_class
1839  do while (real(aero_state_total_particles(aero_state, &
1840  i_group, i_class), kind=dp) &
1841  > aero_state%n_part_ideal(i_group, i_class) * 2d0)
1842  if (initial_state_warning) then
1843  call warn_msg(661936373, &
1844  "halving particles in initial condition")
1845  end if
1846  call aero_state_halve(aero_state, i_group, i_class)
1847  end do
1848  end do
1849  end do
1850  end if
1851 
1852  end subroutine aero_state_rebalance
1853 
1854 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1855 
1856  !> Scale the weighting of the given group/class by the given ratio,
1857  !> altering particle number as necessary to preserve the number
1858  !> concentration.
1859  subroutine aero_state_scale_weight(aero_state, aero_data, i_group, &
1860  i_class, weight_ratio, allow_doubling, allow_halving)
1861 
1862  !> Aerosol state.
1863  type(aero_state_t), intent(inout) :: aero_state
1864  !> Aerosol data.
1865  type(aero_data_t), intent(in) :: aero_data
1866  !> Weight group number.
1867  integer, intent(in) :: i_group
1868  !> Weight class number.
1869  integer, intent(in) :: i_class
1870  !> Ratio of <tt>new_weight / old_weight</tt>.
1871  real(kind=dp), intent(in) :: weight_ratio
1872  !> Whether to allow doubling of the population.
1873  logical, intent(in) :: allow_doubling
1874  !> Whether to allow halving of the population.
1875  logical, intent(in) :: allow_halving
1876 
1877  real(kind=dp) :: ratio
1878  integer :: i_part, i_remove, n_remove, i_entry, n_part
1879  type(aero_info_t) :: aero_info
1880  logical :: record_removal
1881 
1882  ! We could use the ratio < 1 case unconditionally, but that would
1883  ! have higher variance for the ratio > 1 case than the current
1884  ! scheme.
1885 
1886  call aero_state_sort(aero_state, aero_data)
1887  n_part = integer_varray_n_entry( &
1888  aero_state%aero_sorted%group_class%inverse(i_group, i_class))
1889 
1890  if ((weight_ratio > 1d0) .and. (allow_halving .or. (n_part == 0))) then
1891  call aero_weight_scale(aero_state%awa%weight(i_group, i_class), &
1892  weight_ratio)
1893  n_remove = prob_round(real(n_part, kind=dp) &
1894  * (1d0 - 1d0 / weight_ratio))
1895  do i_remove = 1,n_remove
1897  aero_state%aero_sorted%group_class%inverse(i_group, i_class)))
1898  i_part = aero_state%aero_sorted%group_class%inverse(i_group, &
1899  i_class)%entry(i_entry)
1900  aero_info%id = aero_state%apa%particle(i_part)%id
1901  aero_info%action = aero_info_halved
1902 #ifdef PMC_USE_WRF
1903  record_removal = .false.
1904 #else
1905  record_removal = .true.
1906 #endif
1907  call aero_state_remove_particle(aero_state, i_part, &
1908  record_removal, aero_info)
1909  end do
1910  elseif ((weight_ratio < 1d0) &
1911  .and. (allow_doubling .or. (n_part == 0))) then
1912  call aero_weight_scale(aero_state%awa%weight(i_group, i_class), &
1913  weight_ratio)
1914  do i_entry = n_part,1,-1
1915  i_part = aero_state%aero_sorted%group_class%inverse(i_group, &
1916  i_class)%entry(i_entry)
1917  call aero_state_dup_particle(aero_state, aero_data, i_part, &
1918  1d0 / weight_ratio)
1919  end do
1920  end if
1921 
1922  end subroutine aero_state_scale_weight
1923 
1924 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1925 
1926  !> Mix the aero_states between all processes. Currently uses a
1927  !> simple all-to-all diffusion.
1928  subroutine aero_state_mix(aero_state, del_t, mix_timescale, &
1929  aero_data, specify_prob_transfer)
1930 
1931  !> Aerosol state.
1932  type(aero_state_t), intent(inout) :: aero_state
1933  !> Timestep (s).
1934  real(kind=dp), intent(in) :: del_t
1935  !> Mixing timescale (s).
1936  real(kind=dp), intent(in) :: mix_timescale
1937  !> Aero data values.
1938  type(aero_data_t), intent(in) :: aero_data
1939  !> Transfer probability of each particle (0 means no mixing, 1
1940  !> means total mixing).
1941  real(kind=dp), optional, intent(in) :: specify_prob_transfer
1942 
1943 #ifdef PMC_USE_MPI
1944  integer :: rank, n_proc, i_proc, ierr
1945  integer :: buffer_size, buffer_size_check
1946  character, allocatable :: buffer(:)
1947  type(aero_state_t), allocatable :: aero_state_sends(:)
1948  type(aero_state_t), allocatable :: aero_state_recvs(:)
1949  real(kind=dp) :: prob_transfer, prob_not_transferred
1950  real(kind=dp) :: prob_transfer_given_not_transferred
1951 
1952  ! process information
1953  rank = pmc_mpi_rank()
1954  n_proc = pmc_mpi_size()
1955  if (n_proc == 1) then
1956  ! buffer allocation below fails if n_proc == 1
1957  ! so bail out early (nothing to mix anyway)
1958  return
1959  end if
1960 
1961  ! allocate aero_state arrays
1962  allocate(aero_state_sends(n_proc))
1963  allocate(aero_state_recvs(n_proc))
1964 
1965  ! compute the transfer probability
1966  if (present(specify_prob_transfer)) then
1967  prob_transfer = specify_prob_transfer / real(n_proc, kind=dp)
1968  else
1969  prob_transfer = (1d0 - exp(- del_t / mix_timescale)) &
1970  / real(n_proc, kind=dp)
1971  end if
1972 
1973  ! extract particles to send
1974  prob_not_transferred = 1d0
1975  do i_proc = 0,(n_proc - 1)
1976  if (i_proc /= rank) then
1977  ! because we are doing sequential sampling from the aero_state
1978  ! we need to scale up the later transfer probabilities, because
1979  ! the later particles are being transferred conditioned on the
1980  ! fact that they did not transfer already
1981  prob_transfer_given_not_transferred = prob_transfer &
1982  / prob_not_transferred
1983  call aero_state_sample(aero_state, &
1984  aero_state_sends(i_proc + 1), aero_data, &
1985  prob_transfer_given_not_transferred, aero_info_none)
1986  prob_not_transferred = prob_not_transferred - prob_transfer
1987  end if
1988  end do
1989 
1990  ! exchange the particles
1991  call aero_state_mpi_alltoall(aero_state_sends, aero_state_recvs)
1992 
1993  ! process the received particles
1994  do i_proc = 0,(n_proc - 1)
1995  if (i_proc /= rank) then
1996  call aero_state_add(aero_state, aero_state_recvs(i_proc + 1), &
1997  aero_data)
1998  end if
1999  end do
2000 
2001  ! cleanup
2002  deallocate(aero_state_sends)
2003  deallocate(aero_state_recvs)
2004 #endif
2005 
2006  end subroutine aero_state_mix
2007 
2008 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2009 
2010  !> Do an MPI all-to-all transfer of aerosol states.
2011  !!
2012  !! States without particles are not sent.
2013  subroutine aero_state_mpi_alltoall(send, recv)
2014 
2015  !> Array of aero_states to send (one per process).
2016  type(aero_state_t), intent(in) :: send(:)
2017  !> Array of aero_states to receives (one per process).
2018  type(aero_state_t), intent(inout) :: recv(size(send))
2019 
2020 #ifdef PMC_USE_MPI
2021  character, allocatable :: sendbuf(:), recvbuf(:)
2022  integer :: sendcounts(size(send)), sdispls(size(send))
2023  integer :: recvcounts(size(send)), rdispls(size(send))
2024  integer :: i_proc, position, old_position, max_sendbuf_size, ierr
2025 
2026  call assert(978709842, size(send) == pmc_mpi_size())
2027 
2028  max_sendbuf_size = 0
2029  do i_proc = 1,pmc_mpi_size()
2030  if (aero_state_total_particles(send(i_proc)) > 0) then
2031  max_sendbuf_size = max_sendbuf_size &
2032  + pmc_mpi_pack_size_aero_state(send(i_proc))
2033  end if
2034  end do
2035 
2036  allocate(sendbuf(max_sendbuf_size))
2037 
2038  position = 0
2039  do i_proc = 1,pmc_mpi_size()
2040  old_position = position
2041  if (aero_state_total_particles(send(i_proc)) > 0) then
2042  call pmc_mpi_pack_aero_state(sendbuf, position, send(i_proc))
2043  end if
2044  sendcounts(i_proc) = position - old_position
2045  end do
2046  call assert(393267406, position <= max_sendbuf_size)
2047 
2048  call pmc_mpi_alltoall_integer(sendcounts, recvcounts)
2049  allocate(recvbuf(sum(recvcounts)))
2050 
2051  sdispls(1) = 0
2052  rdispls(1) = 0
2053  do i_proc = 2,pmc_mpi_size()
2054  sdispls(i_proc) = sdispls(i_proc - 1) + sendcounts(i_proc - 1)
2055  rdispls(i_proc) = rdispls(i_proc - 1) + recvcounts(i_proc - 1)
2056  end do
2057 
2058  call mpi_alltoallv(sendbuf, sendcounts, sdispls, mpi_character, recvbuf, &
2059  recvcounts, rdispls, mpi_character, mpi_comm_world, ierr)
2060  call pmc_mpi_check_ierr(ierr)
2061 
2062  position = 0
2063  do i_proc = 1,pmc_mpi_size()
2064  call assert(189739257, position == rdispls(i_proc))
2065  if (recvcounts(i_proc) > 0) then
2066  call pmc_mpi_unpack_aero_state(recvbuf, position, recv(i_proc))
2067  end if
2068  end do
2069 
2070  deallocate(sendbuf)
2071  deallocate(recvbuf)
2072 #endif
2073 
2074  end subroutine aero_state_mpi_alltoall
2075 
2076 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2077 
2078  !> Set each aerosol particle to have its original total volume, but
2079  !> species volume ratios given by the total species volume ratio
2080  !> within each bin. This preserves the (weighted) total species
2081  !> volume per bin as well as per-particle total volumes.
2082  subroutine aero_state_bin_average_comp(aero_state, bin_grid, aero_data)
2083 
2084  !> Aerosol state to average.
2085  type(aero_state_t), intent(inout) :: aero_state
2086  !> Bin grid to average within.
2087  type(bin_grid_t), intent(in) :: bin_grid
2088  !> Aerosol data.
2089  type(aero_data_t), intent(in) :: aero_data
2090 
2091  real(kind=dp) :: species_volume_conc(aero_data_n_spec(aero_data))
2092  real(kind=dp) :: total_volume_conc, particle_volume, num_conc
2093  integer :: i_bin, i_class, i_entry, i_part, i_spec
2094 
2095  call aero_state_sort(aero_state, aero_data, bin_grid)
2096 
2097  do i_bin = 1,bin_grid_size(bin_grid)
2098  species_volume_conc = 0d0
2099  total_volume_conc = 0d0
2100  do i_class = 1,size(aero_state%awa%weight, 2)
2101  do i_entry = 1,integer_varray_n_entry( &
2102  aero_state%aero_sorted%size_class%inverse(i_bin, i_class))
2103  i_part = aero_state%aero_sorted%size_class%inverse(i_bin, &
2104  i_class)%entry(i_entry)
2105  num_conc = aero_weight_array_num_conc(aero_state%awa, &
2106  aero_state%apa%particle(i_part), aero_data)
2107  particle_volume = aero_particle_volume( &
2108  aero_state%apa%particle(i_part))
2109  species_volume_conc = species_volume_conc &
2110  + num_conc * aero_state%apa%particle(i_part)%vol
2111  total_volume_conc = total_volume_conc + num_conc * particle_volume
2112  end do
2113  end do
2114  do i_class = 1,size(aero_state%awa%weight, 2)
2115  do i_entry = 1,integer_varray_n_entry( &
2116  aero_state%aero_sorted%size_class%inverse(i_bin, i_class))
2117  i_part = aero_state%aero_sorted%size_class%inverse(i_bin, &
2118  i_class)%entry(i_entry)
2119  particle_volume = aero_particle_volume( &
2120  aero_state%apa%particle(i_part))
2121  aero_state%apa%particle(i_part)%vol &
2122  = particle_volume * species_volume_conc / total_volume_conc
2123  end do
2124  end do
2125  end do
2126 
2127  end subroutine aero_state_bin_average_comp
2128 
2129 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2130 
2131  !> Set each aerosol particle to have its original species ratios,
2132  !> but total volume given by the average volume of all particles
2133  !> within each bin.
2134  !!
2135  !! This does not preserve the total species volume
2136  !! per bin. If the \c bin_center parameter is \c .true. then the
2137  !! particles in each bin are set to have the bin center volume,
2138  !! rather than the average volume of the particles in that bin.
2139  !!
2140  !! If the weighting function is not constant (AERO_WEIGHT_TYPE_NONE)
2141  !! then the averaging can be performed in either a number-preserving
2142  !! way or in a volume-preserving way. The volume-preserving way does
2143  !! not preserve species volume ratios in gernal, but will do so if
2144  !! the particle population has already been composition-averaged.
2145  subroutine aero_state_bin_average_size(aero_state, bin_grid, aero_data, &
2146  bin_center, preserve_number)
2147 
2148  !> Aerosol state to average.
2149  type(aero_state_t), intent(inout) :: aero_state
2150  !> Bin grid to average within.
2151  type(bin_grid_t), intent(in) :: bin_grid
2152  !> Aerosol data.
2153  type(aero_data_t), intent(in) :: aero_data
2154  !> Whether to assign the bin center volume (rather than the average
2155  !> volume).
2156  logical, intent(in) :: bin_center
2157  !> Whether to use the number-preserving scheme (otherwise will use
2158  !> the volume-preserving scheme). This parameter has no effect if
2159  !> \c bin_center is \c .true.
2160  logical, intent(in) :: preserve_number
2161 
2162  real(kind=dp) :: total_volume_conc, particle_volume
2163  real(kind=dp) :: new_particle_volume, num_conc, total_num_conc
2164  real(kind=dp) :: lower_volume, upper_volume, center_volume
2165  real(kind=dp) :: lower_function, upper_function, center_function
2166  integer :: i_bin, i_class, i_entry, i_part, i_bisect, n_part
2167  logical :: monotone_increasing, monotone_decreasing
2168 
2169  call aero_state_sort(aero_state, aero_data, bin_grid)
2170 
2171  do i_bin = 1,bin_grid_size(bin_grid)
2172  do i_class = 1,size(aero_state%awa%weight, 2)
2173  if (integer_varray_n_entry( &
2174  aero_state%aero_sorted%size_class%inverse(i_bin, i_class)) &
2175  == 0) then
2176  cycle
2177  end if
2178 
2179  n_part = integer_varray_n_entry( &
2180  aero_state%aero_sorted%size_class%inverse(i_bin, i_class))
2181  total_num_conc = 0d0
2182  total_volume_conc = 0d0
2183  do i_entry = 1,integer_varray_n_entry( &
2184  aero_state%aero_sorted%size_class%inverse(i_bin, i_class))
2185  i_part = aero_state%aero_sorted%size_class%inverse(i_bin, &
2186  i_class)%entry(i_entry)
2187  num_conc = aero_weight_array_num_conc(aero_state%awa, &
2188  aero_state%apa%particle(i_part), aero_data)
2189  total_num_conc = total_num_conc + num_conc
2190  particle_volume = aero_particle_volume( &
2191  aero_state%apa%particle(i_part))
2192  total_volume_conc = total_volume_conc &
2193  + num_conc * particle_volume
2194  end do
2195 
2196  ! determine the new_particle_volume for all particles in this bin
2197  if (bin_center) then
2198  new_particle_volume = aero_data_rad2vol(aero_data, &
2199  bin_grid%centers(i_bin))
2200  elseif (aero_weight_array_check_flat(aero_state%awa)) then
2201  num_conc & ! any radius will have the same num_conc
2202  = aero_weight_array_num_conc_at_radius(aero_state%awa, &
2203  i_class, 1d0)
2204  new_particle_volume = total_volume_conc / num_conc &
2205  / real(integer_varray_n_entry( &
2206  aero_state%aero_sorted%size_class%inverse(i_bin, i_class)), &
2207  kind=dp)
2208  elseif (preserve_number) then
2209  ! number-preserving scheme: Solve the implicit equation:
2210  ! n_part * W(new_vol) = total_num_conc
2211  !
2212  ! We assume that the weighting function is strictly monotone
2213  ! so this equation has a unique solution and the solution
2214  ! lies between the min and max particle volumes. We use
2215  ! bisection as this doesn't really need to be fast, just
2216  ! robust.
2217 
2218  call aero_weight_array_check_monotonicity(aero_state%awa, &
2219  monotone_increasing, monotone_decreasing)
2220  call assert_msg(214077200, &
2221  monotone_increasing .or. monotone_decreasing, &
2222  "monotone weight function required for averaging")
2223 
2224  ! initialize to min and max particle volumes
2225  do i_entry = 1,n_part
2226  i_part = aero_state%aero_sorted%size_class%inverse(i_bin, &
2227  i_class)%entry(i_entry)
2228  particle_volume = aero_particle_volume( &
2229  aero_state%apa%particle(i_part))
2230  if (i_part == 1) then
2231  lower_volume = particle_volume
2232  upper_volume = particle_volume
2233  end if
2234  lower_volume = min(lower_volume, particle_volume)
2235  upper_volume = max(upper_volume, particle_volume)
2236  end do
2237 
2238  lower_function = real(n_part, kind=dp) &
2239  * aero_weight_array_num_conc_at_radius(aero_state%awa, &
2240  i_class, aero_data_vol2rad(aero_data, lower_volume)) &
2241  - total_num_conc
2242  upper_function = real(n_part, kind=dp) &
2243  * aero_weight_array_num_conc_at_radius(aero_state%awa, &
2244  i_class, aero_data_vol2rad(aero_data, upper_volume)) &
2245  - total_num_conc
2246 
2247  ! do 50 rounds of bisection (2^50 = 10^15)
2248  do i_bisect = 1,50
2249  center_volume = (lower_volume + upper_volume) / 2d0
2250  center_function = real(n_part, kind=dp) &
2251  * aero_weight_array_num_conc_at_radius(aero_state%awa, &
2252  i_class, aero_data_vol2rad(aero_data, center_volume)) &
2253  - total_num_conc
2254  if ((lower_function > 0d0 .and. center_function > 0d0) &
2255  .or. (lower_function < 0d0 .and. center_function < 0d0)) &
2256  then
2257  lower_volume = center_volume
2258  lower_function = center_function
2259  else
2260  upper_volume = center_volume
2261  upper_function = center_function
2262  end if
2263  end do
2264 
2265  new_particle_volume = center_volume
2266  else
2267  ! volume-preserving scheme: Solve the implicit equation:
2268  ! n_part * W(new_vol) * new_vol = total_volume_conc
2269  !
2270  ! We assume that the weighting function is strictly monotone
2271  ! so this equation has a unique solution and the solution
2272  ! lies between the min and max particle volumes. We use
2273  ! bisection as this doesn't really need to be fast, just
2274  ! robust.
2275 
2276  call aero_weight_array_check_monotonicity(aero_state%awa, &
2277  monotone_increasing, monotone_decreasing)
2278  call assert_msg(483078128, &
2279  monotone_increasing .or. monotone_decreasing, &
2280  "monotone weight function required for averaging")
2281 
2282  ! initialize to min and max particle volumes
2283  do i_entry = 1,n_part
2284  i_part = aero_state%aero_sorted%size_class%inverse(i_bin, &
2285  i_class)%entry(i_entry)
2286  particle_volume = aero_particle_volume( &
2287  aero_state%apa%particle(i_part))
2288  if (i_part == 1) then
2289  lower_volume = particle_volume
2290  upper_volume = particle_volume
2291  end if
2292  lower_volume = min(lower_volume, particle_volume)
2293  upper_volume = max(upper_volume, particle_volume)
2294  end do
2295 
2296  lower_function = real(n_part, kind=dp) &
2298  aero_state%awa, i_class, aero_data_vol2rad(aero_data, &
2299  lower_volume)) * lower_volume - total_volume_conc
2300  upper_function = real(n_part, kind=dp) &
2302  aero_state%awa, i_class, aero_data_vol2rad(aero_data, &
2303  upper_volume)) * upper_volume - total_volume_conc
2304 
2305  ! do 50 rounds of bisection (2^50 = 10^15)
2306  do i_bisect = 1,50
2307  center_volume = (lower_volume + upper_volume) / 2d0
2308  center_function = real(n_part, kind=dp) &
2310  aero_state%awa, i_class, aero_data_vol2rad(aero_data, &
2311  center_volume)) * center_volume - total_volume_conc
2312  if ((lower_function > 0d0 .and. center_function > 0d0) &
2313  .or. (lower_function < 0d0 .and. center_function < 0d0)) &
2314  then
2315  lower_volume = center_volume
2316  lower_function = center_function
2317  else
2318  upper_volume = center_volume
2319  upper_function = center_function
2320  end if
2321  end do
2322 
2323  new_particle_volume = center_volume
2324  end if
2325 
2326  do i_entry = 1,n_part
2327  i_part = aero_state%aero_sorted%size_class%inverse(i_bin, &
2328  i_class)%entry(i_entry)
2329  particle_volume = aero_particle_volume( &
2330  aero_state%apa%particle(i_part))
2331  aero_state%apa%particle(i_part)%vol &
2332  = aero_state%apa%particle(i_part)%vol &
2333  / particle_volume * new_particle_volume
2334  end do
2335  end do
2336  end do
2337 
2338  end subroutine aero_state_bin_average_size
2339 
2340 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2341 
2342  !> Make all particles dry (water set to zero).
2343  subroutine aero_state_make_dry(aero_state, aero_data)
2344 
2345  !> Aerosol state to make dry.
2346  type(aero_state_t), intent(inout) :: aero_state
2347  !> Aerosol data.
2348  type(aero_data_t), intent(in) :: aero_data
2349 
2350  integer :: i_part
2351  real(kind=dp) :: reweight_num_conc(aero_state%apa%n_part)
2352 
2353  ! We're modifying particle diameters, so bin sorting is now invalid
2354  aero_state%valid_sort = .false.
2355 
2356  call aero_state_num_conc_for_reweight(aero_state, aero_data, &
2357  reweight_num_conc)
2358  if (aero_data%i_water > 0) then
2359  do i_part = 1,aero_state_n_part(aero_state)
2360  aero_state%apa%particle(i_part)%vol(aero_data%i_water) = 0d0
2361  end do
2362  aero_state%valid_sort = .false.
2363  end if
2364  ! adjust particles to account for weight changes
2365  call aero_state_reweight(aero_state, aero_data, reweight_num_conc)
2366 
2367  end subroutine aero_state_make_dry
2368 
2369 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2370 
2371  !> Determines the number of bytes required to pack the given value.
2372  integer function pmc_mpi_pack_size_aero_state(val)
2373 
2374  !> Value to pack.
2375  type(aero_state_t), intent(in) :: val
2376 
2377  integer :: total_size, i_group
2378 
2379  total_size = 0
2380  total_size = total_size + pmc_mpi_pack_size_apa(val%apa)
2381  total_size = total_size + pmc_mpi_pack_size_aero_weight_array(val%awa)
2382  total_size = total_size + pmc_mpi_pack_size_real_array_2d(val%n_part_ideal)
2383  total_size = total_size + pmc_mpi_pack_size_aia(val%aero_info_array)
2384  pmc_mpi_pack_size_aero_state = total_size
2385 
2386  end function pmc_mpi_pack_size_aero_state
2387 
2388 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2389 
2390  !> Packs the given value into the buffer, advancing position.
2391  subroutine pmc_mpi_pack_aero_state(buffer, position, val)
2392 
2393  !> Memory buffer.
2394  character, intent(inout) :: buffer(:)
2395  !> Current buffer position.
2396  integer, intent(inout) :: position
2397  !> Value to pack.
2398  type(aero_state_t), intent(in) :: val
2399 
2400 #ifdef PMC_USE_MPI
2401  integer :: prev_position, i_group
2402 
2403  prev_position = position
2404  call pmc_mpi_pack_aero_particle_array(buffer, position, val%apa)
2405  call pmc_mpi_pack_aero_weight_array(buffer,position,val%awa)
2406  call pmc_mpi_pack_real_array_2d(buffer, position, val%n_part_ideal)
2407  call pmc_mpi_pack_aero_info_array(buffer, position, val%aero_info_array)
2408  call assert(850997402, &
2409  position - prev_position <= pmc_mpi_pack_size_aero_state(val))
2410 #endif
2411 
2412  end subroutine pmc_mpi_pack_aero_state
2413 
2414 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2415 
2416  !> Unpacks the given value from the buffer, advancing position.
2417  subroutine pmc_mpi_unpack_aero_state(buffer, position, val)
2418 
2419  !> Memory buffer.
2420  character, intent(inout) :: buffer(:)
2421  !> Current buffer position.
2422  integer, intent(inout) :: position
2423  !> Value to pack.
2424  type(aero_state_t), intent(inout) :: val
2425 
2426 #ifdef PMC_USE_MPI
2427  integer :: prev_position, i_group, n_group
2428 
2429  val%valid_sort = .false.
2430  prev_position = position
2431  call pmc_mpi_unpack_aero_particle_array(buffer, position, val%apa)
2432  call pmc_mpi_unpack_aero_weight_array(buffer,position,val%awa)
2433  call pmc_mpi_unpack_real_array_2d(buffer, position, val%n_part_ideal)
2434  call pmc_mpi_unpack_aero_info_array(buffer, position, val%aero_info_array)
2435  call assert(132104747, &
2436  position - prev_position <= pmc_mpi_pack_size_aero_state(val))
2437 #endif
2438 
2439  end subroutine pmc_mpi_unpack_aero_state
2440 
2441 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2442 
2443  !> Gathers data from all processes into one aero_state on process 0.
2444  subroutine aero_state_mpi_gather(aero_state, aero_state_total, aero_data)
2445 
2446  !> Local aero_state.
2447  type(aero_state_t), intent(in) :: aero_state
2448  !> Centralized aero_state (only on process 0).
2449  type(aero_state_t), intent(inout) :: aero_state_total
2450  !> Aero data values.
2451  type(aero_data_t), intent(in) :: aero_data
2452 
2453 #ifdef PMC_USE_MPI
2454  type(aero_state_t) :: aero_state_transfer
2455  integer :: n_proc, ierr, status(MPI_STATUS_SIZE)
2456  integer :: buffer_size, max_buffer_size, i_proc, position
2457  character, allocatable :: buffer(:)
2458 #endif
2459 
2460  if (pmc_mpi_rank() == 0) then
2461  aero_state_total = aero_state
2462  end if
2463 
2464 #ifdef PMC_USE_MPI
2465 
2466  if (pmc_mpi_rank() /= 0) then
2467  ! send data from remote processes
2468  max_buffer_size = 0
2469  max_buffer_size = max_buffer_size &
2470  + pmc_mpi_pack_size_aero_state(aero_state)
2471  allocate(buffer(max_buffer_size))
2472  position = 0
2473  call pmc_mpi_pack_aero_state(buffer, position, aero_state)
2474  call assert(542772170, position <= max_buffer_size)
2475  buffer_size = position
2476  call mpi_send(buffer, buffer_size, mpi_character, 0, &
2477  aero_state_tag_gather, mpi_comm_world, ierr)
2478  call pmc_mpi_check_ierr(ierr)
2479  deallocate(buffer)
2480  else
2481  ! root process receives data from each remote proc
2482  n_proc = pmc_mpi_size()
2483  do i_proc = 1,(n_proc - 1)
2484  ! determine buffer size at root process
2485  call mpi_probe(i_proc, aero_state_tag_gather, mpi_comm_world, &
2486  status, ierr)
2487  call pmc_mpi_check_ierr(ierr)
2488  call mpi_get_count(status, mpi_character, buffer_size, ierr)
2489  call pmc_mpi_check_ierr(ierr)
2490 
2491  ! get buffer at root process
2492  allocate(buffer(buffer_size))
2493  call mpi_recv(buffer, buffer_size, mpi_character, i_proc, &
2494  aero_state_tag_gather, mpi_comm_world, status, ierr)
2495 
2496  ! unpack it
2497  call aero_state_zero(aero_state_transfer)
2498  position = 0
2499  call pmc_mpi_unpack_aero_state(buffer, position, &
2500  aero_state_transfer)
2501  call assert(518174881, position == buffer_size)
2502  deallocate(buffer)
2503 
2504  call aero_state_add(aero_state_total, aero_state_transfer, &
2505  aero_data)
2506  end do
2507  end if
2508 
2509 #endif
2510 
2511  end subroutine aero_state_mpi_gather
2512 
2513 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2514 
2515  !> Write the aero particle dimension to the given NetCDF file if it
2516  !> is not already present and in any case return the associated
2517  !> dimid.
2518  subroutine aero_state_netcdf_dim_aero_particle(aero_state, ncid, &
2519  dimid_aero_particle)
2520 
2521  !> aero_state structure.
2522  type(aero_state_t), intent(in) :: aero_state
2523  !> NetCDF file ID, in data mode.
2524  integer, intent(in) :: ncid
2525  !> Dimid of the aero particle dimension.
2526  integer, intent(out) :: dimid_aero_particle
2527 
2528  integer :: status, i_part
2529  integer :: varid_aero_particle
2530  integer :: aero_particle_centers(aero_state_n_part(aero_state))
2531 
2532  ! try to get the dimension ID
2533  status = nf90_inq_dimid(ncid, "aero_particle", dimid_aero_particle)
2534  if (status == nf90_noerr) return
2535  if (status /= nf90_ebaddim) call pmc_nc_check(status)
2536 
2537  ! dimension not defined, so define now define it
2538  call pmc_nc_check(nf90_redef(ncid))
2539 
2540  call pmc_nc_check(nf90_def_dim(ncid, "aero_particle", &
2541  aero_state_n_part(aero_state), dimid_aero_particle))
2542 
2543  call pmc_nc_check(nf90_enddef(ncid))
2544 
2545  do i_part = 1,aero_state_n_part(aero_state)
2546  aero_particle_centers(i_part) = i_part
2547  end do
2548  call pmc_nc_write_integer_1d(ncid, aero_particle_centers, &
2549  "aero_particle", (/ dimid_aero_particle /), &
2550  description="dummy dimension variable (no useful value)")
2551 
2553 
2554 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2555 
2556  !> Write the aero removed dimension to the given NetCDF file if it
2557  !> is not already present and in any case return the associated
2558  !> dimid.
2559  subroutine aero_state_netcdf_dim_aero_removed(aero_state, ncid, &
2560  dimid_aero_removed)
2561 
2562  !> aero_state structure.
2563  type(aero_state_t), intent(in) :: aero_state
2564  !> NetCDF file ID, in data mode.
2565  integer, intent(in) :: ncid
2566  !> Dimid of the aero removed dimension.
2567  integer, intent(out) :: dimid_aero_removed
2568 
2569  integer :: status, i_remove, dim_size
2570  integer :: varid_aero_removed
2571  integer :: aero_removed_centers( &
2572  max(1, aero_info_array_n_item(aero_state%aero_info_array)))
2573 
2574  ! try to get the dimension ID
2575  status = nf90_inq_dimid(ncid, "aero_removed", dimid_aero_removed)
2576  if (status == nf90_noerr) return
2577  if (status /= nf90_ebaddim) call pmc_nc_check(status)
2578 
2579  ! dimension not defined, so define now define it
2580  call pmc_nc_check(nf90_redef(ncid))
2581 
2582  dim_size = max(1, aero_info_array_n_item(aero_state%aero_info_array))
2583  call pmc_nc_check(nf90_def_dim(ncid, "aero_removed", &
2584  dim_size, dimid_aero_removed))
2585 
2586  call pmc_nc_check(nf90_enddef(ncid))
2587 
2588  do i_remove = 1,dim_size
2589  aero_removed_centers(i_remove) = i_remove
2590  end do
2591  call pmc_nc_write_integer_1d(ncid, aero_removed_centers, &
2592  "aero_removed", (/ dimid_aero_removed /), &
2593  description="dummy dimension variable (no useful value)")
2594 
2595  end subroutine aero_state_netcdf_dim_aero_removed
2596 
2597 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2598 
2599  !> Write the aero component dimension to the given NetCDF file if it
2600  !> is not already present and in any case return the associated
2601  !> dimid.
2602  subroutine aero_state_netcdf_dim_aero_components(aero_state, ncid, &
2603  dimid_aero_components)
2604 
2605  !> Aero_state structure.
2606  type(aero_state_t), intent(in) :: aero_state
2607  !> NetCDF file ID, in data mode.
2608  integer, intent(in) :: ncid
2609  !> Dimid of the aero component dimension.
2610  integer, intent(out) :: dimid_aero_components
2611 
2612  integer :: status, dim_size
2613 
2614  ! try to get the dimension ID
2615  status = nf90_inq_dimid(ncid, "aero_components", dimid_aero_components)
2616  if (status == nf90_noerr) return
2617  if (status /= nf90_ebaddim) call pmc_nc_check(status)
2618 
2619  ! dimension not defined, so define now define it
2620  call pmc_nc_check(nf90_redef(ncid))
2621 
2622  dim_size = aero_state_total_n_components(aero_state)
2623  call pmc_nc_check(nf90_def_dim(ncid, "aero_components", &
2624  dim_size, dimid_aero_components))
2625 
2626  call pmc_nc_check(nf90_enddef(ncid))
2627 
2629 
2630 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2631 
2632  !> Write full state.
2633  subroutine aero_state_output_netcdf(aero_state, ncid, aero_data, &
2634  record_removals, record_optical)
2635 
2636  !> aero_state to write.
2637  type(aero_state_t), intent(in) :: aero_state
2638  !> NetCDF file ID, in data mode.
2639  integer, intent(in) :: ncid
2640  !> aero_data structure.
2641  type(aero_data_t), intent(in) :: aero_data
2642  !> Whether to output particle removal info.
2643  logical, intent(in) :: record_removals
2644  !> Whether to output aerosol optical properties.
2645  logical, intent(in) :: record_optical
2646 
2647  integer :: dimid_aero_particle, dimid_aero_species, dimid_aero_source
2648  integer :: dimid_aero_removed
2649  integer :: dimid_aero_components
2650  integer :: dimid_optical_wavelengths
2651  integer :: i_part, i_remove
2652  real(kind=dp) :: aero_particle_mass(aero_state_n_part(aero_state), &
2653  aero_data_n_spec(aero_data))
2654  integer :: aero_particle_weight_group(aero_state_n_part(aero_state))
2655  integer :: aero_particle_weight_class(aero_state_n_part(aero_state))
2656  real(kind=dp) :: aero_absorb_cross_sect(aero_state_n_part(aero_state), &
2657  n_swbands)
2658  real(kind=dp) :: aero_scatter_cross_sect(aero_state_n_part(aero_state), &
2659  n_swbands)
2660  real(kind=dp) :: aero_asymmetry(aero_state_n_part(aero_state), n_swbands)
2661  real(kind=dp) :: aero_refract_shell_real(aero_state_n_part(aero_state), &
2662  n_swbands)
2663  real(kind=dp) :: aero_refract_shell_imag(aero_state_n_part(aero_state), &
2664  n_swbands)
2665  real(kind=dp) :: aero_refract_core_real(aero_state_n_part(aero_state), &
2666  n_swbands)
2667  real(kind=dp) :: aero_refract_core_imag(aero_state_n_part(aero_state), &
2668  n_swbands)
2669  real(kind=dp) :: aero_core_vol(aero_state_n_part(aero_state))
2670  integer :: aero_water_hyst_leg(aero_state_n_part(aero_state))
2671  real(kind=dp) :: aero_num_conc(aero_state_n_part(aero_state))
2672  integer(kind=8) :: aero_id(aero_state_n_part(aero_state))
2673  integer :: aero_frozen(aero_state_n_part(aero_state))
2674  real(kind=dp) :: aero_imf_temperature(aero_state_n_part(aero_state))
2675  real(kind=dp) :: aero_ice_density(aero_state_n_part(aero_state))
2676  real(kind=dp) :: aero_ice_shape_phi(aero_state_n_part(aero_state))
2677  real(kind=dp) :: aero_least_create_time(aero_state_n_part(aero_state))
2678  real(kind=dp) :: aero_greatest_create_time(aero_state_n_part(aero_state))
2679  integer(kind=8) :: aero_removed_id( &
2680  max(1, aero_info_array_n_item(aero_state%aero_info_array)))
2681  integer :: aero_removed_action( &
2682  max(1, aero_info_array_n_item(aero_state%aero_info_array)))
2683  integer(kind=8) :: aero_removed_other_id( &
2684  max(1, aero_info_array_n_item(aero_state%aero_info_array)))
2685  integer :: aero_component_particle_num(aero_state_total_n_components( &
2686  aero_state))
2687  integer :: aero_component_source_num(aero_state_total_n_components( &
2688  aero_state))
2689  real(kind=dp) :: aero_component_create_time( &
2690  aero_state_total_n_components(aero_state))
2691  integer :: aero_component_len(aero_state_n_part(aero_state))
2692  integer :: aero_component_start_ind(aero_state_n_part(aero_state))
2693  integer :: aero_particle_n_primary_parts(aero_state_n_part(aero_state))
2694  integer :: array_position, i_comp, next_start_component_ind
2695 
2696  !> \page output_format_aero_state Output File Format: Aerosol Particle State
2697  !!
2698  !! The aerosol state consists of a set of individual aerosol
2699  !! particles, each with its own individual properties. The
2700  !! properties of all particles are stored in arrays, one per
2701  !! property. For example, <tt>aero_absorb_cross_sect(i)</tt> gives
2702  !! the absorption cross section of particle number \c i, while
2703  !! <tt>aero_particle_mass(i,s)</tt> gives the mass of species \c s
2704  !! in particle \c i. The aerosol species are described in \ref
2705  !! output_format_aero_data.
2706  !!
2707  !! Each aerosol particle \c i represents a number concentration
2708  !! given by <tt>aero_num_conc(i)</tt>. Multiplying a per-particle
2709  !! quantity by the respective number concentration gives the
2710  !! concentration of that quantity contributed by the particle. For
2711  !! example, summing <tt>aero_particle_mass(i,s) *
2712  !! aero_num_conc(i)</tt> over all \c i gives the total mass
2713  !! concentration of species \c s in kg/m^3. Similarly, summing
2714  !! <tt>aero_absorb_cross_sect(i) * aero_num_conc(i)</tt> over all
2715  !! \c i will give the concentration of scattering cross section in
2716  !! m^2/m^3.
2717  !!
2718  !! FIXME: the aero_weight is also output
2719  !!
2720  !! The aerosol state uses the \c aero_species NetCDF dimension as
2721  !! specified in the \ref output_format_aero_data section, as well
2722  !! as the NetCDF dimension:
2723  !! - \b aero_particle: number of aerosol particles
2724  !! - \b aero_components: total number of aerosol components
2725  !!
2726  !! The aerosol state NetCDF variables are:
2727  !! - \b aero_particle (dim \c aero_particle): dummy dimension variable
2728  !! (no useful value)
2729  !! - \b aero_particle_mass (unit kg,
2730  !! dim <tt>aero_particle x aero_species</tt>): constituent masses of
2731  !! each aerosol particle - <tt>aero_particle_mass(i,s)</tt> gives the
2732  !! mass of species \c s in particle \c i
2733  !! - \b aero_component_len (dim <tt>aero_particle</tt>):
2734  !! number of different components for each particle.
2735  !! - \b aero_component_start_ind (dim <tt>aero_particle</tt>):
2736  !! starting component index for each particle in
2737  !! <tt>aero_component_source_num</tt> and
2738  !! <tt>aero_component_create_time</tt>
2739  !! - \b aero_component_particle_num (dim <tt>aero_particle</tt>):
2740  !! particle index each component belongs to
2741  !! - \b aero_component_source_num (dim <tt>aero_components</tt>):
2742  !! source number for each <tt>aero_component</tt>
2743  !! - \b aero_component_create_time (unit s,
2744  !! dim <tt>aero_components</tt>): creation time for each
2745  !! <tt>aero_component</tt>
2746  !! - \b aero_particle_weight_group (dim <tt>aero_particle</tt>):
2747  !! weight group number (1 to <tt>aero_weight_group</tt>) of
2748  !! each aerosol particle
2749  !! - \b aero_particle_weight_class (dim <tt>aero_particle</tt>):
2750  !! weight class number (1 to <tt>aero_weight_class</tt>) of each
2751  !! aerosol particle
2752  !! - \b aero_absorb_cross_sect (unit m^2, dim \c aero_particle):
2753  !! optical absorption cross sections of each aerosol particle
2754  !! - \b aero_scatter_cross_sect (unit m^2, dim \c aero_particle):
2755  !! optical scattering cross sections of each aerosol particle
2756  !! - \b aero_asymmetry (dimensionless, dim \c aero_particle): optical
2757  !! asymmetry parameters of each aerosol particle
2758  !! - \b aero_refract_shell_real (dimensionless, dim \c aero_particle):
2759  !! real part of the refractive indices of the shell of each
2760  !! aerosol particle
2761  !! - \b aero_refract_shell_imag (dimensionless, dim \c aero_particle):
2762  !! imaginary part of the refractive indices of the shell of each
2763  !! aerosol particle
2764  !! - \b aero_refract_core_real (dimensionless, dim \c aero_particle):
2765  !! real part of the refractive indices of the core of each
2766  !! aerosol particle
2767  !! - \b aero_refract_core_imag (dimensionless, dim \c aero_particle):
2768  !! imaginary part of the refractive indices of the core of each
2769  !! aerosol particle
2770  !! - \b aero_core_vol (unit m^3, dim \c aero_particle): volume of the
2771  !! optical cores of each aerosol particle
2772  !! - \b aero_water_hyst_leg (dim \c aero_particle): integers
2773  !! specifying which leg of the water hysteresis curve each
2774  !! particle is on, using the MOSAIC numbering convention
2775  !! - \b aero_num_conc (unit m^{-3}, dim \c aero_particle): number
2776  !! concentration associated with each particle
2777  !! - \b aero_id (dim \c aero_particle): unique ID number of each
2778  !! aerosol particle
2779  !! - \b aero_least_create_time (unit s, dim \c aero_particle): least
2780  !! (earliest) creation time of any original constituent particles
2781  !! that coagulated to form each particle, measured from the start
2782  !! of the simulation - a particle is said to be created when it
2783  !! first enters the simulation (by emissions, dilution, etc.)
2784  !! - \b aero_greatest_create_time (unit s, dim \c
2785  !! aero_particle): greatest (latest) creation time of any
2786  !! original constituent particles that coagulated to form each
2787  !! particle, measured from the start of the simulation - a
2788  !! particle is said to be created when it first enters the
2789  !! simulation (by emissions, dilution, etc.)
2790 
2791  call aero_weight_array_output_netcdf(aero_state%awa, ncid)
2792 
2793  call aero_data_netcdf_dim_aero_species(aero_data, ncid, &
2794  dimid_aero_species)
2795  call aero_data_netcdf_dim_aero_source(aero_data, ncid, &
2796  dimid_aero_source)
2797 
2798  if (record_optical) then
2799  call aero_data_netcdf_dim_optical_wavelengths(aero_data, ncid, &
2800  dimid_optical_wavelengths)
2801  end if
2802 
2803  if (aero_state_n_part(aero_state) > 0) then
2804  call aero_state_netcdf_dim_aero_particle(aero_state, ncid, &
2805  dimid_aero_particle)
2806  call aero_state_netcdf_dim_aero_components(aero_state, ncid, &
2807  dimid_aero_components)
2808  next_start_component_ind = 1
2809  do i_part = 1,aero_state_n_part(aero_state)
2810  aero_particle_mass(i_part, :) &
2811  = aero_state%apa%particle(i_part)%vol * aero_data%density
2812  aero_component_len(i_part) = aero_particle_n_components( &
2813  aero_state%apa%particle(i_part))
2814  aero_component_start_ind(i_part) = next_start_component_ind
2815  next_start_component_ind = next_start_component_ind &
2816  + aero_component_len(i_part)
2817  do i_comp = 1,aero_component_len(i_part)
2818  array_position = aero_component_start_ind(i_part) + i_comp - 1
2819  aero_component_particle_num(array_position) = i_part
2820  aero_component_source_num(array_position) &
2821  = aero_state%apa%particle(i_part)%component(i_comp)%source_id
2822  aero_component_create_time(array_position) &
2823  = aero_state%apa%particle(i_part)%component( &
2824  i_comp)%create_time
2825  end do
2826  aero_particle_weight_group(i_part) &
2827  = aero_state%apa%particle(i_part)%weight_group
2828  aero_particle_weight_class(i_part) &
2829  = aero_state%apa%particle(i_part)%weight_class
2830  aero_water_hyst_leg(i_part) &
2831  = aero_state%apa%particle(i_part)%water_hyst_leg
2832  aero_num_conc(i_part) &
2833  = aero_state_particle_num_conc(aero_state, &
2834  aero_state%apa%particle(i_part), aero_data)
2835  aero_id(i_part) = aero_state%apa%particle(i_part)%id
2836  if (aero_state%apa%particle(i_part)%frozen) then
2837  aero_frozen(i_part) = 1
2838  else
2839  aero_frozen(i_part) = 0
2840  end if
2841  aero_imf_temperature(i_part) &
2842  = aero_state%apa%particle(i_part)%imf_temperature
2843  aero_ice_density(i_part) = aero_state%apa%particle(i_part)%den_ice
2844  aero_ice_shape_phi(i_part) &
2845  = aero_state%apa%particle(i_part)%ice_shape_phi
2846  aero_least_create_time(i_part) &
2847  = aero_state%apa%particle(i_part)%least_create_time
2848  aero_greatest_create_time(i_part) &
2849  = aero_state%apa%particle(i_part)%greatest_create_time
2850  aero_particle_n_primary_parts(i_part) &
2851  = aero_state%apa%particle(i_part)%n_primary_parts
2852  if (record_optical) then
2853  aero_absorb_cross_sect(i_part,:) &
2854  = aero_state%apa%particle(i_part)%absorb_cross_sect
2855  aero_scatter_cross_sect(i_part,:) &
2856  = aero_state%apa%particle(i_part)%scatter_cross_sect
2857  aero_asymmetry(i_part,:) &
2858  = aero_state%apa%particle(i_part)%asymmetry
2859  aero_refract_shell_real(i_part,:) &
2860  = real(aero_state%apa%particle(i_part)%refract_shell)
2861  aero_refract_shell_imag(i_part,:) &
2862  = aimag(aero_state%apa%particle(i_part)%refract_shell)
2863  aero_refract_core_real(i_part,:) &
2864  = real(aero_state%apa%particle(i_part)%refract_core)
2865  aero_refract_core_imag(i_part,:) &
2866  = aimag(aero_state%apa%particle(i_part)%refract_core)
2867  aero_core_vol(i_part) = aero_state%apa%particle(i_part)%core_vol
2868  end if
2869  end do
2870  call pmc_nc_write_real_2d(ncid, aero_particle_mass, &
2871  "aero_particle_mass", (/ dimid_aero_particle, &
2872  dimid_aero_species /), unit="kg", &
2873  long_name="constituent masses of each aerosol particle")
2874 
2875  call pmc_nc_write_integer_1d(ncid, aero_component_len, &
2876  "aero_component_len", (/ dimid_aero_particle /), &
2877  long_name="number of aero_components for each aerosol particle")
2878  call pmc_nc_write_integer_1d(ncid, aero_component_start_ind, &
2879  "aero_component_start_ind", (/ dimid_aero_particle /), &
2880  long_name="start index of aero_component for each aerosol " &
2881  // "particle")
2882  call pmc_nc_write_integer_1d(ncid, aero_component_particle_num, &
2883  "aero_component_particle_num", (/ dimid_aero_components /), &
2884  long_name="associated aerosol particle number for each component")
2885  call pmc_nc_write_integer_1d(ncid, aero_component_source_num, &
2886  "aero_component_source_num", (/ dimid_aero_components /), &
2887  long_name="associated source number for each component")
2888  call pmc_nc_write_real_1d(ncid, aero_component_create_time, &
2889  "aero_component_create_time", (/ dimid_aero_components /), &
2890  long_name="associated creation time for each component")
2891  call pmc_nc_write_integer_1d(ncid, aero_particle_weight_group, &
2892  "aero_particle_weight_group", (/ dimid_aero_particle /), &
2893  long_name="weight group number of each aerosol particle")
2894  call pmc_nc_write_integer_1d(ncid, aero_particle_weight_class, &
2895  "aero_particle_weight_class", (/ dimid_aero_particle /), &
2896  long_name="weight class number of each aerosol particle")
2897  call pmc_nc_write_integer_1d(ncid, aero_water_hyst_leg, &
2898  "aero_water_hyst_leg", (/ dimid_aero_particle /), &
2899  long_name="leg of the water hysteresis curve leg of each "&
2900  // "aerosol particle")
2901  call pmc_nc_write_real_1d(ncid, aero_num_conc, &
2902  "aero_num_conc", (/ dimid_aero_particle /), unit="m^{-3}", &
2903  long_name="number concentration for each particle")
2904  call pmc_nc_write_integer64_1d(ncid, aero_id, &
2905  "aero_id", (/ dimid_aero_particle /), &
2906  long_name="unique ID number of each aerosol particle")
2907  call pmc_nc_write_integer_1d(ncid, aero_frozen, &
2908  "aero_frozen", (/ dimid_aero_particle /), &
2909  long_name="flag indicating ice phase of each aerosol particle")
2910  call pmc_nc_write_real_1d(ncid, aero_imf_temperature, &
2911  "aero_imf_temperature", (/ dimid_aero_particle /), &
2912  long_name="immersion freezing temperature (Singular)")
2913  call pmc_nc_write_real_1d(ncid, aero_ice_density, &
2914  "aero_ice_density", (/ dimid_aero_particle /), &
2915  long_name="Ice density if the particle nucleates to ice, -9999 indicates the particle is not an ice.")
2916  call pmc_nc_write_real_1d(ncid, aero_ice_shape_phi, &
2917  "aero_ice_shape_phi", (/ dimid_aero_particle /), &
2918  long_name="Ice shape parameter phi")
2919  call pmc_nc_write_real_1d(ncid, aero_least_create_time, &
2920  "aero_least_create_time", (/ dimid_aero_particle /), unit="s", &
2921  long_name="least creation time of each aerosol particle", &
2922  description="least (earliest) creation time of any original " &
2923  // "constituent particles that coagulated to form each " &
2924  // "particle, measured from the start of the simulation")
2925  call pmc_nc_write_real_1d(ncid, aero_greatest_create_time, &
2926  "aero_greatest_create_time", (/ dimid_aero_particle /), &
2927  unit="s", &
2928  long_name="greatest creation time of each aerosol particle", &
2929  description="greatest (latest) creation time of any original " &
2930  // "constituent particles that coagulated to form each " &
2931  // "particle, measured from the start of the simulation")
2932  call pmc_nc_write_integer_1d(ncid, aero_particle_n_primary_parts, &
2933  "aero_particle_n_primary_parts", (/ dimid_aero_particle /), &
2934  unit="1", long_name="number of primary particles that contribute" &
2935  // "to each particle.")
2936  if (record_optical) then
2937  call pmc_nc_write_real_2d(ncid, aero_absorb_cross_sect, &
2938  "aero_absorb_cross_sect", (/ dimid_aero_particle, &
2939  dimid_optical_wavelengths /), unit="m^2", &
2940  long_name="optical absorption cross sections of each " &
2941  // "aerosol particle")
2942  call pmc_nc_write_real_2d(ncid, aero_scatter_cross_sect, &
2943  "aero_scatter_cross_sect", (/ dimid_aero_particle, &
2944  dimid_optical_wavelengths /), unit="m^2", &
2945  long_name="optical scattering cross sections of each " &
2946  // "aerosol particle")
2947  call pmc_nc_write_real_2d(ncid, aero_asymmetry, &
2948  "aero_asymmetry", (/ dimid_aero_particle, &
2949  dimid_optical_wavelengths /), unit="1", &
2950  long_name="optical asymmetry parameters of each " &
2951  // "aerosol particle")
2952  call pmc_nc_write_real_2d(ncid, aero_refract_shell_real, &
2953  "aero_refract_shell_real", (/ dimid_aero_particle, &
2954  dimid_optical_wavelengths /), unit="1", &
2955  long_name="real part of the refractive indices of the " &
2956  // "shell of each aerosol particle")
2957  call pmc_nc_write_real_2d(ncid, aero_refract_shell_imag, &
2958  "aero_refract_shell_imag", (/ dimid_aero_particle, &
2959  dimid_optical_wavelengths /), unit="1", &
2960  long_name="imaginary part of the refractive indices of " &
2961  // "the shell of each aerosol particle")
2962  call pmc_nc_write_real_2d(ncid, aero_refract_core_real, &
2963  "aero_refract_core_real", (/ dimid_aero_particle, &
2964  dimid_optical_wavelengths /), unit="1", &
2965  long_name="real part of the refractive indices of the core " &
2966  // "of each aerosol particle")
2967  call pmc_nc_write_real_2d(ncid, aero_refract_core_imag, &
2968  "aero_refract_core_imag", (/ dimid_aero_particle, &
2969  dimid_optical_wavelengths /), unit="1", &
2970  long_name="imaginary part of the refractive indices of " &
2971  // "the core of each aerosol particle")
2972  call pmc_nc_write_real_1d(ncid, aero_core_vol, &
2973  "aero_core_vol", (/ dimid_aero_particle /), unit="m^3", &
2974  long_name="volume of the optical cores of each " &
2975  // "aerosol particle")
2976  end if
2977  end if
2978 
2979  ! FIXME: move this to aero_info_array.F90, together with
2980  ! aero_state_netcdf_dim_aero_removed() ?
2981  if (record_removals) then
2982  call aero_state_netcdf_dim_aero_removed(aero_state, ncid, &
2983  dimid_aero_removed)
2984  if (aero_info_array_n_item(aero_state%aero_info_array) >= 1) then
2985  do i_remove = 1,aero_info_array_n_item(aero_state%aero_info_array)
2986  aero_removed_id(i_remove) = &
2987  aero_state%aero_info_array%aero_info(i_remove)%id
2988  aero_removed_action(i_remove) = &
2989  aero_state%aero_info_array%aero_info(i_remove)%action
2990  aero_removed_other_id(i_remove) = &
2991  aero_state%aero_info_array%aero_info(i_remove)%other_id
2992  end do
2993  else
2994  aero_removed_id(1) = 0
2995  aero_removed_action(1) = aero_info_none
2996  aero_removed_other_id(1) = 0
2997  end if
2998  call pmc_nc_write_integer64_1d(ncid, aero_removed_id, &
2999  "aero_removed_id", (/ dimid_aero_removed /), &
3000  long_name="ID of removed particles")
3001  call pmc_nc_write_integer_1d(ncid, aero_removed_action, &
3002  "aero_removed_action", (/ dimid_aero_removed /), &
3003  long_name="reason for particle removal", &
3004  description="valid is 0 (invalid entry), 1 (removed due to " &
3005  // "dilution), 2 (removed due to coagulation -- combined " &
3006  // "particle ID is in \c aero_removed_other_id), 3 (removed " &
3007  // "due to populating halving), or 4 (removed due to " &
3008  // "weighting changes")
3009  call pmc_nc_write_integer64_1d(ncid, aero_removed_other_id, &
3010  "aero_removed_other_id", (/ dimid_aero_removed /), &
3011  long_name="ID of other particle involved in removal", &
3012  description="if <tt>aero_removed_action(i)</tt> is 2 " &
3013  // "(due to coagulation), then " &
3014  // "<tt>aero_removed_other_id(i)</tt> is the ID of the " &
3015  // "resulting combined particle, or 0 if the new particle " &
3016  // "was not created")
3017  end if
3018 
3019  end subroutine aero_state_output_netcdf
3020 
3021  ! this belongs in the subroutine above, but is outside because
3022  ! Doxygen 1.8.7 doesn't resolve references when multiple \page
3023  ! blocks are in one subroutine
3024 
3025  !> \page output_format_aero_removed Output File Format: Aerosol Particle Removal Information
3026  !!
3027  !! When an aerosol particle is introduced into the simulation it
3028  !! is assigned a unique ID number. This ID number will persist
3029  !! over time, allowing tracking of a paticular particle's
3030  !! evolution. If the \c record_removals variable in the input spec
3031  !! file is \c yes, then the every time a particle is removed from
3032  !! the simulation its removal will be recorded in the removal
3033  !! information.
3034  !!
3035  !! The removal information written at timestep \c n contains
3036  !! information about every particle ID that is present at time
3037  !! <tt>(n - 1)</tt> but not present at time \c n.
3038  !!
3039  !! The removal information is always written in the output files,
3040  !! even if no particles were removed in the previous
3041  !! timestep. Unfortunately, NetCDF files cannot contain arrays of
3042  !! length 0. In the case of no particles being removed, the \c
3043  !! aero_removed dimension will be set to 1 and
3044  !! <tt>aero_removed_action(1)</tt> will be 0 (\c AERO_INFO_NONE).
3045  !!
3046  !! When two particles coagulate, the ID number of the combined
3047  !! particle will be the ID particle of the largest constituent, if
3048  !! possible (weighting functions can make this impossible to
3049  !! achieve). A given particle ID may thus be lost due to
3050  !! coagulation (if the resulting combined particle has a different
3051  !! ID), or the ID may be preserved (as the ID of the combined
3052  !! particle). Only if the ID is lost will the particle be recorded
3053  !! in the removal information, and in this case
3054  !! <tt>aero_removed_action(i)</tt> will be 2 (\c AERO_INFO_COAG)
3055  !! and <tt>aero_removed_other_id(i)</tt> will be the ID number of
3056  !! the combined particle.
3057  !!
3058  !! The aerosol removal information NetCDF dimensions are:
3059  !! - \b aero_removed: number of aerosol particles removed from the
3060  !! simulation during the previous timestep (or 1, as described
3061  !! above)
3062  !!
3063  !! The aerosol removal information NetCDF variables are:
3064  !! - \b aero_removed (dim \c aero_removed): dummy dimension variable
3065  !! (no useful value)
3066  !! - \b aero_removed_id (dim \c aero_removed): the ID number of each
3067  !! removed particle
3068  !! - \b aero_removed_action (dim \c aero_removed): the reasons for
3069  !! removal for each particle, with values:
3070  !! - 0 (\c AERO_INFO_NONE): no information (invalid entry)
3071  !! - 1 (\c AERO_INFO_DILUTION): particle was removed due to dilution
3072  !! with outside air
3073  !! - 2 (\c AERO_INFO_COAG): particle was removed due to coagulation
3074  !! - 3 (\c AERO_INFO_HALVED): particle was removed due to halving of
3075  !! the aerosol population
3076  !! - 4 (\c AERO_INFO_WEIGHT): particle was removed due to adjustments
3077  !! in the particle's weighting function
3078  !! - \b aero_removed_other_id (dim \c aero_removed): the ID number of
3079  !! the combined particle formed by coagulation, if the removal reason
3080  !! was coagulation (2, \c AERO_INFO_COAG). May be 0, if the new
3081  !! coagulated particle was not created due to weighting.
3082 
3083 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3084 
3085  !> Read full state.
3086  subroutine aero_state_input_netcdf(aero_state, ncid, aero_data)
3087 
3088  !> aero_state to read.
3089  type(aero_state_t), intent(inout) :: aero_state
3090  !> NetCDF file ID, in data mode.
3091  integer, intent(in) :: ncid
3092  !> aero_data structure.
3093  type(aero_data_t), intent(in) :: aero_data
3094 
3095  integer :: dimid_aero_particle, dimid_aero_removed, n_info_item, n_part
3096  integer :: i_bin, i_part_in_bin, i_part, i_remove, status
3097  type(aero_particle_t) :: aero_particle
3098  character(len=1000) :: name
3099 
3100  real(kind=dp), allocatable :: aero_particle_mass(:,:)
3101  integer, allocatable :: aero_particle_weight_group(:)
3102  integer, allocatable :: aero_particle_weight_class(:)
3103  real(kind=dp), allocatable :: aero_absorb_cross_sect(:,:)
3104  real(kind=dp), allocatable :: aero_scatter_cross_sect(:,:)
3105  real(kind=dp), allocatable :: aero_asymmetry(:,:)
3106  real(kind=dp), allocatable :: aero_refract_shell_real(:,:)
3107  real(kind=dp), allocatable :: aero_refract_shell_imag(:,:)
3108  real(kind=dp), allocatable :: aero_refract_core_real(:,:)
3109  real(kind=dp), allocatable :: aero_refract_core_imag(:,:)
3110  real(kind=dp), allocatable :: aero_core_vol(:)
3111  integer, allocatable :: aero_water_hyst_leg(:)
3112  real(kind=dp), allocatable :: aero_num_conc(:)
3113  integer, allocatable :: aero_frozen(:)
3114  real(kind=dp), allocatable :: aero_imf_temperature(:)
3115  real(kind=dp), allocatable :: aero_ice_density(:)
3116  real(kind=dp), allocatable :: aero_ice_shape_phi(:)
3117  integer(kind=8), allocatable :: aero_id(:)
3118  real(kind=dp), allocatable :: aero_least_create_time(:)
3119  real(kind=dp), allocatable :: aero_greatest_create_time(:)
3120  integer, allocatable :: aero_removed_id(:)
3121  integer, allocatable :: aero_removed_action(:)
3122  integer(kind=8), allocatable :: aero_removed_other_id(:)
3123  integer, allocatable :: aero_component_particle_num(:)
3124  integer, allocatable :: aero_component_source_num(:)
3125  integer, allocatable :: aero_component_len(:)
3126  integer, allocatable :: aero_component_start_ind(:)
3127  integer, allocatable :: aero_particle_n_primary_parts(:)
3128  real(kind=dp), allocatable :: aero_component_create_time(:)
3129  integer :: i_comp
3130 
3131 
3132  call aero_state_zero(aero_state)
3133 
3134  status = nf90_inq_dimid(ncid, "aero_particle", dimid_aero_particle)
3135  if (status == nf90_ebaddim) then
3136  ! no aero_particle dimension means no particles present
3137  call aero_weight_array_input_netcdf(aero_state%awa, ncid)
3138  return
3139  end if
3140  call pmc_nc_check(status)
3141  call pmc_nc_check(nf90_inquire_dimension(ncid, dimid_aero_particle, &
3142  name, n_part))
3143 
3144  call pmc_nc_read_real_2d(ncid, aero_particle_mass, &
3145  "aero_particle_mass")
3146  call pmc_nc_read_integer_1d(ncid, aero_particle_n_primary_parts, &
3147  "aero_particle_n_primary_parts")
3148  call pmc_nc_read_integer_1d(ncid, aero_component_particle_num, &
3149  "aero_component_particle_num")
3150  call pmc_nc_read_integer_1d(ncid, aero_component_source_num, &
3151  "aero_component_source_num")
3152  call pmc_nc_read_real_1d(ncid, aero_component_create_time, &
3153  "aero_component_create_time")
3154  call pmc_nc_read_integer_1d(ncid, aero_component_len, "aero_component_len")
3155  call pmc_nc_read_integer_1d(ncid, aero_component_start_ind, &
3156  "aero_component_start_ind")
3157  call pmc_nc_read_integer_1d(ncid, aero_particle_weight_group, &
3158  "aero_particle_weight_group")
3159  call pmc_nc_read_integer_1d(ncid, aero_particle_weight_class, &
3160  "aero_particle_weight_class")
3161  call pmc_nc_read_real_2d(ncid, aero_absorb_cross_sect, &
3162  "aero_absorb_cross_sect", must_be_present=.false.)
3163  call pmc_nc_read_real_2d(ncid, aero_scatter_cross_sect, &
3164  "aero_scatter_cross_sect", must_be_present=.false.)
3165  call pmc_nc_read_real_2d(ncid, aero_asymmetry, &
3166  "aero_asymmetry", must_be_present=.false.)
3167  call pmc_nc_read_real_2d(ncid, aero_refract_shell_real, &
3168  "aero_refract_shell_real", must_be_present=.false.)
3169  call pmc_nc_read_real_2d(ncid, aero_refract_shell_imag, &
3170  "aero_refract_shell_imag", must_be_present=.false.)
3171  call pmc_nc_read_real_2d(ncid, aero_refract_core_real, &
3172  "aero_refract_core_real", must_be_present=.false.)
3173  call pmc_nc_read_real_2d(ncid, aero_refract_core_imag, &
3174  "aero_refract_core_imag", must_be_present=.false.)
3175  call pmc_nc_read_real_1d(ncid, aero_core_vol, &
3176  "aero_core_vol", must_be_present=.false.)
3177  call pmc_nc_read_integer_1d(ncid, aero_water_hyst_leg, &
3178  "aero_water_hyst_leg")
3179  call pmc_nc_read_real_1d(ncid, aero_num_conc, &
3180  "aero_num_conc")
3181  call pmc_nc_read_integer64_1d(ncid, aero_id, &
3182  "aero_id")
3183  call pmc_nc_read_integer_1d(ncid, aero_frozen, &
3184  "aero_frozen")
3185  call pmc_nc_read_real_1d(ncid, aero_imf_temperature, &
3186  "aero_imf_temperature")
3187  call pmc_nc_read_real_1d(ncid, aero_ice_density, &
3188  "aero_ice_density", must_be_present=.true.)
3189  call pmc_nc_read_real_1d(ncid, aero_ice_shape_phi, &
3190  "aero_ice_shape_phi", must_be_present=.true.)
3191  call pmc_nc_read_real_1d(ncid, aero_least_create_time, &
3192  "aero_least_create_time")
3193  call pmc_nc_read_real_1d(ncid, aero_greatest_create_time, &
3194  "aero_greatest_create_time")
3195 
3196  call aero_weight_array_input_netcdf(aero_state%awa, ncid)
3197  call aero_state_set_n_part_ideal(aero_state, 0d0)
3198 
3199  do i_part = 1,n_part
3200  call aero_particle_zero(aero_particle, aero_data)
3201  aero_particle%vol = aero_particle_mass(i_part, :) / aero_data%density
3202  aero_particle%n_primary_parts = aero_particle_n_primary_parts(i_part)
3203  if (allocated(aero_particle%component)) &
3204  deallocate(aero_particle%component)
3205  allocate(aero_particle%component(aero_component_len(i_part)))
3206  do i_comp = 1,aero_component_len(i_part)
3207  aero_particle%component(i_comp)%source_id = &
3208  aero_component_source_num(aero_component_start_ind(i_part) &
3209  + i_comp - 1)
3210  aero_particle%component(i_comp)%create_time = &
3211  aero_component_create_time(aero_component_start_ind(i_part) &
3212  + i_comp - 1)
3213  end do
3214  aero_particle%weight_group = aero_particle_weight_group(i_part)
3215  aero_particle%weight_class = aero_particle_weight_class(i_part)
3216  if (size(aero_absorb_cross_sect, 1) == n_part) then
3217  aero_particle%absorb_cross_sect = aero_absorb_cross_sect(i_part, :)
3218  end if
3219  if (size(aero_scatter_cross_sect, 1) == n_part) then
3220  aero_particle%scatter_cross_sect = &
3221  aero_scatter_cross_sect(i_part, :)
3222  end if
3223  if (size(aero_asymmetry, 1) == n_part) then
3224  aero_particle%asymmetry = aero_asymmetry(i_part, :)
3225  end if
3226  if ((size(aero_refract_shell_real, 1) == n_part) &
3227  .and. (size(aero_refract_shell_imag, 1) == n_part)) then
3228  aero_particle%refract_shell = &
3229  cmplx(aero_refract_shell_real(i_part, :), &
3230  aero_refract_shell_imag(i_part, :), kind=dc)
3231  end if
3232  if ((size(aero_refract_core_real, 1) == n_part) &
3233  .and. (size(aero_refract_core_imag, 1) == n_part)) then
3234  aero_particle%refract_core = cmplx(aero_refract_core_real( &
3235  i_part, :), aero_refract_core_imag(i_part, :), kind=dc)
3236  end if
3237  if (size(aero_core_vol) == n_part) then
3238  aero_particle%core_vol = aero_core_vol(i_part)
3239  end if
3240  aero_particle%water_hyst_leg = aero_water_hyst_leg(i_part)
3241  aero_particle%id = aero_id(i_part)
3242  if (aero_frozen(i_part) == 1) then
3243  aero_particle%frozen = .true.
3244  else
3245  aero_particle%frozen = .false.
3246  end if
3247  aero_particle%imf_temperature = aero_imf_temperature(i_part)
3248  aero_particle%den_ice = aero_ice_density(i_part)
3249  aero_particle%ice_shape_phi = aero_ice_shape_phi(i_part)
3250  aero_particle%least_create_time = aero_least_create_time(i_part)
3251  aero_particle%greatest_create_time = aero_greatest_create_time(i_part)
3252 
3253  call assert(314368871, almost_equal(aero_num_conc(i_part), &
3254  aero_weight_array_num_conc(aero_state%awa, aero_particle, &
3255  aero_data)))
3256 
3257  call aero_state_add_particle(aero_state, aero_particle, aero_data)
3258  end do
3259 
3260  next_id = maxval(aero_id) + 1
3261 
3262  call pmc_nc_read_integer_1d(ncid, aero_removed_id, &
3263  "aero_removed_id", must_be_present=.false.)
3264  call pmc_nc_read_integer_1d(ncid, aero_removed_action, &
3265  "aero_removed_action", must_be_present=.false.)
3266  call pmc_nc_read_integer64_1d(ncid, aero_removed_other_id, &
3267  "aero_removed_other_id", must_be_present=.false.)
3268 
3269  n_info_item = size(aero_removed_id)
3270  if (n_info_item >= 1) then
3271  if ((n_info_item > 1) &
3272  .or. ((n_info_item == 1) .and. (aero_removed_id(1) /= 0))) then
3273  call aero_info_array_enlarge_to(aero_state%aero_info_array, &
3274  n_info_item)
3275  do i_remove = 1,n_info_item
3276  aero_state%aero_info_array%aero_info(i_remove)%id &
3277  = aero_removed_id(i_remove)
3278  aero_state%aero_info_array%aero_info(i_remove)%action &
3279  = aero_removed_action(i_remove)
3280  aero_state%aero_info_array%aero_info(i_remove)%other_id &
3281  = aero_removed_other_id(i_remove)
3282  end do
3283  end if
3284  end if
3285 
3286  end subroutine aero_state_input_netcdf
3287 
3288 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3289 
3290  !> Sorts the particles if necessary.
3291  subroutine aero_state_sort(aero_state, aero_data, bin_grid, all_procs_same)
3292 
3293  !> Aerosol state to sort.
3294  type(aero_state_t), intent(inout) :: aero_state
3295  !> Aerosol data.
3296  type(aero_data_t), intent(in) :: aero_data
3297  !> Bin grid.
3298  type(bin_grid_t), optional, intent(in) :: bin_grid
3299  !> Whether all processors should use the same bin grid.
3300  logical, optional, intent(in) :: all_procs_same
3301 
3302  call aero_sorted_remake_if_needed(aero_state%aero_sorted, aero_state%apa, &
3303  aero_data, aero_state%valid_sort, size(aero_state%awa%weight, 1), &
3304  size(aero_state%awa%weight, 2), bin_grid, all_procs_same)
3305  aero_state%valid_sort = .true.
3306 
3307  end subroutine aero_state_sort
3308 
3309 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3310 
3311  !> Check that aerosol state data is consistent.
3312  subroutine aero_state_check(aero_state, aero_data)
3313 
3314  !> Aerosol state to check.
3315  type(aero_state_t), intent(in) :: aero_state
3316  !> Aerosol data.
3317  type(aero_data_t), intent(in) :: aero_data
3318 
3319  logical, parameter :: continue_on_error = .false.
3320 
3321  call aero_particle_array_check(aero_state%apa, aero_data, &
3322  continue_on_error)
3323  if (aero_state%valid_sort) then
3324  call aero_sorted_check(aero_state%aero_sorted, aero_state%apa, &
3325  aero_data, size(aero_state%awa%weight, 1), &
3326  size(aero_state%awa%weight, 2), continue_on_error)
3327  end if
3328 
3329  end subroutine aero_state_check
3330 
3331 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3332 
3333 #ifdef PMC_USE_CAMP
3334  !> Initialize the aero_state_t variable with camp chem data
3335  subroutine aero_state_initialize(aero_state, aero_data, camp_core)
3336 
3337  !> Aerosol state.
3338  type(aero_state_t), intent(inout) :: aero_state
3339  !> Aerosol data.
3340  class(aero_data_t), intent(in) :: aero_data
3341  !> CAMP core.
3342  type(camp_core_t), intent(in) :: camp_core
3343 
3344  select type( aero_rep => aero_data%aero_rep_ptr)
3345  type is(aero_rep_single_particle_t)
3346  ! Set up the update data objects for number
3347  call camp_core%initialize_update_object(aero_rep, &
3348  aero_state%update_number)
3349  class default
3350  call die_msg(927605681, "Wrong aerosol representation type")
3351  end select
3352 
3353  end subroutine aero_state_initialize
3354 #endif
3355 
3356 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3357 
3358  !> Returns the total number of components for all particles in an aero_state.
3359  elemental integer function aero_state_total_n_components(aero_state)
3360 
3361  !> Aerosol state.
3362  type(aero_state_t), intent(in) :: aero_state
3363 
3365  aero_state%apa)
3366 
3367  end function aero_state_total_n_components
3368 
3369 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3370 
3371  !> Returns the number of components for each particle in an aero_state.
3372  function aero_state_n_components(aero_state)
3373 
3374  !> Aerosol state.
3375  type(aero_state_t), intent(in) :: aero_state
3376 
3377  !> Return number of componenets for each particle.
3378  integer :: aero_state_n_components(aero_state_n_part(aero_state))
3379 
3380  integer :: i_part
3381 
3382  do i_part = 1,aero_state_n_part(aero_state)
3384  aero_state%apa%particle(i_part))
3385  end do
3386 
3387  end function aero_state_n_components
3388 
3389 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3390 
3391  !> Return the total scattering coefficient of a population.
3392  real(kind=dp) function aero_state_scattering(aero_state, aero_data, &
3393  wavelength)
3394 
3395  !> Aerosol state.
3396  type(aero_state_t) :: aero_state
3397  !> Aerosol data.
3398  type(aero_data_t) :: aero_data
3399  !> Wavelength index of interest.
3400  integer :: wavelength
3401 
3402  real(kind=dp) :: num_concs(aero_state_n_part(aero_state))
3403  real(kind=dp) :: b_scat
3404  integer :: i_part
3405 
3406  num_concs = aero_state_num_concs(aero_state, aero_data)
3407 
3408  b_scat = 0.0d0
3409 
3410  do i_part = 1,aero_state_n_part(aero_state)
3411  b_scat = b_scat + num_concs(i_part) &
3412  * aero_state%apa%particle(i_part)%scatter_cross_sect(wavelength)
3413  end do
3414 
3415  aero_state_scattering = b_scat
3416 
3417  end function aero_state_scattering
3418 
3419 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3420 
3421  !> Returns the total absorption coefficient of a population.
3422  real(kind=dp) function aero_state_absorption(aero_state, aero_data, &
3423  wavelength)
3424 
3425  !> Aerosol state.
3426  type(aero_state_t) :: aero_state
3427  !> Aerosol data.
3428  type(aero_data_t) :: aero_data
3429  !> Wavelength index of interest.
3430  integer :: wavelength
3431 
3432  real(kind=dp) :: num_concs(aero_state_n_part(aero_state))
3433  real(kind=dp) :: b_abs
3434  integer :: i_part
3435 
3436  num_concs = aero_state_num_concs(aero_state, aero_data)
3437 
3438  b_abs = 0.0d0
3439 
3440  do i_part = 1,aero_state_n_part(aero_state)
3441  b_abs = b_abs + num_concs(i_part) &
3442  * aero_state%apa%particle(i_part)%absorb_cross_sect(wavelength)
3443  end do
3444 
3445  aero_state_absorption = b_abs
3446 
3447  end function aero_state_absorption
3448 
3449 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3450 
3451  !> Returns an array of scattering coefficients based on the bin_grid sorted
3452  !> based on given array of values.
3453  function aero_state_scattering_binned(aero_state, aero_data, bin_grid, &
3454  bin_values, i_wavelength)
3455 
3456  !> Aerosol state.
3457  type(aero_state_t) :: aero_state
3458  !> Aerosol data.
3459  type(aero_data_t) :: aero_data
3460  !> Bin grid to apply.
3461  type(bin_grid_t) :: bin_grid
3462  !> Bin values.
3463  real(kind=dp), allocatable :: bin_values(:)
3464  !> Wavelength index of interest.
3465  integer :: i_wavelength
3466 
3467  real(kind=dp) :: aero_state_scattering_binned(bin_grid_size(bin_grid))
3468  real(kind=dp) :: num_concs(aero_state_n_part(aero_state))
3469  integer :: i_bin, i_part
3470 
3471  call assert(894840826, size(bin_values) == aero_state_n_part(aero_state))
3472 
3474 
3475  num_concs = aero_state_num_concs(aero_state, aero_data)
3476 
3477  do i_part = 1,aero_state_n_part(aero_state)
3478  i_bin = bin_grid_find(bin_grid, bin_values(i_part))
3479  aero_state_scattering_binned(i_bin) = num_concs(i_part) &
3480  * aero_state%apa%particle(i_part)%scatter_cross_sect( &
3481  i_wavelength) + aero_state_scattering_binned(i_bin)
3482  end do
3483 
3484  end function aero_state_scattering_binned
3485 
3486 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3487 
3488  !> Returns an array of absorption coefficients based on the bin_grid sorted
3489  !> based on given array of values.
3490  function aero_state_absorption_binned(aero_state, aero_data, bin_grid, &
3491  bin_values, i_wavelength)
3492 
3493  !> Aerosol state.
3494  type(aero_state_t) :: aero_state
3495  !> Aerosol data.
3496  type(aero_data_t) :: aero_data
3497  !> Bin grid.
3498  type(bin_grid_t) :: bin_grid
3499  !> Values to use to bin.
3500  real(kind=dp), allocatable :: bin_values(:)
3501  !> Wavelength index of interest.
3502  integer :: i_wavelength
3503 
3504  real(kind=dp) :: aero_state_absorption_binned(bin_grid_size(bin_grid))
3505  real(kind=dp) :: num_concs(aero_state_n_part(aero_state))
3506  integer :: i_bin, i_part
3507 
3508  call assert(448243765, size(bin_values) == aero_state_n_part(aero_state))
3509 
3511 
3512  num_concs = aero_state_num_concs(aero_state, aero_data)
3513 
3514  do i_part = 1,aero_state_n_part(aero_state)
3515  i_bin = bin_grid_find(bin_grid, bin_values(i_part))
3516  aero_state_absorption_binned(i_bin) = num_concs(i_part) &
3517  * aero_state%apa%particle(i_part)%absorb_cross_sect( &
3518  i_wavelength) + aero_state_absorption_binned(i_bin)
3519  end do
3520 
3521  end function aero_state_absorption_binned
3522 
3523 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3524 
3525  !> Returns arrays of mixing state metrics of the particles within given size
3526  !! ranges based on a given bin grid and given array of values to bin by.
3527  !!
3528  !! If \c include is specified then only those species are included
3529  !! in computing the entropies. If \c exclude is specified then all
3530  !! species except those species are included. If both \c include and
3531  !! \c exclude arguments are specified then only those species in \c
3532  !! include but not in \c exclude are included. If \c group is
3533  !! specified then the species are divided into two sets, given by
3534  !! those in the group and those not in the group. The entropies are
3535  !! then computed using the total mass of each set. Alternatively \c
3536  !! groups can be specified, which lists several groups of
3537  !! species. If \c groups is provided, only species explicitly listed
3538  !! will be included.
3539  subroutine aero_state_mixing_state_metrics_binned(aero_state, aero_data, &
3540  bin_grid, bin_values, d_alpha, d_gamma, chi, include, exclude, group, &
3541  groups)
3542 
3543  !> Aerosol state.
3544  type(aero_state_t), intent(in) :: aero_state
3545  !> Aerosol data.
3546  type(aero_data_t), intent(in) :: aero_data
3547  !> Bin grid.
3548  type(bin_grid_t), intent(in) :: bin_grid
3549  !> Values to bin by.
3550  real(kind=dp), allocatable, intent(in) :: bin_values(:)
3551  !> Average particle diversity.
3552  real(kind=dp), allocatable, intent(inout) :: d_alpha(:)
3553  !> Bulk diversity.
3554  real(kind=dp), allocatable, intent(inout) :: d_gamma(:)
3555  !> Mixing state index.
3556  real(kind=dp), allocatable, intent(inout) :: chi(:)
3557  !> Species names to include in the mass.
3558  character(len=*), optional :: include(:)
3559  !> Species names to exclude in the mass.
3560  character(len=*), optional :: exclude(:)
3561  !> Species names to group together.
3562  character(len=*), optional :: group(:)
3563  !> Sets of species names to group together.
3564  character(len=*), optional :: groups(:,:)
3565 
3566  type(aero_state_t) :: aero_state_size_range
3567  integer :: i_bin
3568  integer :: i_part
3569 
3570  call assert(336293361, size(bin_values) == aero_state_n_part(aero_state))
3571 
3572  if (allocated(d_alpha)) deallocate(d_alpha)
3573  if (allocated(d_gamma)) deallocate(d_gamma)
3574  if (allocated(chi)) deallocate(chi)
3575 
3576  allocate(d_alpha(bin_grid_size(bin_grid)))
3577  allocate(d_gamma(bin_grid_size(bin_grid)))
3578  allocate(chi(bin_grid_size(bin_grid)))
3579  d_alpha = 1.0d0
3580  d_gamma = 1.0d0
3581  chi = 0.0d0
3582 
3583  do i_bin = 1,bin_grid_size(bin_grid)
3584  aero_state_size_range = aero_state
3585  do i_part = aero_state_n_part(aero_state),1,-1
3586  if (.not. bin_grid_contains(bin_grid, i_bin, bin_values(i_part) &
3587  )) then
3588  call aero_state_remove_particle_no_info(aero_state_size_range, &
3589  i_part)
3590  end if
3591  end do
3592  call aero_state_mixing_state_metrics(aero_state_size_range, aero_data, &
3593  d_alpha(i_bin), d_gamma(i_bin), chi(i_bin), include, exclude, &
3594  group, groups)
3595  end do
3596 
3598 
3599 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3600 
3601  !> Copies one aero_state to another and assigns new particle IDs.
3602  subroutine aero_state_dup_all_particles(aero_state_from, aero_state_to)
3603 
3604  !> Aerosol state to copy from.
3605  type(aero_state_t), intent(in) :: aero_state_from
3606  !> Aerosol state to copy to.
3607  type(aero_state_t), intent(inout) :: aero_state_to
3608 
3609  integer :: i_part
3610 
3611  if (aero_state_n_part(aero_state_from) > 0) then
3612  aero_state_to = aero_state_from
3613  do i_part = 1,aero_state_n_part(aero_state_to)
3614  call aero_particle_new_id(aero_state_to%apa%particle(i_part))
3615  end do
3616  else
3617  call aero_state_zero(aero_state_to)
3618  aero_state_to%n_part_ideal = aero_state_from%n_part_ideal
3619 #ifdef PMC_USE_CAMP
3620  aero_state_to%update_number = aero_state_from%update_number
3621 #endif
3622  end if
3623 
3624  end subroutine aero_state_dup_all_particles
3625 
3626 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3627 
3628 end module pmc_aero_state
pmc_aero_state::aero_state_bin_average_comp
subroutine aero_state_bin_average_comp(aero_state, bin_grid, aero_data)
Set each aerosol particle to have its original total volume, but species volume ratios given by the t...
Definition: aero_state.F90:2083
pmc_aero_state::aero_state_make_dry
subroutine aero_state_make_dry(aero_state, aero_data)
Make all particles dry (water set to zero).
Definition: aero_state.F90:2344
pmc_aero_state::aero_state_zero
subroutine aero_state_zero(aero_state)
Resets an aero_state to have zero particles per bin.
Definition: aero_state.F90:350
pmc_aero_particle::aero_particle_t
Single aerosol particle data structure.
Definition: aero_particle.F90:30
pmc_aero_state::aero_state_tag_mix
integer, parameter aero_state_tag_mix
MPI tag for mixing particles between processes.
Definition: aero_state.F90:32
pmc_aero_particle_array::aero_particle_array_t
1-D array of particles, used by aero_state to store the particles.
Definition: aero_particle_array.F90:41
pmc_rand::prob_round
integer function prob_round(val)
Round val to floor(val) or ceiling(val) with probability proportional to the relative distance from v...
Definition: rand.F90:215
pmc_aero_weight_array::aero_weight_array_n_class
integer function aero_weight_array_n_class(aero_weight_array)
Return the number of weight classes.
Definition: aero_weight_array.F90:148
pmc_aero_particle::aero_particle_set_weight
subroutine aero_particle_set_weight(aero_particle, i_group, i_class)
Sets the aerosol particle weight group.
Definition: aero_particle.F90:253
pmc_aero_info_array::pmc_mpi_pack_aero_info_array
subroutine pmc_mpi_pack_aero_info_array(buffer, position, val)
Packs the given value into the buffer, advancing position.
Definition: aero_info_array.F90:242
pmc_aero_state::aero_state_total_num_conc_wet
real(kind=dp) function aero_state_total_num_conc_wet(aero_state, aero_data)
Returns the total number concentration of wet particles.
Definition: aero_state.F90:1288
pmc_aero_state::aero_state_remove_particle
subroutine aero_state_remove_particle(aero_state, i_part, record_removal, aero_info)
Remove the given particle and possibly record the removal.
Definition: aero_state.F90:429
pmc_aero_state::aero_state_netcdf_dim_aero_particle
subroutine aero_state_netcdf_dim_aero_particle(aero_state, ncid, dimid_aero_particle)
Write the aero particle dimension to the given NetCDF file if it is not already present and in any ca...
Definition: aero_state.F90:2520
pmc_aero_dist::aero_dist_n_mode
elemental integer function aero_dist_n_mode(aero_dist)
Return the number of modes.
Definition: aero_dist.F90:44
pmc_util::rad2diam
real(kind=dp) elemental function rad2diam(r)
Convert radius (m) to diameter (m).
Definition: util.F90:253
pmc_aero_data::aero_data_n_spec
elemental integer function aero_data_n_spec(aero_data)
Return the number of aerosol species, or -1 if uninitialized.
Definition: aero_data.F90:250
pmc_aero_state::aero_state_scale_weight
subroutine aero_state_scale_weight(aero_state, aero_data, i_group, i_class, weight_ratio, allow_doubling, allow_halving)
Scale the weighting of the given group/class by the given ratio, altering particle number as necessar...
Definition: aero_state.F90:1861
pmc_mpi::pmc_mpi_size
integer function pmc_mpi_size()
Returns the total number of processes.
Definition: mpi.F90:134
pmc_mpi
Wrapper functions for MPI.
Definition: mpi.F90:13
pmc_aero_particle_array::pmc_mpi_pack_aero_particle_array
subroutine pmc_mpi_pack_aero_particle_array(buffer, position, val)
Packs the given value into the buffer, advancing position.
Definition: aero_particle_array.F90:225
pmc_aero_weight_array::aero_weight_array_single_num_conc
real(kind=dp) function aero_weight_array_single_num_conc(aero_weight_array, aero_particle, aero_data)
Compute the number concentration for a particle (m^{-3}).
Definition: aero_weight_array.F90:215
pmc_aero_state::aero_state_weight_nummass_source
integer, parameter aero_state_weight_nummass_source
Coupled number/mass weighting by source.
Definition: aero_state.F90:51
pmc_aero_state::aero_state_n_part
elemental integer function aero_state_n_part(aero_state)
Return the current number of particles.
Definition: aero_state.F90:94
pmc_aero_particle::aero_particle_crit_rel_humid
real(kind=dp) function aero_particle_crit_rel_humid(aero_particle, aero_data, env_state)
Returns the critical relative humidity (1).
Definition: aero_particle.F90:801
pmc_aero_state::aero_state_weight_flat_specified
integer, parameter aero_state_weight_flat_specified
Flat weighting by specified weight classes.
Definition: aero_state.F90:53
pmc_aero_weight_array::pmc_mpi_pack_size_aero_weight_array
integer function pmc_mpi_pack_size_aero_weight_array(val)
Determines the number of bytes required to pack the given value.
Definition: aero_weight_array.F90:420
pmc_aero_info_array::aero_info_array_enlarge_to
subroutine aero_info_array_enlarge_to(aero_info_array, n)
Possibly enlarges the given array, ensuring that it is at least of size n.
Definition: aero_info_array.F90:112
pmc_aero_weight
The aero_weight_t structure and associated subroutines.
Definition: aero_weight.F90:9
pmc_aero_state::aero_state_n_components
integer function, dimension(aero_state_n_part(aero_state)) aero_state_n_components(aero_state)
Returns the number of components for each particle in an aero_state.
Definition: aero_state.F90:3373
pmc_aero_data::aero_data_n_weight_class
elemental integer function aero_data_n_weight_class(aero_data)
Return the number of aerosol sources, or -1 if uninitialized.
Definition: aero_data.F90:282
pmc_aero_info_array::aero_info_array_add_aero_info
subroutine aero_info_array_add_aero_info(aero_info_array, aero_info)
Adds the given aero_info to the end of the array.
Definition: aero_info_array.F90:154
pmc_aero_state::aero_state_add_aero_dist_sample
subroutine aero_state_add_aero_dist_sample(aero_state, aero_data, aero_dist, sample_prop, characteristic_factor, create_time, allow_doubling, allow_halving, n_part_add)
Generates a Poisson sample of an aero_dist, adding to aero_state, with the given sample proportion.
Definition: aero_state.F90:761
pmc_aero_state::aero_state_netcdf_dim_aero_components
subroutine aero_state_netcdf_dim_aero_components(aero_state, ncid, dimid_aero_components)
Write the aero component dimension to the given NetCDF file if it is not already present and in any c...
Definition: aero_state.F90:2604
pmc_aero_particle
The aero_particle_t structure and associated subroutines.
Definition: aero_particle.F90:9
pmc_aero_sorted::aero_sorted_check
subroutine aero_sorted_check(aero_sorted, aero_particle_array, aero_data, n_group, n_class, continue_on_error)
Check sorting.
Definition: aero_sorted.F90:501
pmc_util::integer64_to_string
character(len=pmc_util_convert_string_len) function integer64_to_string(val)
Convert an integer64 to a string format.
Definition: util.F90:783
pmc_aero_particle::aero_particle_new_id
subroutine aero_particle_new_id(aero_particle)
Assigns a globally-unique new ID number to the particle.
Definition: aero_particle.F90:158
pmc_aero_particle::aero_particle_species_volume
elemental real(kind=dp) function aero_particle_species_volume(aero_particle, i_spec)
Volume of a single species in the particle (m^3).
Definition: aero_particle.F90:347
pmc_aero_state::aero_state_num_conc_for_reweight
subroutine aero_state_num_conc_for_reweight(aero_state, aero_data, reweight_num_conc)
Save the correct number concentrations for later use by aero_state_reweight().
Definition: aero_state.F90:566
pmc_aero_weight::aero_weight_scale
elemental subroutine aero_weight_scale(aero_weight, factor)
Scale the weight by the given fraction, so new_weight = old_weight * factor.
Definition: aero_weight.F90:61
pmc_mpi::pmc_mpi_allreduce_sum_integer
subroutine pmc_mpi_allreduce_sum_integer(val, val_sum)
Computes the sum of val across all processes, storing the result in val_sum on all processes.
Definition: mpi.F90:1686
pmc_aero_particle_array::pmc_mpi_pack_size_apa
integer function pmc_mpi_pack_size_apa(val)
Determines the number of bytes required to pack the given value.
Definition: aero_particle_array.F90:204
pmc_rand::rand_poisson
integer function rand_poisson(mean)
Generate a Poisson-distributed random number with the given mean.
Definition: rand.F90:253
pmc_aero_weight_array::aero_weight_array_check_monotonicity
subroutine aero_weight_array_check_monotonicity(aero_weight_array, monotone_increasing, monotone_decreasing)
Determine whether all weight functions in an array are monotone increasing, monotone decreasing,...
Definition: aero_weight_array.F90:306
pmc_aero_state::aero_state_group_class_num_conc
real(kind=dp) function aero_state_group_class_num_conc(aero_state, aero_data, i_group, i_class)
Returns the number concentration of a given weight group and class.
Definition: aero_state.F90:1380
pmc_aero_state::aero_state_remove_rand_particle_from_bin
subroutine aero_state_remove_rand_particle_from_bin(aero_state, i_bin, i_class, aero_particle)
Remove a randomly chosen particle from the given bin and return it.
Definition: aero_state.F90:454
pmc_aero_weight_array::aero_weight_array_set_nummass
subroutine aero_weight_array_set_nummass(aero_weight_array, n_class)
Allocates an aero_weight_array as joint flat/power-3 weightings..
Definition: aero_weight_array.F90:104
pmc_aero_particle::aero_particle_n_components
elemental integer function aero_particle_n_components(particle)
Return the number of aerosol components, or -1 if uninitialized.
Definition: aero_particle.F90:1039
pmc_aero_state::aero_state_rand_particle
subroutine aero_state_rand_particle(aero_state, i_part)
Choose a random particle from the aero_state.
Definition: aero_state.F90:837
pmc_aero_particle::aero_particle_solute_radius
real(kind=dp) function aero_particle_solute_radius(aero_particle, aero_data)
Returns the total solute radius (m).
Definition: aero_particle.F90:726
pmc_aero_state::aero_state_set_weight
subroutine aero_state_set_weight(aero_state, aero_data, weight_type, exponent)
Sets the weighting functions for an aero_state.
Definition: aero_state.F90:186
pmc_util::die_msg
subroutine die_msg(code, error_msg)
Error immediately.
Definition: util.F90:135
pmc_mpi::pmc_mpi_rank
integer function pmc_mpi_rank()
Returns the rank of the current process.
Definition: mpi.F90:117
pmc_aero_state::aero_state_ids
integer(kind=8) function, dimension(aero_state_n_part(aero_state)) aero_state_ids(aero_state)
Returns the IDs of all particles.
Definition: aero_state.F90:1028
pmc_aero_state::aero_state_set_n_part_ideal
subroutine aero_state_set_n_part_ideal(aero_state, n_part)
Set the ideal number of particles to the given value. The aero_state%awa must be already set correctl...
Definition: aero_state.F90:242
pmc_constants::dp
integer, parameter dp
Kind of a double precision real number.
Definition: constants.F90:12
pmc_aero_state::aero_state_add_particle
subroutine aero_state_add_particle(aero_state, aero_particle, aero_data, allow_resort)
Add the given particle.
Definition: aero_state.F90:367
pmc_aero_weight_array
The aero_weight_array_t structure and associated subroutines.
Definition: aero_weight_array.F90:10
pmc_aero_state::aero_state_total_particles_all_procs
integer function aero_state_total_particles_all_procs(aero_state, i_group, i_class)
Returns the total number of particles across all processes.
Definition: aero_state.F90:327
pmc_spec_file
Reading formatted text input.
Definition: spec_file.F90:43
pmc_aero_data::aero_data_n_source
elemental integer function aero_data_n_source(aero_data)
Return the number of aerosol sources, or -1 if uninitialized.
Definition: aero_data.F90:266
pmc_aero_weight_array::pmc_mpi_unpack_aero_weight_array
subroutine pmc_mpi_unpack_aero_weight_array(buffer, position, val)
Unpacks the given value from the buffer, advancing position.
Definition: aero_weight_array.F90:484
pmc_bin_grid::bin_grid_size
elemental integer function bin_grid_size(bin_grid)
Return the number of bins in the grid, or -1 if the bin grid is not allocated.
Definition: bin_grid.F90:51
pmc_aero_weight_array::aero_weight_array_input_netcdf
subroutine aero_weight_array_input_netcdf(aero_weight_array, ncid)
Read full aero_weight_array.
Definition: aero_weight_array.F90:671
pmc_aero_weight_array::aero_weight_array_set_flat
subroutine aero_weight_array_set_flat(aero_weight_array, n_class)
Allocates an aero_weight_array as flat weightings.
Definition: aero_weight_array.F90:67
pmc_util::assert
subroutine assert(code, condition_ok)
Errors unless condition_ok is true.
Definition: util.F90:104
pmc_aero_state::aero_state_dry_diameters
real(kind=dp) function, dimension(aero_state_n_part(aero_state)) aero_state_dry_diameters(aero_state, aero_data)
Returns the dry diameters of all particles.
Definition: aero_state.F90:1072
pmc_aero_state
The aero_state_t structure and assocated subroutines.
Definition: aero_state.F90:9
pmc_aero_weight_array::aero_weight_array_t
An array of aerosol size distribution weighting functions.
Definition: aero_weight_array.F90:35
pmc_aero_state::aero_state_scattering_binned
real(kind=dp) function, dimension(bin_grid_size(bin_grid)) aero_state_scattering_binned(aero_state, aero_data, bin_grid, bin_values, i_wavelength)
Returns an array of scattering coefficients based on the bin_grid sorted based on given array of valu...
Definition: aero_state.F90:3455
pmc_aero_state::aero_state_mass_entropies
real(kind=dp) function, dimension(aero_state_n_part(aero_state)) aero_state_mass_entropies(aero_state, aero_data, include, exclude, group, groups)
Returns the mass-entropies of all particles.
Definition: aero_state.F90:1420
pmc_aero_state::aero_state_weight_flat
integer, parameter aero_state_weight_flat
Single flat weighting scheme.
Definition: aero_state.F90:41
pmc_aero_state::aero_state_absorption_binned
real(kind=dp) function, dimension(bin_grid_size(bin_grid)) aero_state_absorption_binned(aero_state, aero_data, bin_grid, bin_values, i_wavelength)
Returns an array of absorption coefficients based on the bin_grid sorted based on given array of valu...
Definition: aero_state.F90:3492
pmc_integer_varray::integer_varray_n_entry
elemental integer function integer_varray_n_entry(integer_varray)
Return the current number of entries.
Definition: integer_varray.F90:31
pmc_aero_particle::aero_particle_mobility_diameter
real(kind=dp) function aero_particle_mobility_diameter(aero_particle, aero_data, env_state)
Mobility diameter of the particle (m).
Definition: aero_particle.F90:471
pmc_spec_file::spec_file_t
An input file with extra data for printing messages.
Definition: spec_file.F90:59
pmc_rand::pmc_rand_int
integer function pmc_rand_int(n)
Returns a random integer between 1 and n.
Definition: rand.F90:173
pmc_aero_particle_array::aero_particle_array_add_particle
subroutine aero_particle_array_add_particle(aero_particle_array, aero_particle)
Adds the given particle to the end of the array.
Definition: aero_particle_array.F90:160
pmc_aero_data::n_swbands
integer, parameter n_swbands
Definition: aero_data.F90:34
pmc_aero_state::aero_state_weight_nummass_specified
integer, parameter aero_state_weight_nummass_specified
Coupled number/mass weighting by specific weight classes.
Definition: aero_state.F90:57
pmc_util::sphere_vol2rad
real(kind=dp) elemental function sphere_vol2rad(v)
Convert mass-equivalent volume (m^3) to geometric radius (m) for spherical particles.
Definition: util.F90:241
pmc_aero_state::aero_state_weight_power_source
integer, parameter aero_state_weight_power_source
Power-law weighting by source.
Definition: aero_state.F90:49
pmc_aero_state::aero_state_weight_nummass
integer, parameter aero_state_weight_nummass
Coupled number/mass weighting scheme.
Definition: aero_state.F90:45
pmc_aero_particle::aero_particle_set_vols
subroutine aero_particle_set_vols(aero_particle, vols)
Sets the aerosol particle volumes.
Definition: aero_particle.F90:225
pmc_aero_state::aero_state_dup_all_particles
subroutine aero_state_dup_all_particles(aero_state_from, aero_state_to)
Copies one aero_state to another and assigns new particle IDs.
Definition: aero_state.F90:3603
pmc_aero_weight_array::aero_weight_array_num_conc
real(kind=dp) function aero_weight_array_num_conc(aero_weight_array, aero_particle, aero_data)
Compute the number concentration for a particle (m^{-3}).
Definition: aero_weight_array.F90:259
pmc_aero_state::aero_state_frozen_fraction
real(kind=dp) function aero_state_frozen_fraction(aero_state, aero_data)
Returns the frozen fraction (unitless) for whole aerosol population.. frozen fraction = number concen...
Definition: aero_state.F90:1354
pmc_aero_state::aero_state_weight_power_specified
integer, parameter aero_state_weight_power_specified
Power-law weighting by specified weight classes.
Definition: aero_state.F90:55
pmc_mpi::pmc_mpi_pack_size_real_array_2d
integer function pmc_mpi_pack_size_real_array_2d(val)
Determines the number of bytes required to pack the given value.
Definition: mpi.F90:581
pmc_util::warn_msg
subroutine warn_msg(code, warning_msg, already_warned)
Prints a warning message.
Definition: util.F90:38
pmc_aero_particle_array::aero_particle_array_n_components
elemental integer function aero_particle_array_n_components(aero_particle_array)
Returns the total number of components in the aero_particle_array.
Definition: aero_particle_array.F90:311
pmc_aero_weight_array::aero_weight_array_num_conc_at_radius
real(kind=dp) function aero_weight_array_num_conc_at_radius(aero_weight_array, i_class, radius)
Compute the total number concentration at a given radius (m^3).
Definition: aero_weight_array.F90:234
pmc_spec_file::spec_file_read_real
subroutine spec_file_read_real(file, name, var)
Read a real number from a spec file that must have the given name.
Definition: spec_file.F90:563
pmc_aero_state::aero_state_check
subroutine aero_state_check(aero_state, aero_data)
Check that aerosol state data is consistent.
Definition: aero_state.F90:3313
pmc_aero_data::aero_data_netcdf_dim_optical_wavelengths
subroutine aero_data_netcdf_dim_optical_wavelengths(aero_data, ncid, dimid_optical_wavelengths)
Write the number of wavelength dimension to the given NetCDF file if it is not already present and in...
Definition: aero_data.F90:842
pmc_util::integer_to_string
character(len=pmc_util_convert_string_len) function integer_to_string(val)
Convert an integer to a string format.
Definition: util.F90:767
pmc_util::assert_msg
subroutine assert_msg(code, condition_ok, error_msg)
Errors unless condition_ok is true.
Definition: util.F90:78
pmc_aero_particle::aero_particle_approx_crit_rel_humid
real(kind=dp) function aero_particle_approx_crit_rel_humid(aero_particle, aero_data, env_state)
Returns the approximate critical relative humidity (1).
Definition: aero_particle.F90:779
pmc_aero_state::aero_state_absorption
real(kind=dp) function aero_state_absorption(aero_state, aero_data, wavelength)
Returns the total absorption coefficient of a population.
Definition: aero_state.F90:3424
pmc_aero_dist
The aero_dist_t structure and associated subroutines.
Definition: aero_dist.F90:18
pmc_aero_weight_array::aero_weight_array_combine
subroutine aero_weight_array_combine(aero_weight_array, aero_weight_array_delta)
Combine aero_weight_array_delta into aero_weight_array with a harmonic mean.
Definition: aero_weight_array.F90:177
pmc_aero_state::aero_state_weight_class_for_source
integer function aero_state_weight_class_for_source(aero_state, source)
Determine the appropriate weight class for a source.
Definition: aero_state.F90:264
pmc_aero_particle_array::aero_particle_array_n_part
elemental integer function aero_particle_array_n_part(aero_particle_array)
Return the current number of particles.
Definition: aero_particle_array.F90:54
pmc_aero_particle::aero_particle_species_masses
real(kind=dp) function, dimension(aero_data_n_spec(aero_data)) aero_particle_species_masses(aero_particle, aero_data)
Mass of all species in the particle (kg).
Definition: aero_particle.F90:302
pmc_aero_state::aero_state_remove_particle_with_info
subroutine aero_state_remove_particle_with_info(aero_state, i_part, aero_info)
Remove the given particle and record the removal.
Definition: aero_state.F90:410
pmc_aero_state::aero_state_total_particles
integer function aero_state_total_particles(aero_state, i_group, i_class)
Returns the total number of particles in an aerosol distribution.
Definition: aero_state.F90:288
pmc_aero_state::aero_state_mpi_alltoall
subroutine aero_state_mpi_alltoall(send, recv)
Do an MPI all-to-all transfer of aerosol states.
Definition: aero_state.F90:2014
pmc_aero_state::aero_state_bin_average_size
subroutine aero_state_bin_average_size(aero_state, bin_grid, aero_data, bin_center, preserve_number)
Set each aerosol particle to have its original species ratios, but total volume given by the average ...
Definition: aero_state.F90:2147
pmc_aero_info::aero_info_none
integer, parameter aero_info_none
No information.
Definition: aero_info.F90:19
pmc_integer_varray
The integer_varray_t structure and assocated subroutines.
Definition: integer_varray.F90:9
pmc_aero_info_array::pmc_mpi_pack_size_aia
integer function pmc_mpi_pack_size_aia(val)
Determines the number of bytes required to pack the given value.
Definition: aero_info_array.F90:221
pmc_aero_info_array::aero_info_array_add
subroutine aero_info_array_add(aero_info_array, aero_info_array_delta)
Adds aero_info_array_delta to the end of aero_info_array.
Definition: aero_info_array.F90:198
pmc_mpi::pmc_mpi_pack_real_array_2d
subroutine pmc_mpi_pack_real_array_2d(buffer, position, val)
Packs the given value into the buffer, advancing position.
Definition: mpi.F90:991
pmc_aero_state::aero_state_num_concs_by_source
real(kind=dp) function, dimension( aero_data_n_source(aero_data)) aero_state_num_concs_by_source(aero_state, aero_data)
Returns the total number concentration associated with each aerosol source category....
Definition: aero_state.F90:1318
pmc_aero_info_array::aero_info_array_zero
subroutine aero_info_array_zero(aero_info_array)
Sets an aero_info_array to contain zero data.
Definition: aero_info_array.F90:63
pmc_aero_state::aero_state_reweight
subroutine aero_state_reweight(aero_state, aero_data, reweight_num_conc)
Reweight all particles after their constituent volumes have been altered.
Definition: aero_state.F90:598
pmc_aero_weight_array::aero_weight_array_normalize
elemental subroutine aero_weight_array_normalize(aero_weight_array)
Normalizes the aero_weight_array to a non-zero value.
Definition: aero_weight_array.F90:124
pmc_aero_weight_array::aero_weight_array_set_power
subroutine aero_weight_array_set_power(aero_weight_array, n_class, exponent)
Allocates an aero_weight_array as power weightings.
Definition: aero_weight_array.F90:85
pmc_mpi::pmc_mpi_alltoall_integer
subroutine pmc_mpi_alltoall_integer(send, recv)
Does an all-to-all transfer of integers.
Definition: mpi.F90:1952
pmc_aero_state::aero_state_dup_particle
subroutine aero_state_dup_particle(aero_state, aero_data, i_part, n_part_mean, random_weight_group)
Add copies or remove a particle, with a given mean number of resulting particles.
Definition: aero_state.F90:490
pmc_aero_state::aero_state_num_concs
real(kind=dp) function, dimension(aero_state_n_part(aero_state)) aero_state_num_concs(aero_state, aero_data)
Returns the number concentrations of all particles.
Definition: aero_state.F90:1244
pmc_aero_particle::aero_particle_zero
subroutine aero_particle_zero(aero_particle, aero_data)
Resets an aero_particle to be zero.
Definition: aero_particle.F90:119
pmc_aero_state::aero_state_scattering
real(kind=dp) function aero_state_scattering(aero_state, aero_data, wavelength)
Return the total scattering coefficient of a population.
Definition: aero_state.F90:3394
pmc_rand
Random number generators.
Definition: rand.F90:9
pmc_aero_info_array
The aero_info_array_t structure and assoicated subroutines.
Definition: aero_info_array.F90:9
pmc_aero_weight_array::aero_weight_array_rand_group
integer function aero_weight_array_rand_group(aero_weight_array, i_class, radius)
Choose a random group at the given radius, with probability inversely proportional to group weight at...
Definition: aero_weight_array.F90:376
pmc_aero_sorted
The aero_sorted_t structure and assocated subroutines.
Definition: aero_sorted.F90:9
pmc_aero_state::aero_state_input_netcdf
subroutine aero_state_input_netcdf(aero_state, ncid, aero_data)
Read full state.
Definition: aero_state.F90:3087
pmc_aero_state::aero_state_particle_num_conc
real(kind=dp) function aero_state_particle_num_conc(aero_state, aero_particle, aero_data)
The number concentration of a single particle (m^{-3}).
Definition: aero_state.F90:546
pmc_aero_data::aero_data_vol2rad
real(kind=dp) elemental function aero_data_vol2rad(aero_data, v)
Convert mass-equivalent volume (m^3) to geometric radius (m).
Definition: aero_data.F90:106
pmc_aero_state::aero_state_remove_particle_no_info
subroutine aero_state_remove_particle_no_info(aero_state, i_part)
Remove the given particle without recording it.
Definition: aero_state.F90:390
pmc_aero_state::aero_state_to_binned_dry
subroutine aero_state_to_binned_dry(bin_grid, aero_data, aero_state, aero_binned)
Does the same thing as aero_state_to_bin() but based on dry radius.
Definition: aero_state.F90:1683
pmc_aero_state::aero_state_double
subroutine aero_state_double(aero_state, aero_data, i_group, i_class)
Doubles number of particles in the given weight group.
Definition: aero_state.F90:1723
pmc_aero_info::aero_info_halved
integer, parameter aero_info_halved
Particle was removed due to halving of the aerosol population.
Definition: aero_info.F90:25
pmc_aero_data::aero_data_t
Aerosol material properties and associated data.
Definition: aero_data.F90:55
pmc_aero_state::aero_state_mixing_state_metrics
subroutine aero_state_mixing_state_metrics(aero_state, aero_data, d_alpha, d_gamma, chi, include, exclude, group, groups)
Returns the mixing state metrics of the population.
Definition: aero_state.F90:1554
pmc_aero_state::aero_state_copy_weight
subroutine aero_state_copy_weight(aero_state_from, aero_state_to)
Copies weighting information for an aero_state.
Definition: aero_state.F90:171
pmc_util::entropy
real(kind=dp) function entropy(p)
Compute the entropy of a probability mass function (non necessarily normalized).
Definition: util.F90:1947
pmc_aero_state::aero_state_mpi_gather
subroutine aero_state_mpi_gather(aero_state, aero_state_total, aero_data)
Gathers data from all processes into one aero_state on process 0.
Definition: aero_state.F90:2445
pmc_aero_state::pmc_mpi_unpack_aero_state
subroutine pmc_mpi_unpack_aero_state(buffer, position, val)
Unpacks the given value from the buffer, advancing position.
Definition: aero_state.F90:2418
pmc_aero_state::aero_state_weight_none
integer, parameter aero_state_weight_none
Single flat weighting scheme.
Definition: aero_state.F90:39
pmc_aero_data::aero_data_netcdf_dim_aero_species
subroutine aero_data_netcdf_dim_aero_species(aero_data, ncid, dimid_aero_species)
Write the aero species dimension to the given NetCDF file if it is not already present and in any cas...
Definition: aero_data.F90:670
pmc_aero_state::aero_state_approx_crit_rel_humids
real(kind=dp) function, dimension(aero_state_n_part(aero_state)) aero_state_approx_crit_rel_humids(aero_state, aero_data, env_state)
Returns the approximate critical relative humidity for all particles (1).
Definition: aero_state.F90:1632
pmc_mpi::pmc_mpi_check_ierr
subroutine pmc_mpi_check_ierr(ierr)
Dies if ierr is not ok.
Definition: mpi.F90:40
pmc_aero_dist::aero_dist_t
A complete aerosol distribution, consisting of several modes.
Definition: aero_dist.F90:33
pmc_aero_state::aero_state_to_binned
subroutine aero_state_to_binned(bin_grid, aero_data, aero_state, aero_binned)
Create binned number and mass arrays.
Definition: aero_state.F90:989
pmc_aero_state::pmc_mpi_pack_aero_state
subroutine pmc_mpi_pack_aero_state(buffer, position, val)
Packs the given value into the buffer, advancing position.
Definition: aero_state.F90:2392
pmc_aero_particle::aero_particle_dry_diameter
elemental real(kind=dp) function aero_particle_dry_diameter(aero_particle, aero_data)
Total dry diameter of the particle (m).
Definition: aero_particle.F90:455
pmc_aero_sorted::aero_sorted_t
Sorting of particles into bins.
Definition: aero_sorted.F90:47
pmc_aero_state::aero_state_tag_scatter
integer, parameter aero_state_tag_scatter
MPI tag for scattering between processes.
Definition: aero_state.F90:36
pmc_aero_sorted::aero_sorted_add_particle
subroutine aero_sorted_add_particle(aero_sorted, aero_particle_array, aero_particle, aero_data, allow_resort)
Add a new particle to both an aero_sorted and the corresponding aero_particle_array.
Definition: aero_sorted.F90:399
pmc_aero_state::pmc_mpi_pack_size_aero_state
integer function pmc_mpi_pack_size_aero_state(val)
Determines the number of bytes required to pack the given value.
Definition: aero_state.F90:2373
pmc_aero_state::aero_state_volumes
real(kind=dp) function, dimension(aero_state_n_part(aero_state)) aero_state_volumes(aero_state, aero_data, include, exclude)
Returns the volumes of all particles.
Definition: aero_state.F90:1122
pmc_aero_info::aero_info_t
Information about removed particles describing the sink.
Definition: aero_info.F90:48
pmc_util
Common utility subroutines.
Definition: util.F90:9
pmc_aero_weight_array::aero_weight_array_shift
subroutine aero_weight_array_shift(aero_weight_array_from, aero_weight_array_to, sample_prop, overwrite_to)
Adjust source and destination weights to reflect moving sample_prop proportion of particles from aero...
Definition: aero_weight_array.F90:195
pmc_mpi::pmc_mpi_unpack_real_array_2d
subroutine pmc_mpi_unpack_real_array_2d(buffer, position, val)
Unpacks the given value from the buffer, advancing position.
Definition: mpi.F90:1420
pmc_aero_state::aero_state_rebalance
subroutine aero_state_rebalance(aero_state, aero_data, allow_doubling, allow_halving, initial_state_warning)
Double or halve the particle population in each weight group to maintain close to n_part_ideal partic...
Definition: aero_state.F90:1794
pmc_aero_info
The aero_info_t structure and associated subroutines.
Definition: aero_info.F90:9
pmc_aero_state::aero_state_add
subroutine aero_state_add(aero_state, aero_state_delta, aero_data)
aero_state += aero_state_delta, including combining the weights, so the new concentration is the weig...
Definition: aero_state.F90:661
pmc_aero_state::aero_state_sample
subroutine aero_state_sample(aero_state_from, aero_state_to, aero_data, sample_prob, removal_action)
Generates a random sample by removing particles from aero_state_from and adding them to aero_state_to...
Definition: aero_state.F90:938
pmc_spec_file::spec_file_die_msg
subroutine spec_file_die_msg(code, file, msg)
Exit with an error message containing filename and line number.
Definition: spec_file.F90:74
pmc_aero_sorted::aero_sorted_remake_if_needed
subroutine aero_sorted_remake_if_needed(aero_sorted, aero_particle_array, aero_data, valid_sort, n_group, n_class, bin_grid, all_procs_same)
Remake a sorting if particles are getting too close to the edges.
Definition: aero_sorted.F90:216
pmc_aero_state::aero_state_halve
subroutine aero_state_halve(aero_state, i_group, i_class)
Remove approximately half of the particles in the given weight group.
Definition: aero_state.F90:1754
pmc_aero_info_array::aero_info_array_t
1-D arrays of aero_info_t structure.
Definition: aero_info_array.F90:33
pmc_util::almost_equal
logical function almost_equal(d1, d2)
Tests whether two real numbers are almost equal using only a relative tolerance.
Definition: util.F90:319
pmc_aero_binned
The aero_binned_t structure and associated subroutines.
Definition: aero_binned.F90:9
pmc_aero_state::aero_state_crit_rel_humids
real(kind=dp) function, dimension(aero_state_n_part(aero_state)) aero_state_crit_rel_humids(aero_state, aero_data, env_state)
Returns the critical relative humidity for all particles (1).
Definition: aero_state.F90:1658
pmc_aero_state::aero_state_total_num_conc
real(kind=dp) function aero_state_total_num_conc(aero_state, aero_data)
Returns the total number concentration.
Definition: aero_state.F90:1267
pmc_aero_particle_array::aero_particle_array_check
subroutine aero_particle_array_check(aero_particle_array, aero_data, continue_on_error)
Check that the particle array data is consistent.
Definition: aero_particle_array.F90:281
pmc_aero_particle::aero_particle_radius
elemental real(kind=dp) function aero_particle_radius(aero_particle, aero_data)
Total radius of the particle (m).
Definition: aero_particle.F90:407
pmc_spec_file::spec_file_read_string
subroutine spec_file_read_string(file, name, var)
Read a string from a spec file that must have a given name.
Definition: spec_file.F90:605
pmc_constants::dc
integer, parameter dc
Kind of a double precision complex number.
Definition: constants.F90:14
pmc_aero_particle_array::pmc_mpi_unpack_aero_particle_array
subroutine pmc_mpi_unpack_aero_particle_array(buffer, position, val)
Unpacks the given value from the buffer, advancing position.
Definition: aero_particle_array.F90:252
pmc_aero_state::aero_state_mix
subroutine aero_state_mix(aero_state, del_t, mix_timescale, aero_data, specify_prob_transfer)
Mix the aero_states between all processes. Currently uses a simple all-to-all diffusion.
Definition: aero_state.F90:1930
pmc_aero_binned::aero_binned_t
Aerosol number and volume distributions stored per bin.
Definition: aero_binned.F90:37
pmc_aero_state::aero_state_masses
real(kind=dp) function, dimension(aero_state_n_part(aero_state)) aero_state_masses(aero_state, aero_data, include, exclude)
Returns the masses of all particles.
Definition: aero_state.F90:1187
pmc_aero_particle::next_id
integer(kind=8), save next_id
Next unique ID number to use for a particle.
Definition: aero_particle.F90:72
pmc_aero_particle::aero_particle_species_mass
elemental real(kind=dp) function aero_particle_species_mass(aero_particle, i_spec, aero_data)
Mass of a single species in the particle (kg).
Definition: aero_particle.F90:285
pmc_bin_grid
The bin_grid_t structure and associated subroutines.
Definition: bin_grid.F90:9
pmc_aero_data::aero_data_rad2vol
real(kind=dp) elemental function aero_data_rad2vol(aero_data, r)
Convert geometric radius (m) to mass-equivalent volume (m^3).
Definition: aero_data.F90:136
pmc_aero_data
The aero_data_t structure and associated subroutines.
Definition: aero_data.F90:9
pmc_aero_weight_array::pmc_mpi_pack_aero_weight_array
subroutine pmc_mpi_pack_aero_weight_array(buffer, position, val)
Packs the given value into the buffer, advancing position.
Definition: aero_weight_array.F90:449
pmc_bin_grid::bin_grid_t
1D grid, either logarithmic or linear.
Definition: bin_grid.F90:33
pmc_aero_weight_array::aero_weight_array_check_flat
logical function aero_weight_array_check_flat(aero_weight_array)
Check whether a given aero_weight array is flat in total.
Definition: aero_weight_array.F90:277
pmc_aero_state::aero_state_prepare_weight_for_add
subroutine aero_state_prepare_weight_for_add(aero_state, aero_data, i_group, i_class, n_add, allow_doubling, allow_halving)
Change the weight if necessary to ensure that the addition of about n_add computational particles wil...
Definition: aero_state.F90:706
pmc_aero_state::aero_state_total_n_components
elemental integer function aero_state_total_n_components(aero_state)
Returns the total number of components for all particles in an aero_state.
Definition: aero_state.F90:3360
pmc_aero_info::aero_info_weight
integer, parameter aero_info_weight
Particle was removed due to adjustments in the particle's weighting function.
Definition: aero_info.F90:28
pmc_aero_particle_array
The aero_particle_array_t structure and assoicated subroutines.
Definition: aero_particle_array.F90:9
pmc_aero_state::aero_state_mixing_state_metrics_binned
subroutine aero_state_mixing_state_metrics_binned(aero_state, aero_data, bin_grid, bin_values, d_alpha, d_gamma, chi, include, exclude, group, groups)
Returns arrays of mixing state metrics of the particles within given size ranges based on a given bin...
Definition: aero_state.F90:3542
pmc_bin_grid::bin_grid_contains
logical function bin_grid_contains(bin_grid, i_bin, val)
Determines if a value is in a given bin.
Definition: bin_grid.F90:178
pmc_aero_sorted::aero_sorted_remove_particle
subroutine aero_sorted_remove_particle(aero_sorted, aero_particle_array, i_part)
Remove a particle from both an aero_sorted and the corresponding aero_particle_array.
Definition: aero_sorted.F90:457
pmc_rand::rand_binomial
integer function rand_binomial(n, p)
Generate a Binomial-distributed random number with the given parameters.
Definition: rand.F90:320
pmc_aero_particle_array::aero_particle_array_zero
subroutine aero_particle_array_zero(aero_particle_array)
Resets an aero_particle_array to contain zero particles.
Definition: aero_particle_array.F90:70
pmc_bin_grid::bin_grid_type_log
integer, parameter bin_grid_type_log
Logarithmically spaced bin grid.
Definition: bin_grid.F90:23
pmc_aero_state::aero_state_tag_gather
integer, parameter aero_state_tag_gather
MPI tag for gathering between processes.
Definition: aero_state.F90:34
pmc_bin_grid::bin_grid_make
subroutine bin_grid_make(bin_grid, type, n_bin, min, max)
Generates the bin grid given the range and number of bins.
Definition: bin_grid.F90:84
pmc_aero_state::aero_state_t
The current collection of aerosol particles.
Definition: aero_state.F90:69
pmc_aero_particle::aero_particle_mass
elemental real(kind=dp) function aero_particle_mass(aero_particle, aero_data)
Total mass of the particle (kg).
Definition: aero_particle.F90:270
pmc_aero_state::aero_state_weight_flat_source
integer, parameter aero_state_weight_flat_source
Flat weighting by source.
Definition: aero_state.F90:47
pmc_aero_data::aero_data_spec_by_name
integer function aero_data_spec_by_name(aero_data, name)
Returns the number of the species in aero_data with the given name, or returns 0 if there is no such ...
Definition: aero_data.F90:299
pmc_aero_weight_array::aero_weight_array_n_group
integer function aero_weight_array_n_group(aero_weight_array)
Return the number of weight groups.
Definition: aero_weight_array.F90:136
pmc_aero_state::aero_state_weight_power
integer, parameter aero_state_weight_power
Power-law weighting scheme.
Definition: aero_state.F90:43
pmc_aero_data::aero_data_netcdf_dim_aero_source
subroutine aero_data_netcdf_dim_aero_source(aero_data, ncid, dimid_aero_source)
Write the aero source dimension to the given NetCDF file if it is not already present and in any case...
Definition: aero_data.F90:727
pmc_aero_state::aero_state_diameters
real(kind=dp) function, dimension(aero_state_n_part(aero_state)) aero_state_diameters(aero_state, aero_data, include, exclude)
Returns the diameters of all particles.
Definition: aero_state.F90:1047
pmc_aero_state::aero_state_sort
subroutine aero_state_sort(aero_state, aero_data, bin_grid, all_procs_same)
Sorts the particles if necessary.
Definition: aero_state.F90:3292
pmc_aero_state::aero_state_mobility_diameters
real(kind=dp) function, dimension( aero_state_n_part(aero_state)) aero_state_mobility_diameters(aero_state, aero_data, env_state)
Returns the mobility diameters of all particles.
Definition: aero_state.F90:1090
pmc_rand::pmc_random
real(kind=dp) function pmc_random()
Returns a random number between 0 and 1.
Definition: rand.F90:139
pmc_aero_particle::aero_particle_volume
elemental real(kind=dp) function aero_particle_volume(aero_particle)
Total volume of the particle (m^3).
Definition: aero_particle.F90:334
pmc_bin_grid::bin_grid_find
integer function bin_grid_find(bin_grid, val)
Find the bin number that contains a given value.
Definition: bin_grid.F90:144
pmc_aero_state::aero_state_sample_particles
subroutine aero_state_sample_particles(aero_state_from, aero_state_to, aero_data, sample_prob, removal_action)
Generates a random sample by removing particles from aero_state_from and adding them to aero_state_to...
Definition: aero_state.F90:858
pmc_aero_state::aero_state_netcdf_dim_aero_removed
subroutine aero_state_netcdf_dim_aero_removed(aero_state, ncid, dimid_aero_removed)
Write the aero removed dimension to the given NetCDF file if it is not already present and in any cas...
Definition: aero_state.F90:2561
pmc_aero_particle::aero_particle_set_component
subroutine aero_particle_set_component(aero_particle, i_source, create_time)
Set the initial aero_component for a particle.
Definition: aero_particle.F90:187
pmc_aero_state::aero_state_add_particles
subroutine aero_state_add_particles(aero_state, aero_state_delta, aero_data)
aero_state += aero_state_delta, with the weight of aero_state left unchanged, so the new concentratio...
Definition: aero_state.F90:680
pmc_aero_info_array::pmc_mpi_unpack_aero_info_array
subroutine pmc_mpi_unpack_aero_info_array(buffer, position, val)
Unpacks the given value from the buffer, advancing position.
Definition: aero_info_array.F90:268
pmc_aero_particle_array::aero_particle_array_remove_particle
subroutine aero_particle_array_remove_particle(aero_particle_array, index)
Removes the particle at the given index.
Definition: aero_particle_array.F90:180