17 use camp_util,
only: string_t
19 use camp_chem_spec_data
20 use camp_aero_rep_data
21 use camp_aero_rep_single_particle
53 character(len=AERO_NAME_LEN),
allocatable :: name(:)
56 integer,
allocatable :: mosaic_index(:)
58 real(kind=dp),
allocatable :: density(:)
60 integer,
allocatable :: num_ions(:)
62 real(kind=dp),
allocatable :: molec_weight(:)
64 real(kind=dp),
allocatable :: kappa(:)
66 character(len=AERO_SOURCE_NAME_LEN),
allocatable :: source_name(:)
71 class(aero_rep_data_t),
pointer :: aero_rep_ptr
74 integer,
allocatable :: camp_particle_spec_id(:)
77 integer :: camp_particle_state_size = -1
96 real(kind=dp),
intent(in) :: v
111 real(kind=dp),
intent(in) :: v
126 real(kind=dp),
intent(in) :: r
141 real(kind=dp),
intent(in) :: d
159 real(kind=dp),
intent(in) :: v
162 aero_data%fractal, v)
178 real(kind=dp),
intent(in) :: v
180 real(kind=dp),
intent(in) :: temp
182 real(kind=dp),
intent(in) :: pressure
185 aero_data%fractal, v, temp, pressure)
194 mobility_rad, temp, pressure)
199 real(kind=dp),
intent(in) :: mobility_rad
201 real(kind=dp),
intent(in) :: temp
203 real(kind=dp),
intent(in) :: pressure
206 aero_data%fractal, mobility_rad, temp, pressure)
215 mobility_rad, temp, pressure)
220 real(kind=dp),
intent(in) :: mobility_rad
222 real(kind=dp),
intent(in) :: temp
224 real(kind=dp),
intent(in) :: pressure
228 mobility_rad, temp, pressure)
240 if (
allocated(aero_data%name))
then
256 if (
allocated(aero_data%source_name))
then
273 character(len=*),
intent(in) :: name
280 if (name == aero_data%name(i))
then
302 character(len=*),
intent(in) :: name
304 if (.not.
allocated(aero_data%source_name))
then
311 aero_data%source_name = [aero_data%source_name, &
339 integer,
parameter :: n_mosaic_spec = 19
340 character(AERO_NAME_LEN),
parameter,
dimension(n_mosaic_spec) :: &
341 mosaic_spec_name = [ &
342 "SO4 ",
"NO3 ",
"Cl ",
"NH4 ",
"MSA ",
"ARO1 ", &
343 "ARO2 ",
"ALK1 ",
"OLE1 ",
"API1 ",
"API2 ",
"LIM1 ", &
344 "LIM2 ",
"CO3 ",
"Na ",
"Ca ",
"OIN ",
"OC ", &
347 integer :: i_spec, i_mosaic_spec, i
349 aero_data%mosaic_index = 0
352 aero_data%name(i_spec))
366 integer,
intent(in) :: i_part
368 integer,
intent(in) :: i_spec
371 call assert(106669451,
allocated(aero_data%camp_particle_spec_id))
372 call assert(278731889, aero_data%camp_particle_state_size .ge. 0)
373 camp_spec_id = (i_part - 1) * aero_data%camp_particle_state_size + &
374 aero_data%camp_particle_spec_id(i_spec)
384 subroutine spec_file_read_aero_data(file, aero_data)
391 integer :: n_species, species, i
392 character(len=SPEC_LINE_MAX_VAR_LEN),
allocatable :: species_name(:)
393 real(kind=dp),
allocatable :: species_data(:,:)
432 n_species =
size(species_data, 1)
433 if (.not. ((
size(species_data, 2) == 4) .or. (n_species == 0)))
then
434 call die_msg(428926381,
'each line in ' // trim(file%name) &
435 //
' should contain exactly 5 values')
447 aero_data%density(i) = species_data(i,1)
448 aero_data%num_ions(i) = nint(species_data(i,2))
449 aero_data%molec_weight(i) = species_data(i,3)
450 aero_data%kappa(i) = species_data(i,4)
452 (aero_data%num_ions(i) == 0) .or. (aero_data%kappa(i) == 0d0), &
453 "ions and kappa both non-zero for species " &
454 // trim(aero_data%name(i)) //
" in " // trim(file%name))
455 if (species_name(i) ==
"H2O")
then
456 aero_data%i_water = i
458 aero_data%density(i) == const%water_density, &
459 "input H2O density not equal to const%water_density (" &
463 aero_data%molec_weight(i) == const%water_molec_weight, &
464 "input H2O molec_weight not equal " &
465 //
"to const%water_molec_weight (" &
473 end subroutine spec_file_read_aero_data
483 character(len=*),
intent(in) :: name
487 integer,
allocatable :: species_list(:)
489 type(spec_line_t) :: line
495 do i = 1,
size(line%data)
499 'unknown species: ' // trim(line%data(i)))
501 species_list(i) = spec
533 character,
intent(inout) :: buffer(:)
535 integer,
intent(inout) :: position
540 integer :: prev_position
542 prev_position = position
564 character,
intent(inout) :: buffer(:)
566 integer,
intent(inout) :: position
571 integer :: prev_position
573 prev_position = position
600 integer,
intent(in) :: ncid
602 integer,
intent(out) :: dimid_aero_species
604 integer :: status, i_spec
605 integer :: varid_aero_species
606 integer :: aero_species_centers(aero_data_n_spec(aero_data))
607 character(len=(AERO_NAME_LEN * aero_data_n_spec(aero_data))) :: &
611 status = nf90_inq_dimid(ncid,
"aero_species", dimid_aero_species)
612 if (status == nf90_noerr)
return
619 aero_data_n_spec(aero_data), dimid_aero_species))
620 aero_species_names =
""
621 do i_spec = 1,aero_data_n_spec(aero_data)
622 aero_species_names((len_trim(aero_species_names) + 1):) &
623 = trim(aero_data%name(i_spec))
624 if (i_spec < aero_data_n_spec(aero_data))
then
625 aero_species_names((len_trim(aero_species_names) + 1):) =
","
628 call pmc_nc_check(nf90_def_var(ncid,
"aero_species", nf90_int, &
629 dimid_aero_species, varid_aero_species))
630 call pmc_nc_check(nf90_put_att(ncid, varid_aero_species,
"names", &
632 call pmc_nc_check(nf90_put_att(ncid, varid_aero_species,
"description", &
633 "dummy dimension variable (no useful value) - read species names " &
634 //
"as comma-separated values from the 'names' attribute"))
638 do i_spec = 1,aero_data_n_spec(aero_data)
639 aero_species_centers(i_spec) = i_spec
641 call pmc_nc_check(nf90_put_var(ncid, varid_aero_species, &
642 aero_species_centers))
657 integer,
intent(in) :: ncid
659 integer,
intent(out) :: dimid_aero_source
661 integer :: status, i_source
662 integer :: varid_aero_source
663 integer :: aero_source_centers(aero_data_n_source(aero_data))
664 character(len=(AERO_SOURCE_NAME_LEN * aero_data_n_source(aero_data))) &
668 status = nf90_inq_dimid(ncid,
"aero_source", dimid_aero_source)
669 if (status == nf90_noerr)
return
676 aero_data_n_source(aero_data), dimid_aero_source))
677 aero_source_names =
""
678 do i_source = 1,aero_data_n_source(aero_data)
679 aero_source_names((len_trim(aero_source_names) + 1):) &
680 = trim(aero_data%source_name(i_source))
681 if (i_source < aero_data_n_source(aero_data))
then
682 aero_source_names((len_trim(aero_source_names) + 1):) =
","
685 call pmc_nc_check(nf90_def_var(ncid,
"aero_source", nf90_int, &
686 dimid_aero_source, varid_aero_source))
687 call pmc_nc_check(nf90_put_att(ncid, varid_aero_source,
"names", &
689 call pmc_nc_check(nf90_put_att(ncid, varid_aero_source,
"description", &
690 "dummy dimension variable (no useful value) - read source names " &
691 //
"as comma-separated values from the 'names' attribute"))
695 do i_source = 1,aero_data_n_source(aero_data)
696 aero_source_centers(i_source) = i_source
698 call pmc_nc_check(nf90_put_var(ncid, varid_aero_source, &
699 aero_source_centers))
706 subroutine aero_data_output_netcdf(aero_data, ncid)
711 integer,
intent(in) :: ncid
713 integer :: dimid_aero_species, dimid_aero_source
749 "aero_mosaic_index", (/ dimid_aero_species /), &
750 long_name=
"MOSAIC indices of aerosol species")
752 "aero_density", (/ dimid_aero_species /), unit=
"kg/m^3", &
753 long_name=
"densities of aerosol species")
755 "aero_num_ions", (/ dimid_aero_species /), &
756 long_name=
"number of ions after dissociation of aerosol species")
758 "aero_molec_weight", (/ dimid_aero_species /), unit=
"kg/mol", &
759 long_name=
"molecular weights of aerosol species")
761 "aero_kappa", (/ dimid_aero_species /), unit=
"1", &
762 long_name=
"hygroscopicity parameters (kappas) of aerosol species")
763 call fractal_output_netcdf(aero_data%fractal, ncid)
765 end subroutine aero_data_output_netcdf
775 integer,
intent(in) :: ncid
777 integer,
parameter :: MAX_SPECIES = 1000
778 integer,
parameter :: MAX_SOURCES = 1000
780 character(len=1000) :: name
781 integer :: dimid_aero_species, n_spec, varid_aero_species, i_spec, i
782 integer :: dimid_aero_source, n_source, varid_aero_source, i_source
783 character(len=((AERO_NAME_LEN + 2) * MAX_SPECIES)) :: aero_species_names
784 character(len=:),
allocatable :: aero_source_names
786 call pmc_nc_check(nf90_inq_dimid(ncid,
"aero_species", &
789 dimid_aero_species, name, n_spec))
790 call assert(141013948, n_spec < max_species)
795 dimid_aero_source, name, n_source))
796 call assert(739238793, n_source < max_sources)
805 call pmc_nc_check(nf90_inq_varid(ncid,
"aero_species", &
807 call pmc_nc_check(nf90_get_att(ncid, varid_aero_species,
"names", &
813 do while ((aero_species_names(i:i) /=
" ") &
814 .and. (aero_species_names(i:i) /=
","))
817 call assert(852937292, i > 1)
818 aero_data%name(i_spec) = aero_species_names(1:(i-1))
819 aero_species_names = aero_species_names((i+1):)
821 call assert(729138192, aero_species_names ==
"")
825 allocate(
character(len=((AERO_SOURCE_NAME_LEN + 2) * MAX_SPECIES)) &
826 :: aero_source_names)
827 call pmc_nc_check(nf90_get_att(ncid, varid_aero_source,
"names", &
833 do while ((aero_source_names(i:i) /=
" ") &
834 .and. (aero_source_names(i:i) /=
","))
837 call assert(840982478, i > 1)
838 aero_data%source_name(i_source) = aero_source_names(1:(i-1))
839 aero_source_names = aero_source_names((i+1):)
841 call assert(377166446, aero_source_names ==
"")
859 type(camp_core_t),
intent(in) :: camp_core
861 character(len=:),
allocatable :: rep_name, prop_name, str_val
862 type(string_t),
allocatable :: spec_names(:), tmp_spec_names(:)
863 integer :: num_spec, i_spec, spec_type
864 type(chem_spec_data_t),
pointer :: chem_spec_data
865 type(property_t),
pointer :: property_set
867 rep_name =
"PartMC single particle"
868 if (.not. camp_core%get_aero_rep(rep_name, aero_data%aero_rep_ptr))
then
869 call die_msg(418509983,
"Missing 'PartMC single particle' aerosol "// &
873 call assert_msg(935419266, camp_core%get_chem_spec_data(chem_spec_data), &
874 "No chemical species data in camp_core.")
877 spec_names = aero_data%aero_rep_ptr%unique_names()
878 allocate(tmp_spec_names(
size(spec_names)))
880 do i_spec = 1,
size(spec_names)
881 call assert(496388827, chem_spec_data%get_type( &
882 aero_data%aero_rep_ptr%spec_name(spec_names(i_spec)%string), &
884 if (spec_type /= chem_spec_variable .and. &
885 spec_type /= chem_spec_constant .and. &
886 spec_type /= chem_spec_pssa) cycle
887 if (spec_names(i_spec)%string(1:3) /=
"P1.")
exit
888 num_spec = num_spec + 1
889 tmp_spec_names(num_spec)%string = spec_names(i_spec)%string(4:)
891 deallocate(spec_names)
892 allocate(spec_names(num_spec))
893 spec_names(:) = tmp_spec_names(1:num_spec)
894 deallocate(tmp_spec_names)
896 allocate(aero_data%name(num_spec))
897 allocate(aero_data%mosaic_index(num_spec))
898 allocate(aero_data%density(num_spec))
899 allocate(aero_data%num_ions(num_spec))
900 allocate(aero_data%molec_weight(num_spec))
901 allocate(aero_data%kappa(num_spec))
902 allocate(aero_data%camp_particle_spec_id(num_spec))
905 aero_data%i_water = 0
907 do i_spec = 1, num_spec
908 aero_data%name(i_spec) = spec_names(i_spec)%string
909 if (.not. chem_spec_data%get_property_set( &
910 aero_data%aero_rep_ptr%spec_name(
"P1." &
911 // spec_names(i_spec)%string), property_set))
then
912 call die_msg(934844845,
"Missing property set for aerosol species " &
913 // spec_names(i_spec)%string)
915 prop_name =
"density [kg m-3]"
916 if (.not. property_set%get_real(prop_name, &
917 aero_data%density(i_spec)))
then
918 call die_msg(547508215,
"Missing density for aerosol species " &
919 // spec_names(i_spec)%string)
921 prop_name =
"num_ions"
922 if (.not. property_set%get_int(prop_name, &
923 aero_data%num_ions(i_spec)))
then
924 call die_msg(324777059,
"Missing num_ions for aerosol species " &
925 // spec_names(i_spec)%string)
927 prop_name =
"molecular weight [kg mol-1]"
928 if (.not. property_set%get_real(prop_name, &
929 aero_data%molec_weight(i_spec)))
then
930 call die_msg(549413749,
"Missing molec_weight for aerosol species " &
931 // spec_names(i_spec)%string)
934 if (.not. property_set%get_real(prop_name, &
935 aero_data%kappa(i_spec)))
then
936 call die_msg(944207343,
"Missing kappa for aerosol species " &
937 // spec_names(i_spec)%string)
939 prop_name =
"PartMC name"
940 if (property_set%get_string(prop_name, str_val))
then
941 if (str_val ==
"H2O")
then
942 call assert_msg(227489086, aero_data%i_water == 0, &
943 "Multiple aerosol water species")
944 aero_data%i_water = i_spec
947 aero_data%camp_particle_spec_id(i_spec) = &
948 aero_data%aero_rep_ptr%spec_state_id(
"P1." &
949 // spec_names(i_spec)%string)
952 select type( aero_rep => aero_data%aero_rep_ptr)
953 type is(aero_rep_single_particle_t)
956 aero_data%camp_particle_state_size = aero_rep%per_particle_size()
959 call die_msg(281737350,
"Wrong aerosol representation type")