From 88faf33b44336aa053dbf109398a4f0a810da323 Mon Sep 17 00:00:00 2001 From: iboutle <135141261+iboutle@users.noreply.github.com> Date: Fri, 29 May 2026 16:41:40 +0100 Subject: [PATCH 01/20] new diagnostics needed --- applications/lfric_atm/metadata/field_def_diags.xml | 2 ++ .../source/algorithm/jules_extra_alg_mod.x90 | 3 ++- .../source/diagnostics/jules_soil_diags_mod.x90 | 7 ++++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/applications/lfric_atm/metadata/field_def_diags.xml b/applications/lfric_atm/metadata/field_def_diags.xml index a17d884f90..8ce47824f9 100644 --- a/applications/lfric_atm/metadata/field_def_diags.xml +++ b/applications/lfric_atm/metadata/field_def_diags.xml @@ -637,6 +637,8 @@ + + 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 68241339ae..c3d2a16062 100644 --- a/interfaces/jules_interface/source/algorithm/jules_extra_alg_mod.x90 +++ b/interfaces/jules_interface/source/algorithm/jules_extra_alg_mod.x90 @@ -290,7 +290,8 @@ contains surface_runoff, sub_surface_runoff, & water_table, soil_moist_wilt, & soil_moist_crit, soil_moist_sat, & - grid_canopy_water,grid_throughfall) + grid_canopy_water,grid_throughfall, & + soil_sat_frac, wetness_under_soil) ! Output sea ice diagnostics call output_diags_for_jules_seaice(tile_fraction, sea_ice_thickness, & 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 a3db5c3613..ac95601295 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 subroutine output_diags_for_jules_soil(tile_fraction, soil_moisture_content, & canopy_water, soil_moisture, & @@ -101,7 +103,8 @@ contains surface_runoff, sub_surface_runoff, & water_table, soil_moist_wilt, & soil_moist_crit, soil_moist_sat, & - grid_canopy_water,grid_throughfall) + grid_canopy_water,grid_throughfall, & + soil_sat_frac, wetness_under_soil) use jules_control_init_mod, only : n_land_tile @@ -140,6 +143,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 From 68ca1a799261dc8b70fb4f9e617a52053ce1620c Mon Sep 17 00:00:00 2001 From: iboutle <135141261+iboutle@users.noreply.github.com> Date: Thu, 4 Jun 2026 16:49:24 +0100 Subject: [PATCH 02/20] add snow prognostics --- applications/lfric_atm/metadata/field_def_diags.xml | 2 ++ .../source/algorithm/jules_extra_alg_mod.x90 | 1 + .../source/diagnostics/jules_snow_diags_mod.x90 | 6 +++++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/applications/lfric_atm/metadata/field_def_diags.xml b/applications/lfric_atm/metadata/field_def_diags.xml index 8ce47824f9..46e9719734 100644 --- a/applications/lfric_atm/metadata/field_def_diags.xml +++ b/applications/lfric_atm/metadata/field_def_diags.xml @@ -606,6 +606,7 @@ + @@ -1011,6 +1012,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 c3d2a16062..3e0a7ad6dc 100644 --- a/interfaces/jules_interface/source/algorithm/jules_extra_alg_mod.x90 +++ b/interfaces/jules_interface/source/algorithm/jules_extra_alg_mod.x90 @@ -277,6 +277,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, & 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 d48f4f0140..bae757384e 100644 --- a/interfaces/jules_interface/source/diagnostics/jules_snow_diags_mod.x90 +++ b/interfaces/jules_interface/source/diagnostics/jules_snow_diags_mod.x90 @@ -74,6 +74,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 +92,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 +109,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') From 946049d937b19e76934e4bee7fd15a1eb9a9a6cb Mon Sep 17 00:00:00 2001 From: iboutle <135141261+iboutle@users.noreply.github.com> Date: Thu, 4 Jun 2026 16:55:59 +0100 Subject: [PATCH 03/20] forgot doxygen comments --- .../jules_interface/source/diagnostics/jules_snow_diags_mod.x90 | 2 ++ 1 file changed, 2 insertions(+) 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 bae757384e..4aae8eec3b 100644 --- a/interfaces/jules_interface/source/diagnostics/jules_snow_diags_mod.x90 +++ b/interfaces/jules_interface/source/diagnostics/jules_snow_diags_mod.x90 @@ -62,6 +62,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 From 009038299065ef2f1fc0f10eb59c771d9e46ef4c Mon Sep 17 00:00:00 2001 From: iboutle <135141261+iboutle@users.noreply.github.com> Date: Tue, 21 Jul 2026 20:34:26 +0100 Subject: [PATCH 04/20] sea and sea-ice temperature --- applications/lfric_atm/metadata/field_def_diags.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/applications/lfric_atm/metadata/field_def_diags.xml b/applications/lfric_atm/metadata/field_def_diags.xml index 46e9719734..920ffde086 100644 --- a/applications/lfric_atm/metadata/field_def_diags.xml +++ b/applications/lfric_atm/metadata/field_def_diags.xml @@ -527,6 +527,10 @@ + + + zoomed__sea_ice_temperature + zoomed__sea_temperature From 51c312bdf685f8e68877408213b4446858cd6594 Mon Sep 17 00:00:00 2001 From: iboutle <135141261+iboutle@users.noreply.github.com> Date: Wed, 22 Jul 2026 11:25:52 +0100 Subject: [PATCH 05/20] bugfixes --- .../source/diagnostics/jules_snow_diags_mod.x90 | 1 + .../source/diagnostics/jules_soil_diags_mod.x90 | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) 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 4aae8eec3b..40bfe5b8ae 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 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 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 ac95601295..a48e6efd09 100644 --- a/interfaces/jules_interface/source/diagnostics/jules_soil_diags_mod.x90 +++ b/interfaces/jules_interface/source/diagnostics/jules_soil_diags_mod.x90 @@ -119,7 +119,8 @@ contains water_table, & soil_moist_wilt, & soil_moist_crit, & - soil_moist_sat + soil_moist_sat, & + soil_sat_frac, wetness_under_soil ! Diagnostics computed within the kernel type( field_type ), intent(in) :: throughfall From 417d2669c024ef3f5fd30988b656215f4b530253 Mon Sep 17 00:00:00 2001 From: iboutle <135141261+iboutle@users.noreply.github.com> Date: Thu, 23 Jul 2026 14:59:03 +0100 Subject: [PATCH 06/20] fix sea-ice area fraction for coupled models --- applications/lfric_atm/metadata/field_def_diags.xml | 8 ++++---- applications/lfric_atm/metadata/grid_def_main.xml | 10 ++++++++++ .../source/physics/multidata_field_dimensions_mod.F90 | 2 +- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/applications/lfric_atm/metadata/field_def_diags.xml b/applications/lfric_atm/metadata/field_def_diags.xml index 920ffde086..002a1227cc 100644 --- a/applications/lfric_atm/metadata/field_def_diags.xml +++ b/applications/lfric_atm/metadata/field_def_diags.xml @@ -496,9 +496,11 @@ - + + surface__tile_fraction + + - zoomed__sea_ice_tile zoomed__sea_tile surface__sea_ice_tile / (surface__sea_ice_tile + surface__sea_tile + 1.0e-16) @@ -527,9 +529,7 @@ - - zoomed__sea_ice_temperature zoomed__sea_temperature diff --git a/applications/lfric_atm/metadata/grid_def_main.xml b/applications/lfric_atm/metadata/grid_def_main.xml index bf68a5bcf5..6c004a19ed 100644 --- a/applications/lfric_atm/metadata/grid_def_main.xml +++ b/applications/lfric_atm/metadata/grid_def_main.xml @@ -132,6 +132,16 @@ + + + + + + + + + + diff --git a/science/gungho/source/physics/multidata_field_dimensions_mod.F90 b/science/gungho/source/physics/multidata_field_dimensions_mod.F90 index b53cbfbdfd..79d9484c26 100644 --- a/science/gungho/source/physics/multidata_field_dimensions_mod.F90 +++ b/science/gungho/source/physics/multidata_field_dimensions_mod.F90 @@ -95,7 +95,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( & From ecb42ccd1e0039bf9c590749027c724cd4744f38 Mon Sep 17 00:00:00 2001 From: iboutle <135141261+iboutle@users.noreply.github.com> Date: Thu, 23 Jul 2026 15:18:43 +0100 Subject: [PATCH 07/20] sea_ice surface temperature --- applications/lfric_atm/metadata/field_def_diags.xml | 11 ++++++----- applications/lfric_atm/metadata/grid_def_main.xml | 2 +- .../source/diagnostics/jules_imp_diags_mod.x90 | 13 +++++++++++-- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/applications/lfric_atm/metadata/field_def_diags.xml b/applications/lfric_atm/metadata/field_def_diags.xml index 002a1227cc..4ffd8ed576 100644 --- a/applications/lfric_atm/metadata/field_def_diags.xml +++ b/applications/lfric_atm/metadata/field_def_diags.xml @@ -499,14 +499,14 @@ surface__tile_fraction - + - 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) @@ -530,9 +530,10 @@ - zoomed__sea_temperature + zoomed__sea_temperature + diff --git a/applications/lfric_atm/metadata/grid_def_main.xml b/applications/lfric_atm/metadata/grid_def_main.xml index 6c004a19ed..18df3c3df4 100644 --- a/applications/lfric_atm/metadata/grid_def_main.xml +++ b/applications/lfric_atm/metadata/grid_def_main.xml @@ -132,7 +132,7 @@ - + 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 13a36d4bf6..e263c891ac 100644 --- a/interfaces/jules_interface/source/diagnostics/jules_imp_diags_mod.x90 +++ b/interfaces/jules_interface/source/diagnostics/jules_imp_diags_mod.x90 @@ -9,7 +9,8 @@ module jules_imp_diags_mod use constants_mod, only: l_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_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 @@ -259,7 +260,8 @@ 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 integer( tik ) :: id if ( LPROF ) call start_timing( id, 'diags.jules_imp' ) @@ -289,6 +291,13 @@ 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 + if (init_diag(grid_canopy_evap, 'surface__grid_canopy_evap')) then call invoke(weighted_ave_kernel_type(grid_canopy_evap, & canopy_evap, tile_fraction, & From 3d06e070f2bc4190ceba862898d92d86b50a33c3 Mon Sep 17 00:00:00 2001 From: iboutle <135141261+iboutle@users.noreply.github.com> Date: Fri, 24 Jul 2026 14:13:07 +0100 Subject: [PATCH 08/20] xios doesn't work for sea-ice fraction --- .../lfric_atm/metadata/field_def_diags.xml | 5 +--- .../lfric_atm/metadata/grid_def_main.xml | 10 ------- .../diagnostics/jules_seaice_diags_mod.x90 | 26 ++++++++++++++++--- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/applications/lfric_atm/metadata/field_def_diags.xml b/applications/lfric_atm/metadata/field_def_diags.xml index 1e45ede8c0..280b32989a 100644 --- a/applications/lfric_atm/metadata/field_def_diags.xml +++ b/applications/lfric_atm/metadata/field_def_diags.xml @@ -500,10 +500,7 @@ - - surface__tile_fraction - - + zoomed__sea_tile surface__sea_ice_tile / (surface__sea_ice_tile + surface__sea_tile + 1.0e-16) diff --git a/applications/lfric_atm/metadata/grid_def_main.xml b/applications/lfric_atm/metadata/grid_def_main.xml index b312e33fea..954b013f96 100644 --- a/applications/lfric_atm/metadata/grid_def_main.xml +++ b/applications/lfric_atm/metadata/grid_def_main.xml @@ -140,16 +140,6 @@ - - - - - - - - - - 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..2db721952e 100644 --- a/interfaces/jules_interface/source/diagnostics/jules_seaice_diags_mod.x90 +++ b/interfaces/jules_interface/source/diagnostics/jules_seaice_diags_mod.x90 @@ -7,11 +7,13 @@ 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 jules_control_init_mod, only: first_sea_ice_tile, & + n_sea_ice_tile + use initialise_diagnostics_mod, only: init_diag => init_diagnostic_field implicit none @@ -46,7 +48,9 @@ contains 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 + real(r_def) :: num_sice_tiles integer( tik ) :: id if ( LPROF ) call start_timing( id, 'diags.jules_seaice' ) @@ -65,6 +69,20 @@ 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 + if ( LPROF ) call stop_timing( id, 'diags.jules_seaice' ) end subroutine output_diags_for_jules_seaice From 2930e2662ac4bef723877ea8c0d5069157dbc969 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 10 Aug 2026 14:45:09 +0000 Subject: [PATCH 09/20] Add open sea sensible heat flux and evaporation diagnostics Adds surface__sea_heat_flux and surface__sea_moisture_flux, equivalent to UM STASH 3-228 SFC SH FLX FROM OPEN SEA:SEA MN and 3-232 EVAP FROM OPEN SEA: SEA MEAN. This is an XML-only change. The open-sea tile is extracted from the existing surface__tile_heat_flux and surface__tile_moisture_flux fields using the surface_tiles_sea_zoom axis, then the pseudo-level coordinate is dropped, following the pattern already used for surface__sea_tile and surface__sea_temperature. A third field is then needed for each because the UM diagnostics are sea means. JULES applies h_sea = (1 - ice_fract) * h_sea and the equivalent for e_sea in the "Calculate sea mean diagnostics" block of jules_ssi_sf_implicit, so the flux is averaged over the whole sea portion of the grid-box with the sea-ice part contributing zero. The LFRic tiled fluxes are per unit open water, being formed as a residual divided by the open-sea tile fraction, so they are multiplied by (1 - surface__sea_ice_fraction) to match. The rescaling cannot be combined with the earlier steps as it needs both operands on the same grid. Some of the content of this change has been produced with the assistance of Anthropic Claude Opus 5 (Claude Code). --- applications/lfric_atm/metadata/field_def_diags.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/applications/lfric_atm/metadata/field_def_diags.xml b/applications/lfric_atm/metadata/field_def_diags.xml index 280b32989a..af9e23d7ad 100644 --- a/applications/lfric_atm/metadata/field_def_diags.xml +++ b/applications/lfric_atm/metadata/field_def_diags.xml @@ -560,7 +560,16 @@ + + + + zoomed__sea_heat_flux + temporary__sea_heat_flux * (1.0 - surface__sea_ice_fraction) + + + zoomed__sea_moisture_flux + temporary__sea_moisture_flux * (1.0 - surface__sea_ice_fraction) From b451ebbb7ba7c3f5bb97e494029f85e3ac8dd768 Mon Sep 17 00:00:00 2001 From: iboutle <135141261+iboutle@users.noreply.github.com> Date: Mon, 10 Aug 2026 15:53:11 +0100 Subject: [PATCH 10/20] fix names --- applications/lfric_atm/metadata/field_def_diags.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/applications/lfric_atm/metadata/field_def_diags.xml b/applications/lfric_atm/metadata/field_def_diags.xml index af9e23d7ad..c0129eac96 100644 --- a/applications/lfric_atm/metadata/field_def_diags.xml +++ b/applications/lfric_atm/metadata/field_def_diags.xml @@ -563,13 +563,13 @@ - zoomed__sea_heat_flux - temporary__sea_heat_flux * (1.0 - surface__sea_ice_fraction) + zoomed__sea_heat_flux + surface__sea_tile_heat_flux * (1.0 - surface__sea_ice_fraction) - zoomed__sea_moisture_flux - temporary__sea_moisture_flux * (1.0 - surface__sea_ice_fraction) + zoomed__sea_moisture_flux + surface__sea_tile_moisture_flux * (1.0 - surface__sea_ice_fraction) From 2ba3ad87a76dc9b390983522787e977cc96727b9 Mon Sep 17 00:00:00 2001 From: iboutle <135141261+iboutle@users.noreply.github.com> Date: Mon, 10 Aug 2026 16:04:48 +0100 Subject: [PATCH 11/20] add to OS file --- rose-stem/app/lfric_atm/file/file_def_diags_oper_nwp_gl.xml | 2 ++ 1 file changed, 2 insertions(+) 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..75d2861db3 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 @@ -146,6 +146,8 @@ + + From 0f9215d8536bb4540e24b69cc84ce7d86d0c7f95 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 11 Aug 2026 08:51:35 +0000 Subject: [PATCH 12/20] Add sea-ice top melt heat flux diagnostic Adds seaice__sea_mean_topmelt_heat_flux, equivalent to UM STASH 3-235 SEAICE TOP MELT LH FLX:SEA MEAN. Unlike the open sea flux diagnostics this cannot be done in XML alone. The UM sums sf_diag%sice_mlt_htf over the sea-ice categories, but LFRic never sets sf_diag%simlt so that array is never filled. The underlying melt rate is available though, as fluxes%sice_melt is copied onto the sea-ice tiles of snowice_melt in jules_imp_kernel_mod, so the diagnostic is reconstructed from there. Averaging over the tiles has to be done in the code rather than in XIOS, following the precedent set for surface__sea_ice_tile. The tile order is land, sea, sea-ice, so averaging the tiled melt rate over the sea and sea-ice tiles, weighted by their fractions, divides by the marine fraction of the grid-box. This gives the ice_fract_ncat weighting the UM applies in jules_ssi_sf_implicit directly, with no rescaling needed in XML, because jules_imp_kernel_mod sets the melt rate on the sea tile to zero and so only the sea-ice contributes to the sum. The latent heat of fusion then converts the melt rate to an energy flux. Note this covers only the surface melt contribution. The second term the UM accumulates in sice_htf, where the ice surface temperature overshoots the melting point, has no LFRic equivalent as sice_htf is never called. Some of the content of this change has been produced with the assistance of Anthropic Claude Opus 5 (Claude Code). --- .../lfric_atm/metadata/field_def_diags.xml | 2 ++ .../source/algorithm/jules_extra_alg_mod.x90 | 6 +++- .../diagnostics/jules_seaice_diags_mod.x90 | 31 ++++++++++++++++--- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/applications/lfric_atm/metadata/field_def_diags.xml b/applications/lfric_atm/metadata/field_def_diags.xml index c0129eac96..531a87939f 100644 --- a/applications/lfric_atm/metadata/field_def_diags.xml +++ b/applications/lfric_atm/metadata/field_def_diags.xml @@ -638,6 +638,8 @@ seaice__sea_ice_pensolar * seaice__sea_ice_fraction + + 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 8c8ac35e4f..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 @@ -304,7 +307,8 @@ contains 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_seaice_diags_mod.x90 b/interfaces/jules_interface/source/diagnostics/jules_seaice_diags_mod.x90 index 2db721952e..ab0de683f7 100644 --- a/interfaces/jules_interface/source/diagnostics/jules_seaice_diags_mod.x90 +++ b/interfaces/jules_interface/source/diagnostics/jules_seaice_diags_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 Processes diagnostics for jules_extra_alg module jules_seaice_diags_mod @@ -11,7 +14,9 @@ module jules_seaice_diags_mod use field_mod, only: field_type use timing_mod, only: start_timing, stop_timing, tik, LPROF use sci_weighted_ave_kernel_mod, only: weighted_ave_kernel_type - use jules_control_init_mod, only: first_sea_ice_tile, & + use driver_water_constants_mod, only: latent_heat_h2o_fusion + use jules_control_init_mod, only: first_sea_tile, n_sea_tile, & + first_sea_ice_tile, & n_sea_ice_tile use initialise_diagnostics_mod, only: init_diag => init_diagnostic_field @@ -25,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 @@ -32,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) @@ -42,14 +49,15 @@ 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, weights_one, & - sea_ice_tile + sea_ice_tile, sea_ice_topmelt real(r_def) :: num_sice_tiles integer( tik ) :: id @@ -83,6 +91,21 @@ contains 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__sea_mean_topmelt_heat_flux')) then + call invoke(weighted_ave_kernel_type(sea_ice_topmelt, & + snowice_melt, tile_fraction, & + first_sea_tile, & + n_sea_tile + n_sea_ice_tile), & + inc_a_times_X(latent_heat_h2o_fusion, sea_ice_topmelt)) + call sea_ice_topmelt%write_field() + end if + if ( LPROF ) call stop_timing( id, 'diags.jules_seaice' ) end subroutine output_diags_for_jules_seaice From ac579908adc16949670ef4947d031795cbc94f2a Mon Sep 17 00:00:00 2001 From: iboutle <135141261+iboutle@users.noreply.github.com> Date: Tue, 11 Aug 2026 10:19:59 +0100 Subject: [PATCH 13/20] name updates --- applications/lfric_atm/metadata/field_def_diags.xml | 11 +++++------ .../source/diagnostics/jules_seaice_diags_mod.x90 | 2 +- .../app/lfric_atm/file/file_def_diags_oper_nwp_gl.xml | 1 + 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/applications/lfric_atm/metadata/field_def_diags.xml b/applications/lfric_atm/metadata/field_def_diags.xml index 531a87939f..d6965c786b 100644 --- a/applications/lfric_atm/metadata/field_def_diags.xml +++ b/applications/lfric_atm/metadata/field_def_diags.xml @@ -563,13 +563,13 @@ - zoomed__sea_heat_flux - surface__sea_tile_heat_flux * (1.0 - surface__sea_ice_fraction) + 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) + zoomed__sea_moisture_flux + surface__sea_tile_moisture_flux * (1.0 - surface__sea_ice_fraction) @@ -638,8 +638,7 @@ seaice__sea_ice_pensolar * seaice__sea_ice_fraction - - + 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 ab0de683f7..10f1987b55 100644 --- a/interfaces/jules_interface/source/diagnostics/jules_seaice_diags_mod.x90 +++ b/interfaces/jules_interface/source/diagnostics/jules_seaice_diags_mod.x90 @@ -97,7 +97,7 @@ contains ! 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__sea_mean_topmelt_heat_flux')) then + 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, & 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 75d2861db3..001bef4d64 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 @@ -148,6 +148,7 @@ + From b484e19473ef504341b693ef157ed8463e3a0436 Mon Sep 17 00:00:00 2001 From: iboutle <135141261+iboutle@users.noreply.github.com> Date: Tue, 11 Aug 2026 10:46:04 +0100 Subject: [PATCH 14/20] fixes to code --- .../source/diagnostics/jules_seaice_diags_mod.x90 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 10f1987b55..c6c0b8c678 100644 --- a/interfaces/jules_interface/source/diagnostics/jules_seaice_diags_mod.x90 +++ b/interfaces/jules_interface/source/diagnostics/jules_seaice_diags_mod.x90 @@ -10,7 +10,7 @@ module jules_seaice_diags_mod - use constants_mod, only: r_def + use constants_mod, only: r_def, i_def use field_mod, only: field_type use timing_mod, only: start_timing, stop_timing, tik, LPROF use sci_weighted_ave_kernel_mod, only: weighted_ave_kernel_type @@ -59,6 +59,7 @@ contains type( field_type ) :: sea_ice_fraction, weights_one, & sea_ice_tile, sea_ice_topmelt real(r_def) :: num_sice_tiles + integer(i_def) :: num_ssi_tiles integer( tik ) :: id if ( LPROF ) call start_timing( id, 'diags.jules_seaice' ) @@ -98,10 +99,11 @@ contains ! 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 + num_ssi_tiles = n_sea_tile + n_sea_ice_tile call invoke(weighted_ave_kernel_type(sea_ice_topmelt, & snowice_melt, tile_fraction, & first_sea_tile, & - n_sea_tile + n_sea_ice_tile), & + num_ssi_tiles), & inc_a_times_X(latent_heat_h2o_fusion, sea_ice_topmelt)) call sea_ice_topmelt%write_field() end if From 3c340f261acb6adc8c2d1ec63012bcbeabe88257 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 11 Aug 2026 09:52:29 +0000 Subject: [PATCH 15/20] Add land-mean surface heat and latent heat flux diagnostics Adds surface__land_mean_heat_flux and surface__land_mean_latent_heat, the surface sensible and latent heat fluxes averaged over the land portion of the grid-box, matching the UM's land-mean surface flux diagnostics. Both are computed with weighted_ave_kernel_type over the land tiles, weighted by the grid-box tile fractions. Because the land tile fractions sum to the land fraction, that average divides by the land fraction and so reproduces the UM sum over land tiles of the tiled flux weighted by the land-relative tile fraction: LFRic's tile_fraction is a grid-box fraction, and the JULES interface divides it by the land fraction when it fills frac_surft. The tiled fields used are the same ones the UM sums, ftl_surft and le_surft. The latent heat flux is only accumulated when it has been requested, so the new diagnostic is added to the existing test that activates it, and both diagnostics are output on the second loop only, alongside the other tile-averaged diagnostics. The two fields are added to the lfric_atm field definitions and to the hourly instantaneous group of the operational global output stream, alongside the other grid-mean surface fluxes there. Some of the content of this change has been produced with the assistance of Anthropic Claude Opus 5 (Claude Code). --- .../lfric_atm/metadata/field_def_diags.xml | 2 ++ .../diagnostics/jules_imp_diags_mod.x90 | 32 +++++++++++++++++-- .../file/file_def_diags_oper_nwp_gl.xml | 2 ++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/applications/lfric_atm/metadata/field_def_diags.xml b/applications/lfric_atm/metadata/field_def_diags.xml index d6965c786b..336407efe2 100644 --- a/applications/lfric_atm/metadata/field_def_diags.xml +++ b/applications/lfric_atm/metadata/field_def_diags.xml @@ -565,6 +565,7 @@ zoomed__sea_heat_flux surface__sea_tile_heat_flux * (1.0 - surface__sea_ice_fraction) + @@ -572,6 +573,7 @@ surface__sea_tile_moisture_flux * (1.0 - surface__sea_ice_fraction) + 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 41740d8962..f8a91bb6c0 100644 --- a/interfaces/jules_interface/source/diagnostics/jules_imp_diags_mod.x90 +++ b/interfaces/jules_interface/source/diagnostics/jules_imp_diags_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 Processes diagnostics for jules_imp_alg module jules_imp_diags_mod @@ -39,6 +42,7 @@ 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 ) :: snomlt_surf_htf_flag logical( l_def ) :: soil_evap_flag logical( l_def ) :: soil_surf_ht_flux_flag @@ -139,10 +143,13 @@ 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') + if ((grid_latent_heat_flag .or. land_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) & call invoke(setval_c(latent_heat,0.0_r_def)) ! diagnostics which are needed in BL code, so allocate on 1st loop @@ -261,7 +268,8 @@ contains ! Local variables type( field_type ) :: land_surface_temperature, grid_canopy_evap, & grid_surface_temperature, grid_latent_heat, grid_sublimation, & - sea_ice_surface_temperature + sea_ice_surface_temperature, land_mean_heat_flux, & + land_mean_latent_heat integer( tik ) :: id if ( LPROF ) call start_timing( id, 'diags.jules_imp' ) @@ -291,6 +299,16 @@ contains call land_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 + 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, & @@ -323,6 +341,14 @@ 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 + else !loop=1 ! Diagnostics computed within the kernel - these are computed on 1st loop 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 001bef4d64..f5235ed971 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 @@ -19,6 +19,8 @@ + + From db25b618bccd1f57b15e40a0df84cb568c6690fb Mon Sep 17 00:00:00 2001 From: iboutle <135141261+iboutle@users.noreply.github.com> Date: Tue, 11 Aug 2026 12:09:19 +0100 Subject: [PATCH 16/20] change order --- .../source/diagnostics/jules_imp_diags_mod.x90 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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 f8a91bb6c0..a1d5407caa 100644 --- a/interfaces/jules_interface/source/diagnostics/jules_imp_diags_mod.x90 +++ b/interfaces/jules_interface/source/diagnostics/jules_imp_diags_mod.x90 @@ -299,6 +299,13 @@ 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. @@ -309,13 +316,6 @@ contains call land_mean_heat_flux%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 - if (init_diag(grid_canopy_evap, 'surface__grid_canopy_evap')) then call invoke(weighted_ave_kernel_type(grid_canopy_evap, & canopy_evap, tile_fraction, & From 8ace74f53a284a05fb5092fd88196f8b8b18cee4 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 11 Aug 2026 11:15:14 +0000 Subject: [PATCH 17/20] Add sea and sea-ice mean surface heat and latent heat flux diagnostics Adds surface__ssi_mean_heat_flux and surface__ssi_mean_latent_heat, the surface sensible and latent heat fluxes averaged over the sea and sea-ice portion of the grid-box, matching the UM's mean sea surface sensible heat flux and its open sea and sea-ice latent heat flux. Both are computed with weighted_ave_kernel_type over the sea and sea-ice tiles, weighted by the grid-box tile fractions. The tile order is land, sea, sea-ice, so those tiles are contiguous, and because their fractions sum to the marine fraction the average divides by it and so gives a mean over the marine portion of the grid-box. For the sensible heat flux this reproduces the UM residual, the grid-box mean flux less the land contribution divided by the marine fraction, because the interface fills the sea tile with exactly the residual needed to make the tile fractions and tiled fluxes recover the grid-box mean. For the latent heat flux it reproduces the UM sum of the sea-mean evaporation and sea-ice sublimation terms, since JULES rescales those by fractions of the sea whereas the tile fractions here are fractions of the grid-box. The two fields are added to the lfric_atm field definitions and to the hourly instantaneous group of the operational global output stream, alongside the land means. Some of the content of this change has been produced with the assistance of Anthropic Claude Opus 5 (Claude Code). --- .../lfric_atm/metadata/field_def_diags.xml | 2 + .../diagnostics/jules_imp_diags_mod.x90 | 37 ++++++++++++++++--- .../file/file_def_diags_oper_nwp_gl.xml | 2 + 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/applications/lfric_atm/metadata/field_def_diags.xml b/applications/lfric_atm/metadata/field_def_diags.xml index 336407efe2..4ab48c1d29 100644 --- a/applications/lfric_atm/metadata/field_def_diags.xml +++ b/applications/lfric_atm/metadata/field_def_diags.xml @@ -566,6 +566,7 @@ zoomed__sea_heat_flux surface__sea_tile_heat_flux * (1.0 - surface__sea_ice_fraction) + @@ -574,6 +575,7 @@ + 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 a1d5407caa..8e5d3c36ae 100644 --- a/interfaces/jules_interface/source/diagnostics/jules_imp_diags_mod.x90 +++ b/interfaces/jules_interface/source/diagnostics/jules_imp_diags_mod.x90 @@ -10,9 +10,10 @@ module jules_imp_diags_mod - use constants_mod, only: l_def, r_def + use constants_mod, only: i_def, l_def, r_def use field_mod, only: field_type use jules_control_init_mod, only: n_surf_tile, n_land_tile, & + n_sea_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, & @@ -43,6 +44,7 @@ module jules_imp_diags_mod 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 @@ -144,12 +146,13 @@ contains latent_heat_flag = init_diag(latent_heat, 'surface__latent_heat') grid_latent_heat_flag = samp_diag('surface__grid_latent_heat') land_mean_latent_heat_flag = samp_diag('surface__land_mean_latent_heat') - if ((grid_latent_heat_flag .or. land_mean_latent_heat_flag) .and. & - .not. latent_heat_flag) then + 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 .or. & - land_mean_latent_heat_flag) & + 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 @@ -269,13 +272,19 @@ contains type( field_type ) :: land_surface_temperature, grid_canopy_evap, & grid_surface_temperature, grid_latent_heat, grid_sublimation, & sea_ice_surface_temperature, land_mean_heat_flux, & - land_mean_latent_heat + land_mean_latent_heat, ssi_mean_heat_flux, ssi_mean_latent_heat + integer( i_def ) :: num_ssi_tiles integer( tik ) :: id if ( LPROF ) call start_timing( id, 'diags.jules_imp' ) ! Output prognostic fields - always output these on 2nd loop if (loop == 2) then + + ! The tile order is land, sea, sea-ice, so the sea and sea-ice tiles are + ! contiguous and can be averaged over together + num_ssi_tiles = n_sea_tile + n_sea_ice_tile + call tile_heat_flux%write_field('surface__tile_heat_flux') call tile_moisture_flux%write_field('surface__tile_moisture_flux') call tile_temperature%write_field('surface__tile_temperature') @@ -316,6 +325,16 @@ contains call land_mean_heat_flux%write_field() end if + ! As above, but over the sea and sea-ice tiles, 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, num_ssi_tiles)) + 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, & @@ -349,6 +368,14 @@ contains 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, num_ssi_tiles)) + 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/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 f5235ed971..42a6285a75 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 @@ -21,6 +21,8 @@ + + From 14cd336987d21350588f9653582f3e2675cc829d Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 11 Aug 2026 12:04:53 +0000 Subject: [PATCH 18/20] Derive the number of sea and sea-ice tiles in jules_control_init The diagnostics that average over the marine portion of the grid-box each worked out the number of sea plus sea-ice tiles for themselves. Derive it once as n_ssi_tile in jules_control_init, alongside the other tile counts and indices, and use it from jules_imp_diags and jules_seaice_diags. n_surf_tile is now expressed in terms of it as well, so the two counts cannot drift apart. Some of the content of this change has been produced with the assistance of Anthropic Claude Opus 5 (Claude Code). --- .../diagnostics/jules_imp_diags_mod.x90 | 20 +++++++------------ .../diagnostics/jules_seaice_diags_mod.x90 | 9 +++------ .../source/support/jules_control_init_mod.f90 | 16 +++++++++++---- 3 files changed, 22 insertions(+), 23 deletions(-) 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 8e5d3c36ae..10d7b8e54a 100644 --- a/interfaces/jules_interface/source/diagnostics/jules_imp_diags_mod.x90 +++ b/interfaces/jules_interface/source/diagnostics/jules_imp_diags_mod.x90 @@ -10,10 +10,10 @@ module jules_imp_diags_mod - use constants_mod, only: i_def, l_def, r_def + 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, & - n_sea_tile, first_sea_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, & @@ -273,18 +273,12 @@ contains 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( i_def ) :: num_ssi_tiles integer( tik ) :: id if ( LPROF ) call start_timing( id, 'diags.jules_imp' ) ! Output prognostic fields - always output these on 2nd loop if (loop == 2) then - - ! The tile order is land, sea, sea-ice, so the sea and sea-ice tiles are - ! contiguous and can be averaged over together - num_ssi_tiles = n_sea_tile + n_sea_ice_tile - call tile_heat_flux%write_field('surface__tile_heat_flux') call tile_moisture_flux%write_field('surface__tile_moisture_flux') call tile_temperature%write_field('surface__tile_temperature') @@ -325,13 +319,13 @@ contains call land_mean_heat_flux%write_field() end if - ! As above, but over the sea and sea-ice tiles, so dividing by the marine - ! fraction and giving a mean over the sea and sea-ice portion of the - ! grid-box. + ! 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, num_ssi_tiles)) + first_sea_tile, n_ssi_tile)) call ssi_mean_heat_flux%write_field() end if @@ -372,7 +366,7 @@ contains 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, num_ssi_tiles)) + first_sea_tile, n_ssi_tile)) call ssi_mean_latent_heat%write_field() end if 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 c6c0b8c678..ae37ff9aff 100644 --- a/interfaces/jules_interface/source/diagnostics/jules_seaice_diags_mod.x90 +++ b/interfaces/jules_interface/source/diagnostics/jules_seaice_diags_mod.x90 @@ -10,12 +10,12 @@ module jules_seaice_diags_mod - use constants_mod, only: r_def, i_def + use constants_mod, only: r_def use field_mod, only: field_type use timing_mod, only: start_timing, stop_timing, tik, LPROF 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_sea_tile, & + 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 @@ -59,7 +59,6 @@ contains type( field_type ) :: sea_ice_fraction, weights_one, & sea_ice_tile, sea_ice_topmelt real(r_def) :: num_sice_tiles - integer(i_def) :: num_ssi_tiles integer( tik ) :: id if ( LPROF ) call start_timing( id, 'diags.jules_seaice' ) @@ -99,11 +98,9 @@ contains ! 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 - num_ssi_tiles = n_sea_tile + n_sea_ice_tile call invoke(weighted_ave_kernel_type(sea_ice_topmelt, & snowice_melt, tile_fraction, & - first_sea_tile, & - num_ssi_tiles), & + 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 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 From 6211ca46270023dd4aa41cdb57f3f14565113950 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 11 Aug 2026 12:35:12 +0000 Subject: [PATCH 19/20] Add the open sea roughness length diagnostic The sea tile of z0m_tile is filled from the JULES prognostic z0msea in jules_exp_kernel_mod, so extracting it needs no new code: a zoom onto the sea tile in the XML gives the open sea roughness length directly. Some of the content of this change has been produced with the assistance of Anthropic Claude Opus 5 (Claude Code). --- applications/lfric_atm/metadata/field_def_diags.xml | 2 ++ rose-stem/app/lfric_atm/file/file_def_diags_oper_nwp_gl.xml | 1 + 2 files changed, 3 insertions(+) diff --git a/applications/lfric_atm/metadata/field_def_diags.xml b/applications/lfric_atm/metadata/field_def_diags.xml index 4ab48c1d29..0f71d805d9 100644 --- a/applications/lfric_atm/metadata/field_def_diags.xml +++ b/applications/lfric_atm/metadata/field_def_diags.xml @@ -511,6 +511,8 @@ 1.0 - (surface__sea_ice_tile + surface__sea_tile) + + zoomed__z0m_sea 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 42a6285a75..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 @@ + From 15088dbad7b97e464709e67489d26526df5eb8b5 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 11 Aug 2026 15:57:26 +0000 Subject: [PATCH 20/20] Add the mean over ice sea-ice thickness and temperature diagnostics Adds seaice__ice_mean_thickness and seaice__ice_mean_temperature, the sea-ice thickness and bulk temperature aggregated from the categories onto a single level, matching the UM's sea-ice depth and sea-ice temperature after timestep. Those two UM fields are the non-category twins of the by-category thickness and top layer temperature, and are what the UM uses when only one category is active. Both are formed with weighted_ave_kernel_type over the sea-ice categories, weighted by the category fractions, so the kernel divides by the total ice fraction and returns a mean over the ice. For the temperature this is exactly the bulk temperature that sice_htf forms internally, which is otherwise held only in the JULES prognostic structure and so cannot be output. For the thickness the same average is correct because the category thicknesses are physical depths, as their use in the conduction term of sice_htf shows, rather than fraction weighted quantities. The weights are the sea-ice tile fractions already extracted from the tile fractions for seaice__sea_ice_fraction. These are fractions of the grid-box whereas JULES works in fractions of the marine portion, but the normalisation cancels in the ratio so the result is unchanged. Where there is no ice the total weight is zero, the kernel leaves the output at zero, and both fields should be masked on the ice fraction before use. Some of the content of this change has been produced with the assistance of Anthropic Claude Opus 5 (Claude Code). --- .../lfric_atm/metadata/field_def_diags.xml | 2 ++ .../diagnostics/jules_seaice_diags_mod.x90 | 25 +++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/applications/lfric_atm/metadata/field_def_diags.xml b/applications/lfric_atm/metadata/field_def_diags.xml index 0f71d805d9..d4124b8a96 100644 --- a/applications/lfric_atm/metadata/field_def_diags.xml +++ b/applications/lfric_atm/metadata/field_def_diags.xml @@ -645,6 +645,8 @@ + + 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 ae37ff9aff..8daf956b7a 100644 --- a/interfaces/jules_interface/source/diagnostics/jules_seaice_diags_mod.x90 +++ b/interfaces/jules_interface/source/diagnostics/jules_seaice_diags_mod.x90 @@ -56,8 +56,9 @@ contains melt_pond_depth ! Diagnostics locally computed here from other fields - type( field_type ) :: sea_ice_fraction, weights_one, & - sea_ice_tile, sea_ice_topmelt + 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 @@ -105,6 +106,26 @@ contains 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