56 character(len=AERO_MODE_NAME_LEN) :: name
60 real(kind=
dp) :: char_radius
62 real(kind=
dp) :: log10_std_dev_radius
64 real(kind=
dp),
allocatable :: sample_radius(:)
66 real(kind=
dp),
allocatable :: sample_num_conc(:)
68 real(kind=
dp) :: num_conc
70 real(kind=
dp),
allocatable :: vol_frac(:)
73 real(kind=
dp),
allocatable :: vol_frac_std(:)
77 integer :: weight_class
88 integer,
intent(in) :: type
122 call die_msg(719625922,
"unknown aero_mode type: " &
132 log10_sigma_g, bin_grid, num_conc)
135 real(kind=
dp),
intent(in) :: total_num_conc
137 real(kind=
dp),
intent(in) :: geom_mean_radius
139 real(kind=
dp),
intent(in) :: log10_sigma_g
148 num_conc(k) = total_num_conc / (sqrt(2d0 *
const%pi) &
149 * log10_sigma_g) * dexp(-(dlog10(bin_grid%centers(k)) &
150 - dlog10(geom_mean_radius))**2d0 &
151 / (2d0 * log10_sigma_g**2d0)) / dlog(10d0)
166 geom_mean_radius, log10_sigma_g, bin_grid, aero_data, vol_conc)
169 real(kind=
dp),
intent(in) :: total_num_conc
171 real(kind=
dp),
intent(in) :: geom_mean_radius
173 real(kind=
dp),
intent(in) :: log10_sigma_g
184 log10_sigma_g, bin_grid, num_conc)
197 bin_grid, aero_data, num_conc)
200 real(kind=
dp),
intent(in) :: total_num_conc
202 real(kind=
dp),
intent(in) :: radius_at_mean_vol
211 real(kind=
dp) :: mean_vol, num_conc_vol
215 num_conc_vol = total_num_conc / mean_vol &
218 call vol_to_lnr(bin_grid%centers(k), num_conc_vol, num_conc(k))
227 bin_grid, aero_data, vol_conc)
230 real(kind=
dp),
intent(in) :: total_num_conc
232 real(kind=
dp),
intent(in) :: radius_at_mean_vol
243 bin_grid, aero_data, num_conc)
255 real(kind=
dp),
intent(in) :: total_num_conc
257 real(kind=
dp),
intent(in) :: radius
268 call warn_msg(825666877,
"monodisperse radius outside of bin_grid")
270 num_conc(k) = total_num_conc / bin_grid%widths(k)
279 bin_grid, aero_data, vol_conc)
282 real(kind=
dp),
intent(in) :: total_num_conc
284 real(kind=
dp),
intent(in) :: radius
297 call warn_msg(420930707,
"monodisperse radius outside of bin_grid")
299 vol_conc(k) = total_num_conc / bin_grid%widths(k) &
312 real(kind=
dp),
intent(in) :: sample_radius(:)
314 real(kind=
dp),
intent(in) :: sample_num_conc(:)
320 integer :: i_sample, n_sample, i_lower, i_upper, i_bin
321 real(kind=
dp) :: r_lower, r_upper
322 real(kind=
dp) :: r_bin_lower, r_bin_upper, r1, r2, ratio
324 n_sample =
size(sample_num_conc)
325 call assert(188766208,
size(sample_radius) == n_sample + 1)
326 call assert(295384037, n_sample >= 1)
329 do i_sample = 1,n_sample
330 r_lower = sample_radius(i_sample)
331 r_upper = sample_radius(i_sample + 1)
334 if (i_upper < 1) cycle
336 i_lower = max(1, i_lower)
338 do i_bin = i_lower,i_upper
339 r_bin_lower = bin_grid%edges(i_bin)
340 r_bin_upper = bin_grid%edges(i_bin + 1)
341 r1 = max(r_lower, r_bin_lower)
342 r2 = min(r_upper, r_bin_upper)
343 ratio = (log(r2) - log(r1)) / (log(r_upper) - log(r_lower))
344 num_conc(i_bin) = num_conc(i_bin) + ratio &
345 * sample_num_conc(i_sample) / bin_grid%widths(i_bin)
355 bin_grid, aero_data, vol_conc)
358 real(kind=
dp),
intent(in) :: sample_radius(:)
360 real(kind=
dp),
intent(in) :: sample_num_conc(:)
392 aero_mode%log10_std_dev_radius, bin_grid, num_conc)
395 aero_mode%char_radius, bin_grid, aero_data, num_conc)
397 call num_conc_mono(aero_mode%num_conc, aero_mode%char_radius, &
401 aero_mode%sample_num_conc, bin_grid, num_conc)
403 call die_msg(223903246,
"unknown aero_mode type: " &
431 aero_mode%char_radius, aero_mode%log10_std_dev_radius, &
432 bin_grid, aero_data, vol_conc_total)
435 aero_mode%char_radius, bin_grid, aero_data, vol_conc_total)
438 aero_mode%char_radius, bin_grid, aero_data, vol_conc_total)
441 aero_mode%sample_num_conc, bin_grid, aero_data, vol_conc_total)
443 call die_msg(314169653,
"Unknown aero_mode type: " &
446 call assert_msg(756593082, sum(aero_mode%vol_frac_std) == 0d0, &
447 "cannot convert species fractions with non-zero standard deviation " &
448 //
"to binned distributions")
450 vol_conc(:,i_spec) = vol_conc_total * aero_mode%vol_frac(i_spec)
466 real(kind=
dp),
intent(out) :: weighted_num_conc(:)
469 real(kind=
dp) :: x0, x1
473 size(weighted_num_conc) ==
size(aero_mode%sample_num_conc))
476 weighted_num_conc = aero_mode%sample_num_conc
479 do i_sample = 1,
size(aero_mode%sample_num_conc)
480 x0 = log(aero_mode%sample_radius(i_sample))
481 x1 = log(aero_mode%sample_radius(i_sample + 1))
482 weighted_num_conc(i_sample) = aero_mode%sample_num_conc(i_sample) &
483 / aero_weight%exponent * (exp(- aero_weight%exponent * x0) &
484 - exp(- aero_weight%exponent * x1)) / (x1 - x0)
487 call die_msg(576124393,
"unknown aero_weight type: " &
503 real(kind=
dp) :: x_mean_prime
504 real(kind=
dp),
allocatable :: weighted_num_conc(:)
512 x_mean_prime = log10(aero_mode%char_radius) &
513 - aero_weight%exponent * aero_mode%log10_std_dev_radius**2 &
516 * exp((x_mean_prime**2 - log10(aero_mode%char_radius)**2) &
517 / (2d0 * aero_mode%log10_std_dev_radius**2))
519 call die_msg(466668240,
"unknown aero_weight type: " &
527 "cannot use exponential modes with weighting")
532 aero_mode%char_radius)
534 allocate(weighted_num_conc(
size(aero_mode%sample_num_conc)))
538 deallocate(weighted_num_conc)
540 call die_msg(901140225,
"unknown aero_mode type: " &
550 radius, prob_threshold)
559 real(kind=
dp),
intent(out) :: radius
561 real(kind=
dp),
intent(in) :: prob_threshold
563 real(kind=
dp) :: x_mean_prime, x0, x1, x, r, inv_nc0, inv_nc1, inv_nc
565 real(kind=
dp),
allocatable :: weighted_num_conc(:)
569 x_mean_prime = log10(aero_mode%char_radius)
572 x_mean_prime = log10(aero_mode%char_radius) &
573 - aero_weight%exponent * aero_mode%log10_std_dev_radius**2 &
576 call die_msg(517376844,
"unknown aero_weight type: " &
580 aero_mode%log10_std_dev_radius, prob_threshold)
582 allocate(weighted_num_conc(
size(aero_mode%sample_num_conc)))
586 deallocate(weighted_num_conc)
590 .and. (aero_weight%exponent == 0d0)))
then
591 x0 = log(aero_mode%sample_radius(i_sample))
592 x1 = log(aero_mode%sample_radius(i_sample + 1))
594 x = (1d0 - r) * x0 + r * x1
599 aero_mode%sample_radius(i_sample))
601 aero_mode%sample_radius(i_sample + 1))
603 inv_nc = (1d0 - r) * inv_nc0 + r * inv_nc1
606 call die_msg(769131141,
"unknown aero_weight type: " &
616 "cannot use exponential modes with weighting")
618 call die_msg(301787712,
"unknown aero_weight type: " &
622 radius = aero_mode%char_radius
624 call die_msg(749122931,
"Unknown aero_mode type: " &
638 real(kind=
dp),
intent(in) :: total_vol
640 real(kind=
dp),
intent(out) :: vols(
size(aero_mode%vol_frac))
642 integer,
parameter :: aero_mode_max_sample_loops = 1000000
645 real(kind=
dp) :: offset
650 do i_sample = 1,aero_mode_max_sample_loops
655 offset = 1d0 - sum(vols)
656 vols = vols + offset * aero_mode%vol_frac
657 if (minval(vols) >= 0d0)
exit
659 if (i_sample == aero_mode_max_sample_loops)
then
660 call die_msg(549015143,
"Unable to sample non-negative volumes for " &
661 //
"mode: " // trim(aero_mode%name))
663 vols = vols / sum(vols) * total_vol
682 subroutine spec_file_read_vol_frac(file, aero_data, vol_frac, vol_frac_std)
689 real(kind=
dp),
allocatable,
intent(inout) :: vol_frac(:)
691 real(kind=
dp),
allocatable,
intent(inout) :: vol_frac_std(:)
693 integer :: n_species, species, i
694 character(len=SPEC_LINE_MAX_VAR_LEN),
allocatable :: species_name(:)
695 real(kind=
dp),
allocatable :: species_data(:,:)
696 real(kind=
dp) :: tot_vol_frac
744 n_species =
size(species_data, 1)
745 if (n_species < 1)
then
746 call die_msg(628123166,
'file ' // trim(file%name) &
747 //
' must contain at least one line of data')
749 if ((
size(species_data, 2) /= 1) .and. (
size(species_data, 2) /= 2))
then
750 call die_msg(427666881,
'each line in file ' // trim(file%name) &
751 //
' must contain exactly one or two data values')
755 if (
allocated(vol_frac))
deallocate(vol_frac)
756 if (
allocated(vol_frac_std))
deallocate(vol_frac_std)
763 if (species == 0)
then
764 call die_msg(775942501,
'unknown species ' // trim(species_name(i)) &
765 //
' in file ' // trim(file%name))
767 vol_frac(species) = species_data(i, 1)
768 if (
size(species_data, 2) == 2)
then
769 vol_frac_std(species) = species_data(i, 2)
774 vol_frac = vol_frac / aero_data%density
775 vol_frac_std = vol_frac_std / aero_data%density
778 tot_vol_frac = sum(vol_frac)
779 if ((minval(vol_frac) < 0d0) .or. (tot_vol_frac <= 0d0))
then
780 call die_msg(356648030,
'fractions in ' // trim(file%name) &
781 //
' are not positive')
783 if (minval(vol_frac_std) < 0d0)
then
784 call die_msg(676576501,
'standard deviations in ' // trim(file%name) &
785 //
' are not positive')
787 vol_frac = vol_frac / tot_vol_frac
788 vol_frac_std = vol_frac_std / tot_vol_frac
790 end subroutine spec_file_read_vol_frac
795 subroutine spec_file_read_size_dist(file, sample_radius, sample_num_conc)
800 real(kind=
dp),
allocatable,
intent(inout) :: sample_radius(:)
802 real(kind=
dp),
allocatable,
intent(inout) :: sample_num_conc(:)
804 character(len=SPEC_LINE_MAX_VAR_LEN),
allocatable :: names(:)
805 real(kind=
dp),
allocatable ::
data(:,:)
806 integer :: n_sample, i_sample
843 'must contain a line starting with "diam"')
845 n_sample =
size(
data,2) - 1
847 'must have at least two diam values')
849 if (
allocated(sample_radius))
deallocate(sample_radius)
850 allocate(sample_radius(n_sample + 1))
852 do i_sample = 1,n_sample
854 sample_radius(i_sample) < sample_radius(i_sample + 1), &
855 'diam values must be strictly increasing')
860 'must contain a line starting with "num_conc"')
864 'must have one fewer num_conc than diam values')
866 if (
allocated(sample_num_conc))
deallocate(sample_num_conc)
867 allocate(sample_num_conc(n_sample))
868 sample_num_conc =
data(1,:)
869 do i_sample = 1,n_sample
871 sample_num_conc(i_sample) >= 0d0, &
872 'num_conc values must be non-negative')
875 end subroutine spec_file_read_size_dist
881 subroutine spec_file_read_aero_mode(file, aero_data, &
882 read_aero_weight_classes, aero_mode, eof)
893 logical,
intent(in) :: read_aero_weight_classes
895 character(len=SPEC_LINE_MAX_VAR_LEN) :: tmp_str, mode_type, diam_type_str
896 character(len=SPEC_LINE_MAX_VAR_LEN) :: mass_frac_filename
897 character(len=SPEC_LINE_MAX_VAR_LEN) :: size_dist_filename
898 character(len=SPEC_LINE_MAX_VAR_LEN) :: weight_class_name
899 type(spec_line_t) :: line
900 type(
spec_file_t) :: mass_frac_file, size_dist_file
901 real(kind=
dp) :: diam, temp, pressure
902 integer :: diam_type, i_radius
903 real(kind=
dp) :: log10_r0_mob, log10_r1_mob, log10_r2_mob, &
904 r0_mob, r2_mob, r0_geom, r2_geom, log10_r0_geom, log10_r2_geom
1003 tmp_str = line%data(1)
1006 if (read_aero_weight_classes)
then
1010 weight_class_name = aero_mode%name
1016 call spec_file_read_vol_frac(mass_frac_file, aero_data, &
1017 aero_mode%vol_frac, aero_mode%vol_frac_std)
1021 if (trim(diam_type_str) ==
'geometric')
then
1023 elseif (trim(diam_type_str) ==
'mobility')
then
1029 "Unknown diam_type: " // trim(diam_type_str))
1033 aero_mode%sample_radius = [ real(kind=
dp) :: ]
1034 aero_mode%sample_num_conc = [ real(kind=
dp) :: ]
1035 if (trim(mode_type) ==
'log_normal')
then
1040 aero_mode%log10_std_dev_radius)
1042 aero_mode%char_radius =
diam2rad(diam)
1044 aero_mode%char_radius &
1053 log10_r1_mob = log10(
diam2rad(diam))
1054 log10_r0_mob = log10_r1_mob - aero_mode%log10_std_dev_radius
1055 log10_r2_mob = log10_r1_mob + aero_mode%log10_std_dev_radius
1056 r0_mob = 10**log10_r0_mob
1057 r2_mob = 10**log10_r2_mob
1059 r0_mob, temp, pressure)
1061 r2_mob, temp, pressure)
1062 log10_r0_geom = log10(r0_geom)
1063 log10_r2_geom = log10(r2_geom)
1064 aero_mode%log10_std_dev_radius &
1065 = (log10_r2_geom - log10_r0_geom) / 2d0
1067 call die_msg(532966100,
"Diameter type not handled: " &
1070 elseif (trim(mode_type) ==
'exp')
then
1075 aero_mode%char_radius =
diam2rad(diam)
1077 aero_mode%char_radius &
1081 call die_msg(585104460,
"Diameter type not handled: " &
1084 elseif (trim(mode_type) ==
'mono')
then
1089 aero_mode%char_radius =
diam2rad(diam)
1091 aero_mode%char_radius &
1095 call die_msg(902864269,
"Diameter type not handled: " &
1098 elseif (trim(mode_type) ==
'sampled')
then
1102 call spec_file_read_size_dist(size_dist_file, &
1103 aero_mode%sample_radius, aero_mode%sample_num_conc)
1108 do i_radius = 1,
size(aero_mode%sample_radius)
1109 aero_mode%sample_radius(i_radius) &
1111 aero_mode%sample_radius(i_radius), temp, pressure)
1114 call die_msg(239088838,
"Diameter type not handled: " &
1119 "Unknown distribution mode type: " // trim(mode_type))
1123 end subroutine spec_file_read_aero_mode
1154 character,
intent(inout) :: buffer(:)
1156 integer,
intent(inout) :: position
1161 integer :: prev_position
1163 prev_position = position
1187 character,
intent(inout) :: buffer(:)
1189 integer,
intent(inout) :: position
1194 integer :: prev_position
1196 prev_position = position