diff --git a/applications/lfric_atm/metadata/field_def_diags.xml b/applications/lfric_atm/metadata/field_def_diags.xml index 9b01795782..d4124b8a96 100644 --- a/applications/lfric_atm/metadata/field_def_diags.xml +++ b/applications/lfric_atm/metadata/field_def_diags.xml @@ -500,18 +500,19 @@ - + - zoomed__sea_ice_tile - zoomed__sea_tile + zoomed__sea_tile surface__sea_ice_tile / (surface__sea_ice_tile + surface__sea_tile + 1.0e-16) - zoomed__urban_tile + zoomed__urban_tile - zoomed__land_ice_tile + zoomed__land_ice_tile 1.0 - (surface__sea_ice_tile + surface__sea_tile) + + zoomed__z0m_sea @@ -531,8 +532,11 @@ + + zoomed__sea_temperature + @@ -558,9 +562,22 @@ + + + + zoomed__sea_heat_flux + surface__sea_tile_heat_flux * (1.0 - surface__sea_ice_fraction) + + + + + zoomed__sea_moisture_flux + surface__sea_tile_moisture_flux * (1.0 - surface__sea_ice_fraction) + + @@ -610,6 +627,7 @@ + @@ -626,6 +644,9 @@ seaice__sea_ice_pensolar * seaice__sea_ice_fraction + + + @@ -641,6 +662,8 @@ + + @@ -1023,6 +1046,7 @@ + diff --git a/interfaces/jules_interface/source/algorithm/jules_extra_alg_mod.x90 b/interfaces/jules_interface/source/algorithm/jules_extra_alg_mod.x90 index b4304ae136..55d0841406 100644 --- a/interfaces/jules_interface/source/algorithm/jules_extra_alg_mod.x90 +++ b/interfaces/jules_interface/source/algorithm/jules_extra_alg_mod.x90 @@ -3,6 +3,9 @@ ! The file LICENCE, distributed with this code, contains details of the terms ! under which the code may be used. !------------------------------------------------------------------------------- +! Some of the content of this file has been produced with the assistance of +! Anthropic Claude Opus 5 (Claude Code). +!------------------------------------------------------------------------------- !> @brief Interface to the JULES surf_couple_extra scheme module jules_extra_alg_mod @@ -285,6 +288,7 @@ contains call output_diags_for_jules_snow(tile_fraction, tile_snow_mass, & snowice_melt, & snow_depth, tile_snow_rgrain, & + snow_under_canopy, n_snow_layers, & snowpack_density, snow_layer_temp, & snow_layer_thickness, snow_layer_ice_mass,& snow_layer_liq_mass, snow_layer_rgrain, & @@ -299,10 +303,12 @@ contains water_table, soil_moist_wilt, & soil_moist_crit, soil_moist_sat, & grid_canopy_water,grid_throughfall, & + soil_sat_frac, wetness_under_soil, & inland_basin_flow) ! Output sea ice diagnostics - call output_diags_for_jules_seaice(tile_fraction, sea_ice_thickness, & + call output_diags_for_jules_seaice(tile_fraction, snowice_melt, & + sea_ice_thickness, & sea_ice_temperature, sea_ice_conductivity, & sea_ice_pensolar, melt_pond_fraction, & melt_pond_depth) diff --git a/interfaces/jules_interface/source/diagnostics/jules_imp_diags_mod.x90 b/interfaces/jules_interface/source/diagnostics/jules_imp_diags_mod.x90 index c94975f491..10d7b8e54a 100644 --- a/interfaces/jules_interface/source/diagnostics/jules_imp_diags_mod.x90 +++ b/interfaces/jules_interface/source/diagnostics/jules_imp_diags_mod.x90 @@ -3,13 +3,18 @@ ! The file LICENCE, distributed with this code, contains details of the terms ! under which the code may be used. !------------------------------------------------------------------------------- +! Some of the content of this file has been produced with the assistance of +! Anthropic Claude Opus 5 (Claude Code). +!------------------------------------------------------------------------------- !> @brief Processes diagnostics for jules_imp_alg module jules_imp_diags_mod use constants_mod, only: l_def, r_def use field_mod, only: field_type - use jules_control_init_mod, only: n_surf_tile, n_land_tile + use jules_control_init_mod, only: n_surf_tile, n_land_tile, & + n_ssi_tile, first_sea_tile, & + n_sea_ice_tile, first_sea_ice_tile use sci_weighted_ave_kernel_mod, only: weighted_ave_kernel_type use timing_mod, only: start_timing, stop_timing, & tik, LPROF @@ -38,6 +43,8 @@ module jules_imp_diags_mod logical( l_def ) :: rh1p5m_land_flag logical( l_def ) :: latent_heat_flag logical( l_def ) :: grid_latent_heat_flag + logical( l_def ) :: land_mean_latent_heat_flag + logical( l_def ) :: ssi_mean_latent_heat_flag logical( l_def ) :: snomlt_surf_htf_flag logical( l_def ) :: soil_evap_flag logical( l_def ) :: soil_surf_ht_flux_flag @@ -138,10 +145,14 @@ contains ! and save from 1st to 2nd loop latent_heat_flag = init_diag(latent_heat, 'surface__latent_heat') grid_latent_heat_flag = samp_diag('surface__grid_latent_heat') - if (grid_latent_heat_flag .and. .not. latent_heat_flag) then + land_mean_latent_heat_flag = samp_diag('surface__land_mean_latent_heat') + ssi_mean_latent_heat_flag = samp_diag('surface__ssi_mean_latent_heat') + if ((grid_latent_heat_flag .or. land_mean_latent_heat_flag .or. & + ssi_mean_latent_heat_flag) .and. .not. latent_heat_flag) then ignore = init_diag(latent_heat, 'surface__latent_heat', activate=.true.) end if - if (latent_heat_flag .or. grid_latent_heat_flag) & + if (latent_heat_flag .or. grid_latent_heat_flag .or. & + land_mean_latent_heat_flag .or. ssi_mean_latent_heat_flag) & call invoke(setval_c(latent_heat,0.0_r_def)) ! diagnostics which are needed in BL code, so allocate on 1st loop @@ -259,7 +270,9 @@ contains ! Local variables type( field_type ) :: land_surface_temperature, grid_canopy_evap, & - grid_surface_temperature, grid_latent_heat, grid_sublimation + grid_surface_temperature, grid_latent_heat, grid_sublimation, & + sea_ice_surface_temperature, land_mean_heat_flux, & + land_mean_latent_heat, ssi_mean_heat_flux, ssi_mean_latent_heat integer( tik ) :: id if ( LPROF ) call start_timing( id, 'diags.jules_imp' ) @@ -289,6 +302,33 @@ contains call land_surface_temperature%write_field() end if + if(init_diag(sea_ice_surface_temperature, 'surface__sea_ice_surface_temperature')) then + call invoke(weighted_ave_kernel_type(sea_ice_surface_temperature, & + tile_temperature, tile_fraction, & + first_sea_ice_tile, n_sea_ice_tile)) + call sea_ice_surface_temperature%write_field() + end if + + ! Averaging the tiled flux over the land tiles, weighted by their grid-box + ! fractions, divides by the land fraction and so gives a mean over the + ! land portion of the grid-box. + if(init_diag(land_mean_heat_flux, 'surface__land_mean_heat_flux')) then + call invoke(weighted_ave_kernel_type(land_mean_heat_flux, & + tile_heat_flux, tile_fraction, & + 1, n_land_tile)) + call land_mean_heat_flux%write_field() + end if + + ! As above, but over the sea and sea-ice tiles, which are contiguous, so + ! dividing by the marine fraction and giving a mean over the sea and + ! sea-ice portion of the grid-box. + if(init_diag(ssi_mean_heat_flux, 'surface__ssi_mean_heat_flux')) then + call invoke(weighted_ave_kernel_type(ssi_mean_heat_flux, & + tile_heat_flux, tile_fraction, & + first_sea_tile, n_ssi_tile)) + call ssi_mean_heat_flux%write_field() + end if + if (init_diag(grid_canopy_evap, 'surface__grid_canopy_evap')) then call invoke(weighted_ave_kernel_type(grid_canopy_evap, & canopy_evap, tile_fraction, & @@ -314,6 +354,22 @@ contains call grid_latent_heat%write_field() end if + ! As above, a mean over the land portion of the grid-box + if (init_diag(land_mean_latent_heat, 'surface__land_mean_latent_heat')) then + call invoke(weighted_ave_kernel_type(land_mean_latent_heat, & + latent_heat, tile_fraction, & + 1, n_land_tile)) + call land_mean_latent_heat%write_field() + end if + + ! As above, a mean over the sea and sea-ice portion of the grid-box + if (init_diag(ssi_mean_latent_heat, 'surface__ssi_mean_latent_heat')) then + call invoke(weighted_ave_kernel_type(ssi_mean_latent_heat, & + latent_heat, tile_fraction, & + first_sea_tile, n_ssi_tile)) + call ssi_mean_latent_heat%write_field() + end if + else !loop=1 ! Diagnostics computed within the kernel - these are computed on 1st loop diff --git a/interfaces/jules_interface/source/diagnostics/jules_seaice_diags_mod.x90 b/interfaces/jules_interface/source/diagnostics/jules_seaice_diags_mod.x90 index 1129a35d79..8daf956b7a 100644 --- a/interfaces/jules_interface/source/diagnostics/jules_seaice_diags_mod.x90 +++ b/interfaces/jules_interface/source/diagnostics/jules_seaice_diags_mod.x90 @@ -3,15 +3,22 @@ ! The file LICENCE, distributed with this code, contains details of the terms ! under which the code may be used. !------------------------------------------------------------------------------- +! Some of the content of this file has been produced with the assistance of +! Anthropic Claude Opus 5 (Claude Code). +!------------------------------------------------------------------------------- !> @brief Processes diagnostics for jules_extra_alg module jules_seaice_diags_mod + use constants_mod, only: r_def use field_mod, only: field_type use timing_mod, only: start_timing, stop_timing, tik, LPROF - - use jules_control_init_mod, only: first_sea_ice_tile, & - n_sea_ice_tile + use sci_weighted_ave_kernel_mod, only: weighted_ave_kernel_type + use driver_water_constants_mod, only: latent_heat_h2o_fusion + use jules_control_init_mod, only: first_sea_tile, n_ssi_tile, & + first_sea_ice_tile, & + n_sea_ice_tile + use initialise_diagnostics_mod, only: init_diag => init_diagnostic_field implicit none @@ -23,6 +30,7 @@ contains !> @brief Output diagnostics from jules_extra_alg !> @param[in] tile_fraction Fraction of surface tiles + !> @param[in] snowice_melt Total snow and ice melt on tiles !> @param[in] sea_ice_thickness Sea ice thickness !> @param[in] sea_ice_temperature Sea ice layer temperature !> @param[in] sea_ice_conductivity Sea ice conductivity @@ -30,7 +38,8 @@ contains !> @param[in] melt_pond_fraction Melt pond fraction !> @param[in] melt_pond_depth Melt pond depth - subroutine output_diags_for_jules_seaice(tile_fraction, sea_ice_thickness, & + subroutine output_diags_for_jules_seaice(tile_fraction, snowice_melt, & + sea_ice_thickness, & sea_ice_temperature, sea_ice_conductivity, & sea_ice_pensolar, melt_pond_fraction, & melt_pond_depth) @@ -40,13 +49,17 @@ contains implicit none ! Prognostic fields to output - type( field_type ), intent(in) :: tile_fraction, sea_ice_thickness, & + type( field_type ), intent(in) :: tile_fraction, snowice_melt, & + sea_ice_thickness, & sea_ice_temperature, sea_ice_conductivity, & sea_ice_pensolar, melt_pond_fraction, & melt_pond_depth ! Diagnostics locally computed here from other fields - type( field_type ) :: sea_ice_fraction + type( field_type ) :: sea_ice_fraction, weights_one, & + sea_ice_tile, sea_ice_topmelt, & + ice_mean_thickness, ice_mean_temperature + real(r_def) :: num_sice_tiles integer( tik ) :: id if ( LPROF ) call start_timing( id, 'diags.jules_seaice' ) @@ -65,6 +78,54 @@ contains first_sea_ice_tile, n_sea_ice_tile)) call sea_ice_fraction%write_field('seaice__sea_ice_fraction') + ! Output sea-ice as a fraction of total grid-box + ! kernel calculates the avearge, so multiply after by number of tiles + ! to get the sum + if(init_diag(sea_ice_tile, 'surface__sea_ice_tile')) then + call tile_fraction%copy_field_properties(weights_one) + num_sice_tiles = real(n_sea_ice_tile, r_def) + call invoke(setval_c(weights_one, 1.0_r_def), & + weighted_ave_kernel_type(sea_ice_tile, & + tile_fraction, weights_one, & + first_sea_ice_tile, n_sea_ice_tile), & + inc_a_times_X(num_sice_tiles, sea_ice_tile)) + call sea_ice_tile%write_field() + end if + + ! Output the heat flux used in melting the top of the sea-ice, as a mean + ! over the marine portion of the grid-box. The tile order is land, sea, + ! sea-ice, so averaging over the sea and sea-ice tiles weighted by their + ! fractions divides by the marine fraction. The melt rate is zero on the + ! sea tile, so only the sea-ice contributes to the sum. It is converted + ! from a melt rate to an energy flux by the latent heat of fusion. + if(init_diag(sea_ice_topmelt, 'seaice__ssi_mean_topmelt_heat_flux')) then + call invoke(weighted_ave_kernel_type(sea_ice_topmelt, & + snowice_melt, tile_fraction, & + first_sea_tile, n_ssi_tile), & + inc_a_times_X(latent_heat_h2o_fusion, sea_ice_topmelt)) + call sea_ice_topmelt%write_field() + end if + + ! Aggregate the category thickness and temperature onto a single level by + ! averaging over the categories weighted by their fractions, which divides + ! by the total ice fraction and so gives a mean over the ice. This is how + ! sice_htf forms the bulk temperature, and the weights cancel in the ratio + ! so it does not matter that the fractions here are of the grid-box while + ! those in JULES are of the marine portion. + if(init_diag(ice_mean_thickness, 'seaice__ice_mean_thickness')) then + call invoke(weighted_ave_kernel_type(ice_mean_thickness, & + sea_ice_thickness, sea_ice_fraction, & + 1, n_sea_ice_tile)) + call ice_mean_thickness%write_field() + end if + + if(init_diag(ice_mean_temperature, 'seaice__ice_mean_temperature')) then + call invoke(weighted_ave_kernel_type(ice_mean_temperature, & + sea_ice_temperature, sea_ice_fraction, & + 1, n_sea_ice_tile)) + call ice_mean_temperature%write_field() + end if + if ( LPROF ) call stop_timing( id, 'diags.jules_seaice' ) end subroutine output_diags_for_jules_seaice diff --git a/interfaces/jules_interface/source/diagnostics/jules_snow_diags_mod.x90 b/interfaces/jules_interface/source/diagnostics/jules_snow_diags_mod.x90 index 294719b77a..9ecf6d0f08 100644 --- a/interfaces/jules_interface/source/diagnostics/jules_snow_diags_mod.x90 +++ b/interfaces/jules_interface/source/diagnostics/jules_snow_diags_mod.x90 @@ -9,6 +9,7 @@ module jules_snow_diags_mod use constants_mod, only: l_def, r_def use field_mod, only: field_type + use integer_field_mod, only: integer_field_type use timing_mod, only: start_timing, stop_timing, tik, LPROF use sci_weighted_ave_kernel_mod, only: weighted_ave_kernel_type @@ -62,6 +63,8 @@ contains !> @param[in] snowice_melt Total snow and ice melt on tiles !> @param[in] snow_depth Depth of snow on tiles !> @param[in] tile_snow_rgrain Snow grain size on tiles + !> @param[in] snow_under_canopy Snow beneath the tile canopy + !> @param[in] n_snow_layers Number of snow layers on tiles !> @param[in] snowpack_density Density of snow pack on tiles !> @param[in] snow_layer_temp Temperature of snow layers on tiles !> @param[in] snow_layer_thickness Thickness of snow layers on tiles @@ -74,6 +77,7 @@ contains subroutine output_diags_for_jules_snow(tile_fraction, tile_snow_mass, & snowice_melt, & snow_depth, tile_snow_rgrain, & + snow_under_canopy, n_snow_layers, & snowpack_density, snow_layer_temp, & snow_layer_thickness, snow_layer_ice_mass,& snow_layer_liq_mass, snow_layer_rgrain, & @@ -91,7 +95,8 @@ contains snow_layer_thickness, & snow_layer_ice_mass, & snow_layer_liq_mass, & - snow_layer_rgrain + snow_layer_rgrain, snow_under_canopy + type( integer_field_type ), intent(in) :: n_snow_layers ! Diagnostics computed within the kernel type( field_type ), intent(in) :: grid_snow_mass @@ -107,7 +112,9 @@ contains call snowice_melt%write_field('snow__snowice_melt') call snow_depth%write_field('snow__snow_depth') call tile_snow_rgrain%write_field('snow__tile_snow_rgrain') + call n_snow_layers%write_field('snow__n_snow_layers') call snowpack_density%write_field('snow__snowpack_density') + call snow_under_canopy%write_field('snow__snow_under_canopy') call snow_layer_temp%write_field('snow__snow_layer_temp') call snow_layer_thickness%write_field('snow__snow_layer_thickness') call snow_layer_ice_mass%write_field('snow__snow_layer_ice_mass') diff --git a/interfaces/jules_interface/source/diagnostics/jules_soil_diags_mod.x90 b/interfaces/jules_interface/source/diagnostics/jules_soil_diags_mod.x90 index 37b40f5436..fd927a4d15 100644 --- a/interfaces/jules_interface/source/diagnostics/jules_soil_diags_mod.x90 +++ b/interfaces/jules_interface/source/diagnostics/jules_soil_diags_mod.x90 @@ -93,6 +93,8 @@ contains !> @param[in] soil_moist_sat Soil moisture at saturation !> @param[in,out] grid_canopy_water Grid canopy water !> @param[in,out] grid_throughfall Grid throughfall + !> @param[in] soil_sat_frac Surface saturation fraction + !> @param[in] wetness_under_soil Deep soil saturation fraction !> @param[in] inland_basin_flow Inland basin flow from rivers to soil subroutine output_diags_for_jules_soil(tile_fraction, soil_moisture_content, & @@ -103,6 +105,7 @@ contains water_table, soil_moist_wilt, & soil_moist_crit, soil_moist_sat, & grid_canopy_water,grid_throughfall, & + soil_sat_frac, wetness_under_soil, & inland_basin_flow) use jules_control_init_mod, only : n_land_tile @@ -119,6 +122,7 @@ contains soil_moist_wilt, & soil_moist_crit, & soil_moist_sat, & + soil_sat_frac, wetness_under_soil, & inland_basin_flow ! Diagnostics computed within the kernel @@ -143,6 +147,8 @@ contains call soil_moist_wilt%write_field('soil__soil_moist_wilt') call soil_moist_crit%write_field('soil__soil_moist_crit') call soil_moist_sat%write_field('soil__soil_moist_sat') + call soil_sat_frac%write_field('soil__soil_sat_frac') + call wetness_under_soil%write_field('soil__wetness_under_soil') ! Diagnostics locally computed here from other fields if (grid_canopy_water_flag) then diff --git a/interfaces/jules_interface/source/support/jules_control_init_mod.f90 b/interfaces/jules_interface/source/support/jules_control_init_mod.f90 index c47a3d643b..b0b8b15985 100644 --- a/interfaces/jules_interface/source/support/jules_control_init_mod.f90 +++ b/interfaces/jules_interface/source/support/jules_control_init_mod.f90 @@ -3,6 +3,9 @@ ! The file LICENCE, distributed with this code, contains details of the terms ! under which the code may be used. !---------------------------------------------------------------------------- +! Some of the content of this file has been produced with the assistance of +! Anthropic Claude Opus 5 (Claude Code). +!---------------------------------------------------------------------------- !> @brief Controls the setting of Jules high level variables which are either !> fixed in LFRic or derived from LFRic inputs. @@ -25,15 +28,16 @@ module jules_control_init_mod integer(kind=i_def), protected :: n_land_tile integer(kind=i_def), protected :: n_sea_ice_tile + integer(kind=i_def), protected :: n_ssi_tile integer(kind=i_def), protected :: n_surf_tile integer(kind=i_def), protected :: first_sea_tile integer(kind=i_def), protected :: first_sea_ice_tile integer(kind=i_def), protected :: soil_lev_tile private - public :: n_land_tile, n_sea_tile, n_sea_ice_tile, n_surf_tile, & - first_sea_tile, first_sea_ice_tile, jules_control_init, & - soil_lev_tile, n_surf_interp + public :: n_land_tile, n_sea_tile, n_sea_ice_tile, n_ssi_tile, & + n_surf_tile, first_sea_tile, first_sea_ice_tile, & + jules_control_init, soil_lev_tile, n_surf_interp contains @@ -147,9 +151,13 @@ subroutine jules_control_init() n_sea_ice_tile = nice nice_use = nice + ! Total number of sea and sea-ice tiles, used where a quantity is + ! averaged over the marine portion of the grid-box + n_ssi_tile = n_sea_tile + n_sea_ice_tile + ! Total number of surface tiles, used to dimension LFRic ! multidata fields - n_surf_tile = n_land_tile + n_sea_tile + n_sea_ice_tile + n_surf_tile = n_land_tile + n_ssi_tile ! Indices of the first sea and sea-ice tiles. By convection the tile ! order is always land, sea, sea-ice diff --git a/rose-stem/app/lfric_atm/file/file_def_diags_oper_nwp_gl.xml b/rose-stem/app/lfric_atm/file/file_def_diags_oper_nwp_gl.xml index fb5ee25716..b710d4aaec 100644 --- a/rose-stem/app/lfric_atm/file/file_def_diags_oper_nwp_gl.xml +++ b/rose-stem/app/lfric_atm/file/file_def_diags_oper_nwp_gl.xml @@ -7,6 +7,7 @@ + @@ -19,6 +20,10 @@ + + + + @@ -146,6 +151,9 @@ + + + diff --git a/science/gungho/source/physics/multidata_field_dimensions_mod.F90 b/science/gungho/source/physics/multidata_field_dimensions_mod.F90 index c9b6f96df6..b545c8bb2a 100644 --- a/science/gungho/source/physics/multidata_field_dimensions_mod.F90 +++ b/science/gungho/source/physics/multidata_field_dimensions_mod.F90 @@ -97,7 +97,7 @@ subroutine sync_multidata_field_dimensions() call set_zoom_axis_attr( & 'surface_tiles_sea_ice_zoom_axis', & get_multidata_field_dimension('land_tiles')+1, & - 1, & + get_multidata_field_dimension('sea_ice_categories'), & tolerate_missing_axes) ! Settings for some land tile zoom axes call set_zoom_axis_attr( &