Skip to content

Commit d4bd202

Browse files
dwfncardavidfillmoreDavid Fillmoreboulderdaze
authored
MUSICA TUVX scheme: create aerosol radiator, set_aerosol_optics_values (#182)
Originator(s): @dwfncar Summary (include the keyword ['closes', 'fixes', 'resolves'] and issue number): - Closes #99 Describe any changes made to the namelist: N/A List all files eliminated and why: N/A List all files added and what they do: ``` A schemes/musica/tuvx/musica_ccpp_tuvx_aerosol_optics.F90 A test/musica/tuvx/test_tuvx_aerosol_optics.F90 ``` List all existing files that have been modified, and describe the changes: ``` M schemes/musica/tuvx/musica_ccpp_tuvx.F90 M schemes/musica/tuvx/musica_ccpp_tuvx_surface_albedo.F90 M test/docker/Dockerfile.musica M test/docker/Dockerfile.musica.no_install M test/musica/tuvx/CMakeLists.txt ``` List any test failures: N/A Is this a science-changing update? New physics package, algorithm change, tuning changes, etc? No --------- Co-authored-by: davidfillmore <fillmore.david.winslow@gmail.com> Co-authored-by: David Fillmore <fillmore@Andromeda.local> Co-authored-by: Jiwon Gim <jiwongim@ucar.edu>
1 parent 74e905b commit d4bd202

7 files changed

+260
-6
lines changed

schemes/musica/tuvx/musica_ccpp_tuvx.F90

+37-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ module musica_ccpp_tuvx
2323
type(profile_t), pointer :: surface_albedo_profile => null()
2424
type(profile_t), pointer :: extraterrestrial_flux_profile => null()
2525
type(radiator_t), pointer :: cloud_optics => null()
26+
type(radiator_t), pointer :: aerosol_optics => null()
2627
type(index_mappings_t), pointer :: photolysis_rate_constants_mapping => null( )
2728
integer, parameter :: DEFAULT_NUM_PHOTOLYSIS_RATE_CONSTANTS = 0
2829
integer :: number_of_photolysis_rate_constants = DEFAULT_NUM_PHOTOLYSIS_RATE_CONSTANTS
@@ -84,6 +85,11 @@ subroutine cleanup_tuvx_resources()
8485
cloud_optics => null()
8586
end if
8687

88+
if (associated( aerosol_optics )) then
89+
deallocate( aerosol_optics )
90+
aerosol_optics => null()
91+
end if
92+
8793
if (associated( photolysis_rate_constants_mapping )) then
8894
deallocate( photolysis_rate_constants_mapping )
8995
photolysis_rate_constants_mapping => null()
@@ -146,6 +152,8 @@ subroutine tuvx_init(vertical_layer_dimension, vertical_interface_dimension, &
146152
extraterrestrial_flux_unit
147153
use musica_ccpp_tuvx_cloud_optics, &
148154
only: create_cloud_optics_radiator, cloud_optics_label
155+
use musica_ccpp_tuvx_aerosol_optics, &
156+
only: create_aerosol_optics_radiator, aerosol_optics_label
149157

150158
integer, intent(in) :: vertical_layer_dimension ! (count)
151159
integer, intent(in) :: vertical_interface_dimension ! (count)
@@ -278,6 +286,21 @@ subroutine tuvx_init(vertical_layer_dimension, vertical_interface_dimension, &
278286
return
279287
end if
280288

289+
aerosol_optics => create_aerosol_optics_radiator( height_grid, wavelength_grid, &
290+
errmsg, errcode )
291+
if (errcode /= 0) then
292+
call reset_tuvx_map_state( grids, profiles, radiators )
293+
call cleanup_tuvx_resources()
294+
return
295+
endif
296+
297+
call radiators%add( aerosol_optics, error )
298+
if (has_error_occurred( error, errmsg, errcode )) then
299+
call reset_tuvx_map_state( grids, profiles, radiators )
300+
call cleanup_tuvx_resources()
301+
return
302+
end if
303+
281304
tuvx => tuvx_t( trim(filename_of_tuvx_configuration), grids, profiles, &
282305
radiators, error )
283306
if (has_error_occurred( error, errmsg, errcode )) then
@@ -372,6 +395,15 @@ subroutine tuvx_init(vertical_layer_dimension, vertical_interface_dimension, &
372395
return
373396
end if
374397

398+
aerosol_optics => radiators%get( aerosol_optics_label, error )
399+
if (has_error_occurred( error, errmsg, errcode )) then
400+
deallocate( tuvx )
401+
tuvx => null()
402+
call reset_tuvx_map_state( grids, profiles, radiators )
403+
call cleanup_tuvx_resources()
404+
return
405+
end if
406+
375407
call reset_tuvx_map_state( grids, profiles, radiators )
376408

377409
! 'photolysis_rate_constants_ordering' is a local variable
@@ -432,6 +464,7 @@ subroutine tuvx_run(temperature, dry_air_density, &
432464
use musica_ccpp_tuvx_surface_albedo, only: set_surface_albedo_values
433465
use musica_ccpp_tuvx_extraterrestrial_flux, only: set_extraterrestrial_flux_values
434466
use musica_ccpp_tuvx_cloud_optics, only: set_cloud_optics_values
467+
use musica_ccpp_tuvx_aerosol_optics, only: set_aerosol_optics_values
435468

436469
real(kind_phys), intent(in) :: temperature(:,:) ! K (column, layer)
437470
real(kind_phys), intent(in) :: dry_air_density(:,:) ! kg m-3 (column, layer)
@@ -502,6 +535,9 @@ subroutine tuvx_run(temperature, dry_air_density, &
502535
errmsg, errcode )
503536
if (errcode /= 0) return
504537

538+
call set_aerosol_optics_values( aerosol_optics, errmsg, errcode )
539+
if (errcode /= 0) return
540+
505541
! calculate photolysis rate constants and heating rates
506542
call tuvx%run( solar_zenith_angle(i_col), earth_sun_distance, &
507543
photolysis_rate_constants(:,:), heating_rates(:,:), &
@@ -540,4 +576,4 @@ subroutine tuvx_final(errmsg, errcode)
540576

541577
end subroutine tuvx_final
542578

543-
end module musica_ccpp_tuvx
579+
end module musica_ccpp_tuvx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
! Copyright (C) 2024 National Science Foundation-National Center for Atmospheric Research
2+
! SPDX-License-Identifier: Apache-2.0
3+
module musica_ccpp_tuvx_aerosol_optics
4+
implicit none
5+
6+
private
7+
public :: create_aerosol_optics_radiator, set_aerosol_optics_values
8+
9+
!> Label for aerosol optical properties in TUV-x
10+
character(len=*), parameter, public :: aerosol_optics_label = "aerosols"
11+
!> Label
12+
character(len=*), parameter, public :: \
13+
aerosol_optical_depth_label = "optical depths"
14+
character(len=*), parameter, public :: \
15+
aerosol_single_scattering_albedo_label = "single scattering albedos"
16+
character(len=*), parameter, public :: \
17+
aerosol_asymmetry_factor_label = "asymmetry factor"
18+
!> Unit
19+
character(len=*), parameter, public :: aerosol_optical_depth_unit = "none"
20+
character(len=*), parameter, public :: aerosol_single_scattering_albedo_unit = "none"
21+
character(len=*), parameter, public :: aerosol_asymmetry_factor_unit = "none"
22+
!> Default value of number of vertical levels
23+
integer, parameter :: DEFAULT_NUM_VERTICAL_LEVELS = 0
24+
!> Number of vertical levels
25+
integer, protected :: num_vertical_levels = DEFAULT_NUM_VERTICAL_LEVELS
26+
!> Default value of number of wavelength bins
27+
integer, parameter :: DEFAULT_NUM_WAVELENGTH_BINS = 0
28+
!> Number of wavelength bins
29+
integer, protected :: num_wavelength_bins = DEFAULT_NUM_WAVELENGTH_BINS
30+
!> Default value of number of streams
31+
integer, parameter :: DEFAULT_NUM_STREAMS = 1
32+
!> Number of streams
33+
integer, protected :: num_streams = DEFAULT_NUM_STREAMS
34+
35+
contains
36+
37+
!> Creates a TUV-x aerosol optics radiator
38+
function create_aerosol_optics_radiator( height_grid, wavelength_grid, &
39+
errmsg, errcode ) result( radiator )
40+
use musica_ccpp_util, only: has_error_occurred
41+
use musica_tuvx_grid, only: grid_t
42+
use musica_tuvx_radiator, only: radiator_t
43+
use musica_util, only: error_t
44+
45+
type(grid_t), intent(inout) :: height_grid
46+
type(grid_t), intent(inout) :: wavelength_grid
47+
character(len=*), intent(out) :: errmsg
48+
integer, intent(out) :: errcode
49+
type(radiator_t), pointer :: radiator
50+
51+
! local variables
52+
type(error_t) :: error
53+
54+
num_vertical_levels = height_grid%number_of_sections( error )
55+
if ( has_error_occurred( error, errmsg, errcode ) ) return
56+
57+
num_wavelength_bins = wavelength_grid%number_of_sections( error )
58+
if ( has_error_occurred( error, errmsg, errcode ) ) return
59+
60+
radiator => radiator_t( aerosol_optics_label, height_grid, wavelength_grid, &
61+
error )
62+
if ( has_error_occurred( error, errmsg, errcode ) ) return
63+
64+
end function create_aerosol_optics_radiator
65+
66+
!> Sets TUV-x aerosol optics values
67+
! Temporarily setting optical properties to zero until aerosol optical
68+
! property calculations are ported to CAM-SIMA.
69+
subroutine set_aerosol_optics_values( radiator, errmsg, errcode )
70+
use ccpp_kinds, only: kind_phys
71+
use musica_ccpp_util, only: has_error_occurred
72+
use musica_tuvx_radiator, only: radiator_t
73+
use musica_util, only: error_t
74+
75+
type(radiator_t), intent(inout) :: radiator
76+
character(len=*), intent(out) :: errmsg
77+
integer, intent(out) :: errcode
78+
79+
! local variables
80+
type(error_t) :: error
81+
real(kind_phys) :: \
82+
aerosol_optical_depth(num_vertical_levels, num_wavelength_bins)
83+
real(kind_phys) :: \
84+
aerosol_single_scattering_albedo(num_vertical_levels, num_wavelength_bins)
85+
real(kind_phys) :: \
86+
aerosol_asymmetry_factor(num_vertical_levels, num_wavelength_bins, num_streams)
87+
88+
aerosol_optical_depth(:,:) = 0.0_kind_phys
89+
aerosol_single_scattering_albedo(:,:) = 0.0_kind_phys
90+
aerosol_asymmetry_factor(:,:,:) = 0.0_kind_phys
91+
92+
call radiator%set_optical_depths( aerosol_optical_depth, error )
93+
if ( has_error_occurred( error, errmsg, errcode ) ) return
94+
95+
call radiator%set_single_scattering_albedos( aerosol_single_scattering_albedo, error )
96+
if ( has_error_occurred( error, errmsg, errcode ) ) return
97+
98+
call radiator%set_asymmetry_factors( aerosol_asymmetry_factor, error )
99+
if ( has_error_occurred( error, errmsg, errcode ) ) return
100+
101+
end subroutine set_aerosol_optics_values
102+
103+
end module musica_ccpp_tuvx_aerosol_optics

schemes/musica/tuvx/musica_ccpp_tuvx_surface_albedo.F90

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,4 @@ subroutine set_surface_albedo_values( profile, host_surface_albedo, &
7272

7373
end subroutine set_surface_albedo_values
7474

75-
end module musica_ccpp_tuvx_surface_albedo
75+
end module musica_ccpp_tuvx_surface_albedo

test/docker/Dockerfile.musica

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
FROM ubuntu:22.04
77

88
ARG MUSICA_GIT_TAG=326b5119768d5be9654baf96ae3bd6a1b757fdc8
9-
ARG CAM_SIMA_CHEMISTRY_DATA_TAG=abc7cacbec3d33d5c0ed5bb79a157e93b42c45c0
9+
ARG CAM_SIMA_CHEMISTRY_DATA_TAG=be87bc14822aa50b1afda0059ab6f5b5bd7397e6
1010
ARG BUILD_TYPE=Debug
1111

1212
RUN apt update \
@@ -92,4 +92,4 @@ RUN cd atmospheric_physics/test \
9292
-D CCPP_ENABLE_MEMCHECK=ON \
9393
&& cmake --build ./build
9494

95-
WORKDIR /home/test_user/atmospheric_physics/test/build
95+
WORKDIR /home/test_user/atmospheric_physics/test/build

test/docker/Dockerfile.musica.no_install

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
FROM ubuntu:22.04
1010

1111
ARG MUSICA_GIT_TAG=326b5119768d5be9654baf96ae3bd6a1b757fdc8
12-
ARG CAM_SIMA_CHEMISTRY_DATA_TAG=abc7cacbec3d33d5c0ed5bb79a157e93b42c45c0
12+
ARG CAM_SIMA_CHEMISTRY_DATA_TAG=be87bc14822aa50b1afda0059ab6f5b5bd7397e6
1313
ARG BUILD_TYPE=Debug
1414

1515
RUN apt update \
@@ -80,4 +80,4 @@ RUN cd atmospheric_physics/test \
8080
-D CCPP_ENABLE_MEMCHECK=ON \
8181
&& cmake --build ./build
8282

83-
WORKDIR /home/test_user/atmospheric_physics/test/build
83+
WORKDIR /home/test_user/atmospheric_physics/test/build

test/musica/tuvx/CMakeLists.txt

+30
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,33 @@ add_test(
172172
)
173173

174174
add_memory_check_test(test_tuvx_cloud_optics $<TARGET_FILE:test_tuvx_cloud_optics> "" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
175+
176+
# Aerosol optics
177+
add_executable(test_tuvx_aerosol_optics test_tuvx_aerosol_optics.F90)
178+
179+
target_sources(test_tuvx_aerosol_optics
180+
PUBLIC
181+
${MUSICA_SRC_PATH}/tuvx/musica_ccpp_tuvx_height_grid.F90
182+
${MUSICA_SRC_PATH}/tuvx/musica_ccpp_tuvx_wavelength_grid.F90
183+
${MUSICA_SRC_PATH}/tuvx/musica_ccpp_tuvx_aerosol_optics.F90
184+
${MUSICA_SRC_PATH}/musica_ccpp_util.F90
185+
${CCPP_TEST_SRC_PATH}/ccpp_kinds.F90
186+
)
187+
188+
target_link_libraries(test_tuvx_aerosol_optics
189+
PRIVATE
190+
musica::musica-fortran
191+
)
192+
193+
set_target_properties(test_tuvx_aerosol_optics
194+
PROPERTIES
195+
LINKER_LANGUAGE Fortran
196+
)
197+
198+
add_test(
199+
NAME test_tuvx_aerosol_optics
200+
COMMAND $<TARGET_FILE:test_tuvx_aerosol_optics>
201+
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
202+
)
203+
204+
add_memory_check_test(test_tuvx_aerosol_optics $<TARGET_FILE:test_tuvx_aerosol_optics> "" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
! Copyright (C) 2024 National Science Foundation-National Center for Atmospheric Research
2+
! SPDX-License-Identifier: Apache-2.0
3+
program test_tuvx_aerosol_optics
4+
5+
use musica_ccpp_tuvx_aerosol_optics
6+
7+
#define ASSERT(x) if (.not.(x)) then; write(*,*) "Assertion failed[", __FILE__, ":", __LINE__, "]: x"; stop 1; endif
8+
#define ASSERT_NEAR( a, b, abs_error ) if( (abs(a - b) >= abs_error) .and. (abs(a - b) /= 0.0) ) then; write(*,*) "Assertion failed[", __FILE__, ":", __LINE__, "]: a, b"; stop 1; endif
9+
10+
call test_create_aerosol_optics_radiator()
11+
12+
contains
13+
14+
subroutine test_create_aerosol_optics_radiator()
15+
16+
use musica_util, only: error_t
17+
use musica_ccpp_tuvx_height_grid, only: create_height_grid
18+
use musica_ccpp_tuvx_wavelength_grid, only: create_wavelength_grid
19+
use musica_tuvx_grid, only: grid_t
20+
use musica_tuvx_radiator, only: radiator_t
21+
use ccpp_kinds, only: kind_phys
22+
23+
integer, parameter :: NUM_HOST_HEIGHT_MIDPOINTS = 2
24+
integer, parameter :: NUM_HOST_HEIGHT_INTERFACES = 3
25+
integer, parameter :: NUM_WAVELENGTH_MIDPOINTS = 3
26+
integer, parameter :: NUM_WAVELENGTH_INTERFACES = 4
27+
real(kind_phys) :: host_wavelength_interfaces(NUM_WAVELENGTH_INTERFACES) = [180.0e-9_kind_phys, 200.0e-9_kind_phys, 240.0e-9_kind_phys, 300.0e-9_kind_phys]
28+
real(kind_phys) :: aerosol_optical_depth(NUM_HOST_HEIGHT_MIDPOINTS+1, NUM_WAVELENGTH_MIDPOINTS)
29+
real(kind_phys) :: single_scattering_albedo(NUM_HOST_HEIGHT_MIDPOINTS+1, NUM_WAVELENGTH_MIDPOINTS)
30+
real(kind_phys) :: asymmetry_parameter(NUM_HOST_HEIGHT_MIDPOINTS+1, NUM_WAVELENGTH_MIDPOINTS,1)
31+
type(grid_t), pointer :: height_grid => null()
32+
type(grid_t), pointer :: wavelength_grid => null()
33+
type(radiator_t), pointer :: aerosols => null()
34+
type(error_t) :: error
35+
character(len=512) :: errmsg
36+
integer :: errcode
37+
integer :: i
38+
39+
height_grid => create_height_grid(NUM_HOST_HEIGHT_MIDPOINTS, NUM_HOST_HEIGHT_INTERFACES, &
40+
errmsg, errcode)
41+
ASSERT(errcode == 0)
42+
ASSERT(associated(height_grid))
43+
44+
wavelength_grid => create_wavelength_grid(host_wavelength_interfaces, errmsg, errcode)
45+
ASSERT(errcode == 0)
46+
ASSERT(associated(wavelength_grid))
47+
48+
aerosols => create_aerosol_optics_radiator(height_grid, wavelength_grid, errmsg, errcode)
49+
ASSERT(errcode == 0)
50+
ASSERT(associated(aerosols))
51+
52+
call set_aerosol_optics_values(aerosols, errmsg, errcode)
53+
ASSERT(errcode == 0)
54+
55+
call aerosols%get_optical_depths(aerosol_optical_depth, error)
56+
ASSERT(error%is_success())
57+
do i = 1, size(aerosol_optical_depth, dim=1)
58+
do j = 1, size(aerosol_optical_depth, dim=2)
59+
ASSERT_NEAR(aerosol_optical_depth(i,j), 0.0_kind_phys, ABS_ERROR)
60+
end do
61+
end do
62+
63+
call aerosols%get_single_scattering_albedos(single_scattering_albedo, error)
64+
ASSERT(error%is_success())
65+
do i = 1, size(single_scattering_albedo, dim=1)
66+
do j = 1, size(single_scattering_albedo, dim=2)
67+
ASSERT_NEAR(single_scattering_albedo(i,j), 0.0_kind_phys, ABS_ERROR)
68+
end do
69+
end do
70+
71+
call aerosols%get_asymmetry_factors(asymmetry_parameter, error)
72+
ASSERT(error%is_success())
73+
do i = 1, size(asymmetry_parameter, dim=1)
74+
do j = 1, size(asymmetry_parameter, dim=2)
75+
ASSERT_NEAR(asymmetry_parameter(i,j,1), 0.0_kind_phys, ABS_ERROR)
76+
end do
77+
end do
78+
79+
deallocate( height_grid )
80+
deallocate( wavelength_grid )
81+
deallocate( aerosols )
82+
83+
end subroutine test_create_aerosol_optics_radiator
84+
85+
end program test_tuvx_aerosol_optics

0 commit comments

Comments
 (0)