From cba11c042943207a437bba85b09aa14e431b0179 Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Mon, 27 Jul 2026 12:57:18 +0100
Subject: [PATCH 01/35] bring across spectral nudging changes
---
.../rose-app-nudging-newtonian-coarse.conf | 30 ++
.../opt/rose-app-nudging-newtonian.conf | 24 ++
... => rose-app-nudging-spectral-coarse.conf} | 0
...ra.conf => rose-app-nudging-spectral.conf} | 0
rose-stem/app/lfric_atm/rose-app.conf | 6 +
.../common/lfric_atm/tasks_lfric_atm.cylc | 34 ++-
.../site/meto/groups/groups_lfric_atm.cylc | 13 +-
.../lfric-gungho/HEAD/rose-meta.conf | 91 ++++++
.../intermesh_mappings_alg_mod.x90 | 2 +-
.../physics/external_forcing_alg_mod.X90 | 161 ++++++----
.../algorithm/physics/nudging_alg_mod.x90 | 285 ++++++++++++++++++
.../physics_constants_mod.X90 | 275 ++++++++++++++++-
.../configuration/check_configuration_mod.F90 | 38 ++-
.../source/driver/gungho_driver_mod.F90 | 16 +-
.../convolution_2d_kernel_mod.F90 | 110 +++++++
.../convolution_2d_weights_kernel_mod.F90 | 263 ++++++++++++++++
.../ecmwf_level_support_mod.F90 | 163 ++++++++++
.../nudging_vert_interp_kernel_mod.F90 | 264 ++++++++++++++++
.../nudging_weights_kernel_mod.F90 | 118 ++++++++
.../convolution_2d_kernel_mod_test.pf | 98 ++++++
.../convolution_2d_weights_kernel_mod_test.pf | 158 ++++++++++
.../nudging_vert_interp_kernel_mod_test.pf | 146 +++++++++
.../nudging_weights_kernel_mod_test.pf | 55 ++++
23 files changed, 2277 insertions(+), 73 deletions(-)
create mode 100644 rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian-coarse.conf
create mode 100644 rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf
rename rose-stem/app/lfric_atm/opt/{rose-app-nudging-era-coarse.conf => rose-app-nudging-spectral-coarse.conf} (100%)
rename rose-stem/app/lfric_atm/opt/{rose-app-nudging-era.conf => rose-app-nudging-spectral.conf} (100%)
create mode 100644 science/gungho/source/algorithm/physics/nudging_alg_mod.x90
create mode 100644 science/gungho/source/kernel/external_forcing/convolution_2d_kernel_mod.F90
create mode 100644 science/gungho/source/kernel/external_forcing/convolution_2d_weights_kernel_mod.F90
create mode 100644 science/gungho/source/kernel/external_forcing/ecmwf_level_support_mod.F90
create mode 100644 science/gungho/source/kernel/external_forcing/nudging_vert_interp_kernel_mod.F90
create mode 100644 science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90
create mode 100644 science/gungho/unit-test/kernel/external_forcing/convolution_2d_kernel_mod_test.pf
create mode 100644 science/gungho/unit-test/kernel/external_forcing/convolution_2d_weights_kernel_mod_test.pf
create mode 100644 science/gungho/unit-test/kernel/external_forcing/nudging_vert_interp_kernel_mod_test.pf
create mode 100644 science/gungho/unit-test/kernel/external_forcing/nudging_weights_kernel_mod_test.pf
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian-coarse.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian-coarse.conf
new file mode 100644
index 0000000000..68f8c5ef52
--- /dev/null
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian-coarse.conf
@@ -0,0 +1,30 @@
+[file:iodef.xml]
+source=$ROSE_SUITE_DIR/app/lfric_atm/file/iodef_gal_nwp_coarse_nudging.xml
+
+[namelist:external_forcing]
+pc2_force_response=.false.
+theta_forcing='nudging'
+wind_forcing='nudging'
+
+[namelist:files]
+!!coarse_ancil_directory='$BIG_DATA_DIR/ancils/basic-gal/yak/C12'
+nudging_directory='/data/users/mohit.dalvi/lfric/data/nudge/era/C12'
+nudging_filename='era5_nudge_L137_C12_2021032400-2706'
+
+[namelist:formulation]
+use_multires_coupling=.true.
+
+[namelist:multires_coupling]
+aerosol_mesh_name='dynamics'
+coarse_nudging=.true.
+lowest_order_aero_flag=.false.
+multires_coupling_mesh_tags='dynamics','multigrid_l2'
+nudging_mesh_name='multigrid_l2'
+orography_mesh_name='dynamics'
+physics_mesh_name='dynamics'
+
+[namelist:nudging]
+nudging_method='newtonian'
+
+[namelist:section_choice]
+external_forcing=.true.
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf
new file mode 100644
index 0000000000..0ae8acd5b1
--- /dev/null
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf
@@ -0,0 +1,24 @@
+[file:iodef.xml]
+source=$ROSE_SUITE_DIR/app/lfric_atm/file/iodef_gal_nwp_nudging.xml
+
+[namelist:external_forcing]
+pc2_force_response=.false.
+theta_forcing='nudging'
+wind_forcing='nudging'
+
+[namelist:files]
+nudging_directory='/data/users/mohit.dalvi/lfric/data/nudge/era/C48'
+nudging_filename='era5_nudge_L137_C48_MG_2021032400-2706'
+
+[!!namelist:multires_coupling]
+aerosol_mesh_name='dynamics'
+coarse_nudging=.false.
+!!nudging_mesh_name='dynamics'
+orography_mesh_name='dynamics'
+physics_mesh_name='dynamics'
+
+[namelist:nudging]
+nudging_method='newtonian'
+
+[namelist:section_choice]
+external_forcing=.true.
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-era-coarse.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf
similarity index 100%
rename from rose-stem/app/lfric_atm/opt/rose-app-nudging-era-coarse.conf
rename to rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-era.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf
similarity index 100%
rename from rose-stem/app/lfric_atm/opt/rose-app-nudging-era.conf
rename to rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf
diff --git a/rose-stem/app/lfric_atm/rose-app.conf b/rose-stem/app/lfric_atm/rose-app.conf
index 1bcb9f8a6d..1e4ebb11f6 100644
--- a/rose-stem/app/lfric_atm/rose-app.conf
+++ b/rose-stem/app/lfric_atm/rose-app.conf
@@ -1456,9 +1456,15 @@ recovery_order='linear'
nudge_data_levels=137
nudging_level_bottom=5
nudging_level_top=52
+nudging_method='convolution'
nudging_source='era'
+nudging_spinup_start=3600.0
+nudging_spinup_end=7200.0
nudging_width_bottom=1
nudging_width_top=0
+spectral_kmax=10
+spectral_kmin=0
+spectral_stencil_extent=4
[namelist:orbit]
!!arg_periapsis=1.796767421
diff --git a/rose-stem/site/common/lfric_atm/tasks_lfric_atm.cylc b/rose-stem/site/common/lfric_atm/tasks_lfric_atm.cylc
index a37b78aceb..6791d64ae0 100644
--- a/rose-stem/site/common/lfric_atm/tasks_lfric_atm.cylc
+++ b/rose-stem/site/common/lfric_atm/tasks_lfric_atm.cylc
@@ -278,10 +278,10 @@
"plot_str": "plot_map.py $NODAL_DATA_DIR/lfric_diagnostics.nc $PLOT_DIR",
}) %}
-{% elif task_ns.conf_name == "nwp_gal9_nudging-era-C48_MG" %}
+{% elif task_ns.conf_name == "nwp_gal9_nudging-spectral-C48_MG" %}
{% do task_dict.update({
- "opt_confs": ["nudging-era","um_dump"],
+ "opt_confs": ["nudging-spectral","um_dump"],
"resolution": "C48_MG",
"DT": 1800,
"tsteps": 12,
@@ -291,10 +291,36 @@
"plot_str": "plot_map.py $NODAL_DATA_DIR/lfric_diagnostics.nc $PLOT_DIR",
}) %}
-{% elif task_ns.conf_name == "nwp_gal9_nudging-era-coarse-C48_MG" %}
+{% elif task_ns.conf_name == "nwp_gal9_nudging-spectral-coarse-C48_MG" %}
{% do task_dict.update({
- "opt_confs": ["nudging-era-coarse","um_dump"],
+ "opt_confs": ["nudging-spectral-coarse","um_dump"],
+ "resolution": "C48_MG",
+ "DT": 1800,
+ "tsteps": 12,
+ "mpi_parts": 24,
+ "log_level": "info",
+ "kgo_checks": ["checksum"],
+ "plot_str": "plot_map.py $NODAL_DATA_DIR/lfric_diagnostics.nc $PLOT_DIR",
+ }) %}
+
+{% elif task_ns.conf_name == "nwp_gal9_nudging-newtonian-C48_MG" %}
+
+ {% do task_dict.update({
+ "opt_confs": ["nudging-newtonian","um_dump"],
+ "resolution": "C48_MG",
+ "DT": 1800,
+ "tsteps": 12,
+ "mpi_parts": 24,
+ "log_level": "info",
+ "kgo_checks": ["checksum"],
+ "plot_str": "plot_map.py $NODAL_DATA_DIR/lfric_diagnostics.nc $PLOT_DIR",
+ }) %}
+
+{% elif task_ns.conf_name == "nwp_gal9_nudging-newtonian-coarse-C48_MG" %}
+
+ {% do task_dict.update({
+ "opt_confs": ["nudging-newtonian-coarse","um_dump"],
"resolution": "C48_MG",
"DT": 1800,
"tsteps": 12,
diff --git a/rose-stem/site/meto/groups/groups_lfric_atm.cylc b/rose-stem/site/meto/groups/groups_lfric_atm.cylc
index 17717320f0..c89ba5c986 100644
--- a/rose-stem/site/meto/groups/groups_lfric_atm.cylc
+++ b/rose-stem/site/meto/groups/groups_lfric_atm.cylc
@@ -64,8 +64,10 @@
"lfric_atm_canned_azspice_gnu_fast-debug-32bit",
],
"lfric_atm_azspice_nudging": [
- "lfric_atm_nwp_gal9_nudging-era-C48_MG_azspice_gnu_fast-debug-32bit",
- "lfric_atm_nwp_gal9_nudging-era-coarse-C48_MG_azspice_gnu_fast-debug-32bit",
+ "lfric_atm_nwp_gal9_nudging-spectral-C48_MG_azspice_gnu_fast-debug-32bit",
+ "lfric_atm_nwp_gal9_nudging-spectral-coarse-C48_MG_azspice_gnu_fast-debug-32bit",
+ "lfric_atm_nwp_gal9_nudging-newtonian-C48_MG_azspice_gnu_fast-debug-32bit",
+ "lfric_atm_nwp_gal9_nudging-newtonian-coarse-C48_MG_azspice_gnu_fast-debug-32bit",
],
"lfric_atm_azspice_developer": [
"lfric_atm_nwp_azspice_developer",
@@ -177,6 +179,12 @@
"lfric_atm_scm_hd209458b-BiP2x2-50000x50000_ex1a_cce_fast-debug-32bit",
"lfric_atm_canned_ex1a_cce_fast-debug-32bit",
],
+ "lfric_atm_ex1a_nudging": [
+ "lfric_atm_nwp_gal9_nudging-spectral-C48_MG_ex1a_cce_fast-debug-32bit",
+ "lfric_atm_nwp_gal9_nudging-spectral-coarse-C48_MG_ex1a_cce_fast-debug-32bit",
+ "lfric_atm_nwp_gal9_nudging-newtonian-C48_MG_azspice_gnu_fast-debug-32bit",
+ "lfric_atm_nwp_gal9_nudging-newtonian-coarse-C48_MG_ex1a_cce_fast-debug-32bit",
+ ],
"lfric_atm_ex1a_developer": [
"lfric_atm_nwp_ex1a_developer",
"lfric_atm_clim_gal9-C12_ex1a_cce_fast-debug-32bit",
@@ -196,6 +204,7 @@
"lfric_atm_hd209458b-C24_ex1a_cce_fast-debug-32bit",
"lfric_atm_camembert_case3_gj1214b-C12_ex1a_cce_fast-debug-32bit",
"lfric_atm_ex1a_threaded",
+ "lfric_atm_ex1a_nudging",
],
"lfric_atm_ex1a_weekly": [
"lfric_atm_nwp_gal9-C224_MG_ex1a_cce_production-32bit",
diff --git a/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf b/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
index 335d8e89b4..28c2e15574 100644
--- a/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
+++ b/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
@@ -3982,6 +3982,38 @@ ns=namelist/Science/Nudging
sort-key=Panel-A05b
type=integer
+[namelist:nudging=nudging_method]
+compulsory=true
+description=The method used for nudging
+!enumeration=true
+help=Nudging has been implemented with the following methods:
+ = (a) convolution: smooths a field towards a reference state using a
+ = convolution in physical space (which may be equivalent to a specific
+ = filter in spectral space)
+ = (b) newtonian: pointwise nudging towards a reference state
+!kind=default
+ns=namelist/Science/Nudging
+sort-key=Panel-A02
+trigger=namelist:nudging=spectral_stencil_extent: this == "'convolution'";
+ =namelist:nudging=spectral_kmax: this != "'newtonian'";
+ =namelist:nudging=spectral_kmin: this != "'newtonian'";
+value-titles=Convolution, Newtonian
+values='convolution', 'newtonian'
+
+[namelist:nudging=nudging_relax_time]
+compulsory=true
+description=Time scale for nudging (seconds)
+fail_if=this < 0.0 ;
+help=The nudging scheme applies an increment to nudge (parts of the
+ =spectrum of) a field towards a specified reference state. This parameter
+ =gives the time scale (in seconds) after which the spectrum of the field
+ =would match that of the reference field. A value of zero sets the (spectrum
+ =of the) field to that of the reference state in a single time step.
+!kind=default
+ns=namelist/Science/Nudging
+sort-key=Panel-A03
+type=real
+
[namelist:nudging=nudging_source]
compulsory=true
description=The source of the reference states for nudging
@@ -3995,6 +4027,30 @@ sort-key=Panel-A01
value-titles=ERA, AIFS
values='era', 'aifs'
+[namelist:nudging=nudging_spinup_end]
+compulsory=true
+description=Time at which to end nudging spinup (seconds)
+fail_if=this < 0.0 ;
+help=This option allows the nudging to be ramped up linearly over some
+ =spin-up period. This parameter specifies the length of time after the start
+ =of the model simulation to end the nudging spinup, in seconds.
+!kind=default
+ns=namelist/Science/Nudging
+sort-key=Panel-A04b
+type=real
+
+[namelist:nudging=nudging_spinup_start]
+compulsory=true
+description=Time at which to start nudging spinup (seconds)
+fail_if=this < 0.0 ;
+help=This option allows the nudging to be ramped up linearly over some
+ =spin-up period. This parameter specifies the length of time after the start
+ =of the model simulation to begin the nudging spinup, in seconds.
+!kind=default
+ns=namelist/Science/Nudging
+sort-key=Panel-A04a
+type=real
+
[namelist:nudging=nudging_width_bottom]
compulsory=true
description=Width of tapering of nudging at the bottom of the nudging region
@@ -4013,6 +4069,41 @@ ns=namelist/Science/Nudging
sort-key=Panel-A05d
type=integer
+[namelist:nudging=spectral_kmax]
+compulsory=true
+description=Maximum wavenumber to use in spectral nudging filter
+help=The spectral nudging scheme uses a convolution to nudge parts of the
+ =spectrum towards a specified reference state. The convolution replicates
+ =a low-pass top-hat filter in spectral space. This parameter sets the
+ =maximum wavenumber for the top-hat filter.
+!kind=default
+ns=namelist/Science/Nudging
+sort-key=Panel-A06a
+type=integer
+
+[namelist:nudging=spectral_kmin]
+compulsory=true
+description=Minimum wavenumber to use in spectral nudging filter
+help=The spectral nudging scheme uses a convolution to nudge parts of the
+ =spectrum towards a specified reference state. The convolution replicates
+ =a low-pass top-hat filter in spectral space. This parameter sets the
+ =minimum wavenumber for the top-hat filter.
+!kind=default
+ns=namelist/Science/Nudging
+sort-key=Panel-A06b
+type=integer
+
+[namelist:nudging=spectral_stencil_extent]
+compulsory=true
+description=Sets the stencil extent to be used in spectral nudging
+help=The spectral nudging scheme uses a convolution to nudge parts of the
+ =spectrum towards a specified reference state. This parameter sets the
+ =stencil extent to be used in the convolution.
+!kind=default
+ns=namelist/Science/Nudging
+sort-key=Panel-A07
+type=integer
+
#==============================================================================
# ORBIT
#==============================================================================
diff --git a/science/gungho/source/algorithm/core_dynamics/intermesh_mappings_alg_mod.x90 b/science/gungho/source/algorithm/core_dynamics/intermesh_mappings_alg_mod.x90
index 61ee45d90c..3c434fb059 100644
--- a/science/gungho/source/algorithm/core_dynamics/intermesh_mappings_alg_mod.x90
+++ b/science/gungho/source/algorithm/core_dynamics/intermesh_mappings_alg_mod.x90
@@ -48,7 +48,7 @@ module intermesh_mappings_alg_mod
public :: obtain_shifted_rho ! Shifts rho from prime extrusion to shifted extrusion
private :: restrict_scalar ! Non-conservative restriction of W3/Wtheta field
private :: restrict_multidata ! Non-conservative restriction of W3/Wtheta multidata field
- private :: prolong_scalar_simple ! Injection/recovery of W3/Wtheta field
+ public :: prolong_scalar_simple ! Injection/recovery of W3/Wtheta field
private :: prolong_multidata_simple ! Injection/recovery of W3/Wtheta multidata field
private :: prolong_scalar_reversible ! Reversible prolongation of non-conservative W3/Wtheta field
public :: map_scalar_intermesh ! Public routine for non-conservative W3/Wtheta fields
diff --git a/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90 b/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
index 6ea49157b4..ee6110f388 100644
--- a/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
+++ b/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
@@ -6,8 +6,7 @@
!> @brief Interface to external forcing code.
module external_forcing_alg_mod
- use external_forcing_config_mod, only: theta_forcing, &
- theta_forcing_none, &
+ use external_forcing_config_mod, only: theta_forcing_none, &
theta_forcing_held_suarez, &
theta_forcing_earth_like, &
theta_forcing_tidally_locked_earth, &
@@ -15,28 +14,23 @@ module external_forcing_alg_mod
theta_forcing_deep_hot_jupiter, &
theta_forcing_temp_tend, &
theta_forcing_nudging, &
- theta_relaxation, &
- vapour_forcing, &
vapour_forcing_none, &
vapour_forcing_profile, &
- wind_forcing, &
wind_forcing_none, &
wind_forcing_held_suarez, &
wind_forcing_filtering, &
wind_forcing_diffusion, &
wind_forcing_profile, &
- wind_forcing_nudging, &
- vertadvect_forcing, &
- geostrophic_forcing, &
- filtering_order, &
- diffusion_order, &
- diffusion_coefficient, &
- pc2_force_response
+ wind_forcing_nudging
use log_mod, only: log_event, log_scratch_space, &
LOG_LEVEL_INFO, LOG_LEVEL_ERROR
use lfric_xios_write_mod, only: write_field_generic
use field_parent_mod, only: write_interface
+ use nudging_alg_mod, only: nudging_alg, &
+ set_nudging_reference_fields
+ use map_fd_to_prognostics_alg_mod, only: set_wind
+
use held_suarez_fv_kernel_mod, only: held_suarez_fv_kernel_type
use held_suarez_fv_wind_kernel_mod, only: held_suarez_fv_wind_kernel_type
use earth_like_kernel_mod, only: earth_like_kernel_type
@@ -64,10 +58,8 @@ module external_forcing_alg_mod
get_face_selector_ns
use mesh_mod, only: mesh_type
use mr_indices_mod, only: nummr
- use planet_config_mod, only: kappa
- use io_config_mod, only: write_diag, use_xios_io
- use section_choice_config_mod, only: cloud, cloud_um
- use cloud_config_mod, only: scheme, scheme_pc2
+ use section_choice_config_mod, only: cloud_um
+ use cloud_config_mod, only: scheme_pc2
use reference_element_mod, only: T
use sci_set_any_dof_kernel_mod, only: set_any_dof_kernel_type
@@ -130,48 +122,87 @@ contains
implicit none
- type(config_type), intent(in) :: config
-
- type( field_collection_type ), intent( in ) :: derived_fields, cloud_fields
- type( field_type ), intent( in ) :: u, theta, exner, exner_in_wth, rho
- type( field_type ), intent( in ) :: mr_n(nummr)
- type( field_type ), intent( out ) :: dtheta_forcing, du_forcing
- type( field_type ), intent( out ) :: dmv_forcing, dmcl_forcing, &
- dcfl_forcing, dbcf_forcing
- type( mesh_type ), intent( in ), pointer :: twod_mesh
- real( kind=r_def ), intent( in ) :: dt, time_now
- logical( kind=l_def ), intent(inout) :: theta_forcing_done, &
- vapour_forcing_done, &
- wind_forcing_done
+ type(config_type), intent(in) :: config
+ type(field_collection_type), intent(inout) :: derived_fields
+ type(field_collection_type), intent(in) :: cloud_fields
+ type(field_type), intent(in) :: u, theta, exner, exner_in_wth, rho
+ type(field_type), intent(in) :: mr_n(nummr)
+ type(field_type), intent(out) :: dtheta_forcing, du_forcing
+ type(field_type), intent(out) :: dmv_forcing, dmcl_forcing
+ type(field_type), intent(out) :: dcfl_forcing, dbcf_forcing
+ type(mesh_type), pointer, intent(in) :: twod_mesh
+ real(kind=r_def), intent(in) :: dt, time_now
+ logical(kind=l_def), intent(inout) :: theta_forcing_done
+ logical(kind=l_def), intent(inout) :: vapour_forcing_done
+ logical(kind=l_def), intent(inout) :: wind_forcing_done
! Temporary field to unpack from field collection
- type( field_type ), pointer :: u_physics => null()
- type( field_type ), pointer :: w2_rmultiplicity => null()
- type( field_type ), pointer :: dA => null()
- type( field_type ), pointer :: chi(:) => null()
- type( field_type ), pointer :: panel_id => null()
- type( field_type ), pointer :: height_wth => null()
- type( field_type ), pointer :: height_w1 => null()
- type( field_type ), pointer :: height_w2 => null()
- type( field_type ), pointer :: dx_at_w2 => null()
-
- type( integer_field_type ), pointer :: face_selector_ew
- type( integer_field_type ), pointer :: face_selector_ns
-
- type( field_type ) :: u_smoothed, dx, visc_m
-
- integer( kind=i_def ) :: order, nlayers
- real( kind=r_def ) :: diffusion_coefficient_scaled, dx_sum, dx_size
-
- type( mesh_type ), pointer :: mesh => null()
- procedure(write_interface), pointer :: write_behaviour => null()
- integer(tik) :: id
+ type(field_type), pointer :: u_physics
+ type(field_type), pointer :: w2_rmultiplicity
+ type(field_type), pointer :: dA
+ type(field_type), pointer :: chi(:)
+ type(field_type), pointer :: panel_id
+ type(field_type), pointer :: height_wth
+ type(field_type), pointer :: height_w1
+ type(field_type), pointer :: height_w2
+ type(field_type), pointer :: dx_at_w2
+ type(field_type), pointer :: u_in_w3
+ type(field_type), pointer :: v_in_w3
+ type(field_type), pointer :: u_in_w3_ref
+ type(field_type), pointer :: v_in_w3_ref
+ type(field_type), pointer :: theta_ref
+ type(integer_field_type), pointer :: face_selector_ew
+ type(integer_field_type), pointer :: face_selector_ns
+ type(mesh_type), pointer :: mesh
+ procedure(write_interface), pointer :: write_behaviour
+
+ type(field_type) :: u_smoothed, dx, visc_m
+ type(field_type) :: du_in_w3, dv_in_w3, dw_in_wth
+ integer(kind=i_def) :: order, nlayers
+ real(kind=r_def) :: diffusion_coefficient_scaled, dx_sum, dx_size
+ integer(tik) :: id
+
+ ! Config variables
+ integer(kind=i_def) :: theta_forcing
+ integer(kind=i_def) :: vapour_forcing, wind_forcing
+ logical(kind=l_def) :: geostrophic_forcing, vertadvect_forcing
+ logical(kind=l_def) :: pc2_force_response, theta_relaxation
+ integer(kind=i_def) :: filtering_order, diffusion_order
+ real(kind=r_def) :: diffusion_coefficient
+ real(kind=r_def) :: kappa
+ real(kind=r_def) :: nudging_spinup_start
+ integer(kind=i_def) :: cloud, scheme
+ logical(kind=l_def) :: write_diag, use_xios_io
if ( LPROF ) call start_timing( id, 'external_forcing' )
+ ! Get config variables
+ theta_forcing = config%external_forcing%theta_forcing()
+ theta_relaxation = config%external_forcing%theta_relaxation()
+ vapour_forcing = config%external_forcing%vapour_forcing()
+ wind_forcing = config%external_forcing%wind_forcing()
+ geostrophic_forcing = config%external_forcing%geostrophic_forcing()
+ vertadvect_forcing = config%external_forcing%vertadvect_forcing()
+ pc2_force_response = config%external_forcing%pc2_force_response()
+ filtering_order = config%external_forcing%filtering_order()
+ diffusion_order = config%external_forcing%diffusion_order()
+ diffusion_coefficient = config%external_forcing%diffusion_coefficient()
+ cloud = config%section_choice%cloud()
+ scheme = config%cloud%scheme()
+ write_diag = config%io%write_diag()
+ use_xios_io = config%io%use_xios_io()
+ kappa = config%planet%kappa()
+ nudging_spinup_start = config%nudging%nudging_spinup_start()
+
mesh => theta%get_mesh()
height_wth => get_height_fv(config, mesh, Wtheta)
+ ! If nudging is to be applied, obtain reference fields
+ if ( theta_forcing == theta_forcing_nudging .or. &
+ wind_forcing == wind_forcing_nudging ) then
+ call set_nudging_reference_fields(config, derived_fields, exner)
+ end if
+
if ( theta_forcing /= theta_forcing_none .or. theta_relaxation .or. &
vertadvect_forcing ) then
call dtheta_forcing%initialise( vector_space = theta%get_function_space() )
@@ -235,7 +266,11 @@ contains
call temp_tend_profile_alg( config, dtheta_forcing, exner_in_wth, time_now )
case(theta_forcing_nudging)
- ! DO nothing - placeholder for future, field will not be modified
+
+ if (time_now >= nudging_spinup_start) then
+ call derived_fields%get_field('theta_nudging_ref', theta_ref)
+ call nudging_alg(config, dtheta_forcing, theta, theta_ref)
+ end if
case default
@@ -368,9 +403,27 @@ contains
case ( wind_forcing_nudging )
- ! Placeholder for future, only initialise forcing field
- call du_forcing%initialise( vector_space = u%get_function_space() )
- call invoke(setval_c(du_forcing, 0.0_r_def))
+ if (time_now >= nudging_spinup_start) then
+ ! Extract fields for spectral nudging
+ call derived_fields%get_field('u_in_w3', u_in_w3)
+ call derived_fields%get_field('v_in_w3', v_in_w3)
+ call derived_fields%get_field('u_in_w3_nudging_ref', u_in_w3_ref)
+ call derived_fields%get_field('v_in_w3_nudging_ref', v_in_w3_ref)
+
+ ! Initialise components for wind increment
+ call du_forcing%initialise(u%get_function_space())
+ call du_in_w3%initialise(u_in_w3%get_function_space())
+ call dv_in_w3%initialise(v_in_w3%get_function_space())
+ call dw_in_wth%initialise(theta%get_function_space())
+
+ ! Apply nudging to calculate increments for each component
+ call nudging_alg(config, du_in_w3, u_in_w3, u_in_w3_ref)
+ call nudging_alg(config, dv_in_w3, v_in_w3, v_in_w3_ref)
+ call invoke(setval_c(dw_in_wth, 0.0_r_def))
+
+ ! Combine increments to obtain full wind increment
+ call set_wind(config, du_forcing, du_in_w3, dv_in_w3, dw_in_wth)
+ end if
case( wind_forcing_profile )
diff --git a/science/gungho/source/algorithm/physics/nudging_alg_mod.x90 b/science/gungho/source/algorithm/physics/nudging_alg_mod.x90
new file mode 100644
index 0000000000..6400e24457
--- /dev/null
+++ b/science/gungho/source/algorithm/physics/nudging_alg_mod.x90
@@ -0,0 +1,285 @@
+!-------------------------------------------------------------------------------
+! (C) Crown copyright Met Office. All rights reserved.
+! The file LICENCE, distributed with this code, contains details of the terms
+! under which the code may be used.
+!-------------------------------------------------------------------------------
+!> @brief Computes the forcing from nudging to some external data.
+module nudging_alg_mod
+
+ ! Infrastructure
+ use config_mod, only: config_type
+ use constants_mod, only: i_def, l_def, r_def, str_def
+ use field_mod, only: field_type
+ use field_collection_mod, only: field_collection_type
+ use function_space_mod, only: function_space_type
+ use log_mod, only: log_event, LOG_LEVEL_ERROR
+ use mesh_mod, only: mesh_type
+ use mesh_collection_mod, only: mesh_collection
+ use timing_mod, only: start_timing, stop_timing, &
+ tik, LPROF
+
+ ! Configs
+ use external_forcing_config_mod, only: theta_forcing_nudging, &
+ wind_forcing_nudging
+ use nudging_config_mod, only: nudging_method_newtonian, &
+ nudging_method_convolution
+
+ ! Kernels
+ use convolution_2d_kernel_mod, only: convolution_2d_kernel_type
+ use nudging_vert_interp_kernel_mod, only: nudging_vert_interp_kernel_type
+
+ ! Other algorithms
+ use intermesh_mappings_alg_mod, only: map_scalar_intermesh, &
+ prolong_scalar_simple
+
+ ! Getters to runtime constants
+ use physics_constants_mod, only: get_convolution_weights, &
+ get_nudging_weights
+
+ implicit none
+
+ private
+ public :: nudging_alg
+ public :: set_nudging_reference_fields
+
+contains
+
+ !> @details Computes the increment associated with nudging the spectrum of a
+ !! field towards that of a reference field.
+ !> @param[in] config Application namelist configuration object
+ !> @param[in,out] increment Increment field to be computed
+ !> @param[in] field_in Input field to be nudged
+ !> @param[in] field_ref Reference field to nudge towards
+ subroutine nudging_alg(config, increment, field_in, field_ref)
+
+ use multires_coupling_config_mod, only: recovery_order_linear
+
+ implicit none
+
+ type(config_type), intent(in) :: config
+ type(field_type), intent(inout) :: increment
+ type(field_type), intent(in) :: field_in, field_ref
+
+ type(mesh_type), pointer :: mesh, coarse_mesh
+ type(field_type), pointer :: convolution_weights
+ type(field_type), pointer :: nudging_weights
+ type(function_space_type), pointer :: fs
+
+ type(field_type) :: coarse_field_in, coarse_increment, coarse_field_diff
+ integer(kind=i_def) :: fs_id, coarse_mesh_id
+ integer(kind=tik) :: id
+
+ ! Config variables
+ logical(kind=l_def) :: coarse_nudging, use_multires_coupling
+ integer(kind=i_def) :: nudging_method, spectral_stencil_extent
+ integer(kind=i_def) :: nudge_data_levels
+ integer(kind=i_def) :: theta_forcing, wind_forcing
+ character(len=str_def) :: nudging_mesh_name
+ real(kind=r_def) :: p_zero, kappa
+
+ if ( LPROF ) call start_timing( id, 'nudging' )
+
+ ! ------------------------------------------------------------------------ !
+ ! Get config variables
+ ! ------------------------------------------------------------------------ !
+
+ coarse_nudging = config%multires_coupling%coarse_nudging()
+ use_multires_coupling = config%formulation%use_multires_coupling()
+ nudging_method = config%nudging%nudging_method()
+ spectral_stencil_extent = config%nudging%spectral_stencil_extent()
+ nudge_data_levels = config%nudging%nudge_data_levels()
+ theta_forcing = config%external_forcing%theta_forcing()
+ wind_forcing = config%external_forcing%wind_forcing()
+ nudging_mesh_name = config%multires_coupling%nudging_mesh_name()
+ p_zero = config%planet%p_zero()
+ kappa = config%planet%kappa()
+
+ ! ------------------------------------------------------------------------ !
+ ! Set pointers and initialise fields
+ ! ------------------------------------------------------------------------ !
+
+ if (use_multires_coupling .and. coarse_nudging) then
+ coarse_mesh => mesh_collection%get_mesh(trim(nudging_mesh_name))
+ else
+ mesh => field_in%get_mesh()
+ coarse_mesh => mesh
+ end if
+
+ ! Check reference field is on the correct mesh
+ coarse_mesh_id = coarse_mesh%get_id()
+ if (coarse_mesh_id /= field_ref%get_mesh_id()) then
+
+ call log_event( &
+ "nudging_alg: reference field is not on the mesh " // &
+ "specified for nudging", &
+ LOG_LEVEL_ERROR &
+ )
+ end if
+
+ ! Initialise coarse fields
+ fs => field_ref%get_function_space()
+ call coarse_field_in%initialise(fs)
+ call coarse_increment%initialise(fs)
+
+ ! Point to weights used for nudging
+ fs_id = field_ref%which_function_space()
+ nudging_weights => get_nudging_weights(config, coarse_mesh, fs_id)
+
+ ! ------------------------------------------------------------------------ !
+ ! Coarsen
+ ! ------------------------------------------------------------------------ !
+
+ if (coarse_nudging) then
+ call map_scalar_intermesh(coarse_field_in, field_in)
+ else
+ call invoke( setval_X(coarse_field_in, field_in) )
+ end if
+
+ select case (nudging_method)
+
+ ! ------------------------------------------------------------------------ !
+ ! Newtonian relaxation
+ ! ------------------------------------------------------------------------ !
+ case (nudging_method_newtonian)
+ ! Simply nudge towards reference field, with no spectral impact
+ call invoke( &
+ X_minus_Y(coarse_increment, field_ref, coarse_field_in), &
+ inc_X_times_Y(coarse_increment, nudging_weights) &
+ )
+
+ ! ------------------------------------------------------------------------ !
+ ! Convolution
+ ! ------------------------------------------------------------------------ !
+ case (nudging_method_convolution)
+ ! Filter difference using a convolution in physical space
+ convolution_weights => get_convolution_weights(config, coarse_mesh)
+ call coarse_field_diff%initialise(fs, halo_depth=spectral_stencil_extent)
+ call invoke( &
+ X_minus_Y(coarse_field_diff, field_ref, coarse_field_in), &
+ ! Compute convolution
+ setval_c(coarse_increment, 0.0_r_def), &
+ convolution_2d_kernel_type( &
+ coarse_increment, coarse_field_diff, &
+ spectral_stencil_extent, convolution_weights &
+ ), &
+ ! Scale convolution increment by weights
+ inc_X_times_Y(coarse_increment, nudging_weights) &
+ )
+
+ end select
+
+ ! ------------------------------------------------------------------------ !
+ ! Reconstruction
+ ! ------------------------------------------------------------------------ !
+
+ if (coarse_nudging) then
+ call prolong_scalar_simple( &
+ increment, coarse_increment, recovery_order_linear &
+ )
+ else
+ call invoke( setval_X(increment, coarse_increment) )
+ end if
+
+ if ( LPROF ) call stop_timing( id, 'nudging' )
+
+ end subroutine nudging_alg
+
+ !> @brief Sets the reference fields for nudging from external data
+ !> @param[in] config Application namelist configuration object
+ !> @param[in,out] derived_fields Collection of derived fields, which holds
+ !! both the model's reference fields and any
+ !! external reference fields
+ !> @param[in] exner Current Exner pressure field
+ subroutine set_nudging_reference_fields(config, derived_fields, exner)
+
+ implicit none
+
+ ! Arguments
+ type(config_type), intent(in) :: config
+ type(field_collection_type), intent(inout) :: derived_fields
+ type(field_type), target, intent(in) :: exner
+
+ ! Local variables
+ type(field_type), pointer :: theta_ref
+ type(field_type), pointer :: u_in_w3_ref
+ type(field_type), pointer :: v_in_w3_ref
+ type(field_type), pointer :: T_nudging_ext_ref
+ type(field_type), pointer :: u_nudging_ext_ref
+ type(field_type), pointer :: v_nudging_ext_ref
+ type(field_type), pointer :: surfp_nudging_ext_ref
+ type(field_type), pointer :: exner_in_wth
+ type(field_type), pointer :: exner_in_w3
+ type(field_type), target :: exner_in_wth_coarse
+ type(field_type), target :: exner_in_w3_coarse
+
+ ! Config variables
+ integer(kind=i_def) :: theta_forcing, wind_forcing
+ integer(kind=i_def) :: nudge_data_levels
+ real(kind=r_def) :: p_zero, kappa
+ logical(kind=l_def) :: coarse_nudging
+
+ ! Get config variables
+ theta_forcing = config%external_forcing%theta_forcing()
+ wind_forcing = config%external_forcing%wind_forcing()
+ nudge_data_levels = config%nudging%nudge_data_levels()
+ p_zero = config%planet%p_zero()
+ kappa = config%planet%kappa()
+ coarse_nudging = config%multires_coupling%coarse_nudging()
+
+ ! Get target reference fields from derived field collection
+ if (theta_forcing == theta_forcing_nudging) then
+ call derived_fields%get_field('theta_nudging_ref', theta_ref)
+ end if
+
+ if (wind_forcing == wind_forcing_nudging) then
+ call derived_fields%get_field('u_in_w3_nudging_ref', u_in_w3_ref)
+ call derived_fields%get_field('v_in_w3_nudging_ref', v_in_w3_ref)
+ end if
+
+ ! ------------------------------------------------------------------------ !
+ ! Set reference fields based on input external data
+ ! ------------------------------------------------------------------------ !
+ if (.not. (theta_forcing == theta_forcing_nudging .or. &
+ wind_forcing == wind_forcing_nudging) ) then
+ call log_event( &
+ "set_nudging_reference_fields: nudging to external data. This " // &
+ "is currently only implemented to nudge both theta and wind", &
+ LOG_LEVEL_ERROR &
+ )
+ end if
+
+ ! Get external fields
+ call derived_fields%get_field('temperature_nudging_ext_ref', T_nudging_ext_ref)
+ call derived_fields%get_field('u_nudging_ext_ref', u_nudging_ext_ref)
+ call derived_fields%get_field('v_nudging_ext_ref', v_nudging_ext_ref)
+ call derived_fields%get_field('exner_in_wth', exner_in_wth)
+ call derived_fields%get_field( &
+ 'surface_pressure_nudging_ext_ref', surfp_nudging_ext_ref &
+ )
+
+ ! Map Exner fields to coarse mesh, if required
+ if (coarse_nudging) then
+ call exner_in_wth_coarse%initialise(theta_ref%get_function_space())
+ call exner_in_w3_coarse%initialise(u_in_w3_ref%get_function_space())
+ call map_scalar_intermesh(exner_in_wth_coarse, exner_in_wth)
+ call map_scalar_intermesh(exner_in_w3_coarse, exner)
+ exner_in_wth => exner_in_wth_coarse
+ exner_in_w3 => exner_in_w3_coarse
+ else
+ exner_in_w3 => exner
+ end if
+
+ ! Vertical remapping of external fields to model levels
+ call invoke( &
+ nudging_vert_interp_kernel_type( &
+ theta_ref, u_in_w3_ref, v_in_w3_ref, &
+ T_nudging_ext_ref, u_nudging_ext_ref, v_nudging_ext_ref, &
+ exner_in_wth, exner_in_w3, surfp_nudging_ext_ref, &
+ p_zero, kappa, nudge_data_levels &
+ ) &
+ )
+
+ end subroutine set_nudging_reference_fields
+
+
+end module nudging_alg_mod
diff --git a/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90 b/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
index 674d098ffe..10b7bdd2ba 100644
--- a/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
+++ b/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
@@ -42,7 +42,8 @@ module physics_constants_mod
! Variables private to this module that can only be accessed by public
! functions returning pointers to them
- real(kind=r_def) :: dt_stored = 0.0_r_def
+ real(kind=r_def) :: dt_stored = 0.0_r_def
+ logical(kind=l_def) :: to_update_nudging_weights = .true.
! ========================================================================== !
! Inventories for use in the rest of the model
@@ -54,6 +55,10 @@ module physics_constants_mod
type(inventory_by_mesh_type) :: rdz_w3_inventory
type(inventory_by_mesh_type) :: max_diff_wtheta_inventory
+ type(inventory_by_mesh_type), target :: nudging_weights_w3_inventory
+ type(inventory_by_mesh_type), target :: nudging_weights_wt_inventory
+ type(inventory_by_local_mesh_type) :: convolution_weights_inventory
+
#ifdef UM_PHYSICS
! Spherical harmonics, used by stochastic physics
type(inventory_by_local_mesh_type) :: Pnm_star_inventory
@@ -69,6 +74,10 @@ module physics_constants_mod
public :: get_rdz_fd1
public :: get_rdz_w3
public :: get_max_diff
+ public :: get_convolution_weights
+ public :: get_nudging_weights
+ public :: update_nudging_weights
+
#ifdef UM_PHYSICS
public :: get_Pnm_star
#endif
@@ -404,6 +413,158 @@ contains
end function get_max_diff
+ !> @brief Returns the weights for performing a 2D convolution
+ !> @param[in] config Application namelist configuration object
+ !> @param[in] mesh The mesh to get the object for
+ !> @return The convolution_weights field
+ function get_convolution_weights(config, mesh) result(convolution_weights)
+
+ use base_mesh_config_mod, only: geometry_spherical
+ use convolution_2d_weights_kernel_mod, only: convolution_2d_weights_kernel_type
+
+ implicit none
+
+ type(config_type), intent(in) :: config
+ type(mesh_type), intent(in) :: mesh
+ integer(kind=i_def) :: local_mesh_id
+ integer(kind=i_def) :: ndata
+ integer(kind=i_def) :: stencil_depth
+ type(mesh_type), pointer :: twod_mesh
+ type(local_mesh_type), pointer :: local_mesh
+ type(function_space_type), pointer :: fs
+ type(field_type), pointer :: convolution_weights
+ type(field_type), pointer :: chi(:)
+ type(field_type), pointer :: panel_id
+ logical(kind=l_def) :: constant_exists
+ integer(kind=i_def) :: geometry, topology, coord_system
+ integer(kind=i_def) :: spectral_kmax, spectral_kmin
+ real(kind=r_def) :: scaled_radius
+ integer(kind=tik) :: id
+
+ ! Initialise inventory if it hasn't been done so already
+ if (.not. convolution_weights_inventory%is_initialised()) then
+ call convolution_weights_inventory%initialise(name="convolution_weights")
+ end if
+
+ local_mesh => mesh%get_local_mesh()
+ local_mesh_id = local_mesh%get_id()
+ constant_exists = convolution_weights_inventory%paired_object_exists(local_mesh_id)
+
+ ! Create object if it doesn't already exist
+ if (.not. constant_exists) then
+ chi => get_coordinates(mesh)
+ panel_id => get_panel_id(mesh)
+ twod_mesh => mesh_collection%get_mesh(mesh, TWOD)
+ geometry = config%base_mesh%geometry()
+ topology = config%base_mesh%topology()
+ coord_system = config%finite_element%coord_system()
+ scaled_radius = config%planet%scaled_radius()
+ spectral_kmax = config%nudging%spectral_kmax()
+ spectral_kmin = config%nudging%spectral_kmin()
+ stencil_depth = config%nudging%spectral_stencil_extent()
+ ndata = (2*stencil_depth + 1)**2
+
+ if (geometry /= geometry_spherical) then
+ call log_event( &
+ 'Spectral nudging only implemented for spherical geometry', &
+ LOG_LEVEL_ERROR &
+ )
+ end if
+
+ if ( LPROF ) call start_timing( id, 'runtime_constants.physics' )
+
+ fs => function_space_collection%get_fs(twod_mesh, 0, 0, W3, ndata)
+ call convolution_weights_inventory%add_field(convolution_weights, fs, local_mesh)
+
+ call invoke( convolution_2d_weights_kernel_type(convolution_weights, &
+ chi, stencil_depth, &
+ panel_id, stencil_depth, &
+ spectral_kmax, &
+ spectral_kmin, &
+ geometry, topology, &
+ coord_system, &
+ scaled_radius) )
+
+ if ( LPROF ) call stop_timing( id, 'runtime_constants.physics' )
+ end if
+
+ call convolution_weights_inventory%get_field(local_mesh, convolution_weights)
+
+ end function get_convolution_weights
+
+ !> @brief Returns the weights for applying spectral nudging
+ !> @param[in] config Application namelist configuration object
+ !> @param[in] mesh The mesh to get the object for
+ !> @param[in] fs_id The function space ID to get the object for
+ !> @return The nudging_weights field
+ function get_nudging_weights(config, mesh, fs_id) result(nudging_weights)
+
+ use nudging_weights_kernel_mod, only: nudging_weights_kernel_type
+
+ implicit none
+
+ type(config_type), intent(in) :: config
+ type(mesh_type), intent(in) :: mesh
+ integer(kind=i_def), intent(in) :: fs_id
+ integer(kind=i_def) :: mesh_id
+ type(function_space_type), pointer :: fs
+ type(field_type), pointer :: nudging_weights
+ type(inventory_by_mesh_type), pointer :: nudging_weights_inventory
+ logical(kind=l_def) :: constant_exists
+ character(len=str_def) :: inventory_name
+ integer(kind=i_def) :: nudging_level_top
+ integer(kind=i_def) :: nudging_level_bottom
+ integer(kind=i_def) :: nudging_width_top
+ integer(kind=i_def) :: nudging_width_bottom
+ integer(kind=tik) :: id
+
+ if (fs_id == W3) then
+ nudging_weights_inventory => nudging_weights_w3_inventory
+ inventory_name = "nudging_weights_w3"
+ else if (fs_id == Wtheta) then
+ nudging_weights_inventory => nudging_weights_wt_inventory
+ inventory_name = "nudging_weights_wt"
+ else
+ call log_event( &
+ 'Spectral nudging weights only implemented for W3 and Wtheta', &
+ LOG_LEVEL_ERROR &
+ )
+ end if
+
+ ! Initialise inventory if it hasn't been done so already
+ if (.not. nudging_weights_inventory%is_initialised()) then
+ call nudging_weights_inventory%initialise(name=inventory_name)
+ end if
+
+ mesh_id = mesh%get_id()
+ constant_exists = nudging_weights_inventory%paired_object_exists(mesh_id)
+ nudging_level_bottom = config%nudging%nudging_level_bottom()
+ nudging_level_top = config%nudging%nudging_level_top()
+ nudging_width_bottom = config%nudging%nudging_width_bottom()
+ nudging_width_top = config%nudging%nudging_width_top()
+
+ ! Create object if it doesn't already exist
+ if (.not. constant_exists) then
+ fs => function_space_collection%get_fs(mesh, 0, 0, fs_id)
+
+ if ( LPROF ) call start_timing( id, 'runtime_constants.physics' )
+
+ call nudging_weights_inventory%add_field(nudging_weights, fs, mesh)
+
+ call invoke( &
+ nudging_weights_kernel_type( &
+ nudging_weights, nudging_level_bottom, nudging_level_top, &
+ nudging_width_bottom, nudging_width_top &
+ ) &
+ )
+
+ if ( LPROF ) call stop_timing( id, 'runtime_constants.physics' )
+ end if
+
+ call nudging_weights_inventory%get_field(mesh, nudging_weights)
+
+ end function get_nudging_weights
+
#ifdef UM_PHYSICS
!> @brief Returns the Legendre Polynomials times the SH amplitude
!> @param[in] config Application namelist configuration object
@@ -472,10 +633,122 @@ contains
call rdz_fd1_inventory%clear()
call rdz_w3_inventory%clear()
call max_diff_wtheta_inventory%clear()
+ call nudging_weights_w3_inventory%clear()
+ call nudging_weights_wt_inventory%clear()
+ call convolution_weights_inventory%clear()
#ifdef UM_PHYSICS
call Pnm_star_inventory%clear()
#endif
end subroutine final_physics_constants
+ ! ========================================================================== !
+ ! UPDATE SPECTRAL NUDGING WEIGHTS
+ ! ========================================================================== !
+ !> @brief Update the spectral nudging weights based on the current time
+ !> @param[in] model_clock The model clock object
+ !> @param[in] config Application namelist configuration object
+ !> @param[in] mesh The mesh to compute the weights on
+ subroutine update_nudging_weights(model_clock, config, mesh)
+
+ use nudging_weights_kernel_mod, only: nudging_weights_kernel_type
+
+ implicit none
+
+ class(model_clock_type), intent(in) :: model_clock
+ type(config_type), intent(in) :: config
+ type(mesh_type), intent(in) :: mesh
+ type(field_type), pointer :: nudging_weights
+ real(kind=r_def) :: nudging_spinup_ratio
+ real(kind=r_def) :: dt, time
+
+ ! Configuration variables
+ integer(kind=i_def) :: nudging_level_bottom, nudging_level_top
+ integer(kind=i_def) :: nudging_width_bottom, nudging_width_top
+ real(kind=r_def) :: nudging_spinup_start, nudging_spinup_end
+ real(kind=r_def) :: nudging_relax_time
+
+ ! Get nudging configuration variables
+ nudging_level_bottom = config%nudging%nudging_level_bottom()
+ nudging_level_top = config%nudging%nudging_level_top()
+ nudging_width_bottom = config%nudging%nudging_width_bottom()
+ nudging_width_top = config%nudging%nudging_width_top()
+ nudging_spinup_start = config%nudging%nudging_spinup_start()
+ nudging_spinup_end = config%nudging%nudging_spinup_end()
+ nudging_relax_time = config%nudging%nudging_relax_time()
+
+ ! NB: this assumes that dt is constant
+ dt = REAL(model_clock%get_seconds_per_step(), r_def)
+ time = dt*REAL(model_clock%get_step() - 1, r_def)
+
+ ! Set factor for nudging weight during spin-up period
+ if (time <= nudging_spinup_start) then
+ ! Before spin-up period
+ nudging_spinup_ratio = 0.0_r_def
+
+ ! Set weights to zero
+ nudging_weights => get_nudging_weights(config, mesh, W3)
+ call invoke( setval_c(nudging_weights, 0.0_r_def) )
+ nudging_weights => get_nudging_weights(config, mesh, Wtheta)
+ call invoke( setval_c(nudging_weights, 0.0_r_def) )
+
+ ! Do not need to call usual kernel so set flag to false
+ to_update_nudging_weights = .false.
+
+ else if (time > nudging_spinup_start .and. time < nudging_spinup_end) then
+ ! During spin-up period
+ if (ABS(nudging_spinup_end - nudging_spinup_start) < EPS) then
+ nudging_spinup_ratio = 1.0_r_def
+ else
+ nudging_spinup_ratio = ( &
+ (time - nudging_spinup_start) &
+ / (nudging_spinup_end - nudging_spinup_start) &
+ )
+ end if
+ ! Ramping up nudging weights, so enforce that they are updated
+ to_update_nudging_weights = .true.
+
+ else
+ ! After spin-up period
+ nudging_spinup_ratio = 1.0_r_def
+ end if
+
+ ! Now adjust weighting based on relaxation time
+ if (nudging_relax_time > EPS) then
+ nudging_spinup_ratio = nudging_spinup_ratio * ( &
+ MIN(1.0_r_def, dt / nudging_relax_time) &
+ )
+ end if
+
+ if (to_update_nudging_weights) then
+ ! Update W3 nudging weights
+ nudging_weights => get_nudging_weights(config, mesh, W3)
+
+ call invoke( &
+ nudging_weights_kernel_type( &
+ nudging_weights, nudging_level_bottom, nudging_level_top, &
+ nudging_width_bottom, nudging_width_top &
+ ), &
+ inc_a_times_X(nudging_spinup_ratio, nudging_weights) &
+ )
+
+ ! Update Wtheta nudging weights
+ nudging_weights => get_nudging_weights(config, mesh, Wtheta)
+
+ call invoke( &
+ nudging_weights_kernel_type( &
+ nudging_weights, nudging_level_bottom, nudging_level_top, &
+ nudging_width_bottom, nudging_width_top &
+ ), &
+ inc_a_times_X(nudging_spinup_ratio, nudging_weights) &
+ )
+
+ ! If after spin-up period, we don't need to update the weights any more
+ if (time >= nudging_spinup_end) then
+ to_update_nudging_weights = .false.
+ end if
+ end if
+
+ end subroutine update_nudging_weights
+
end module physics_constants_mod
diff --git a/science/gungho/source/configuration/check_configuration_mod.F90 b/science/gungho/source/configuration/check_configuration_mod.F90
index 201537a5a7..ead552c9bf 100644
--- a/science/gungho/source/configuration/check_configuration_mod.F90
+++ b/science/gungho/source/configuration/check_configuration_mod.F90
@@ -625,6 +625,10 @@ end subroutine check_configuration
!===========================================================================
subroutine get_required_stencil_depth(stencil_depths, base_mesh_names, config)
+ use external_forcing_config_mod, only: theta_forcing_nudging, &
+ wind_forcing_nudging
+ use nudging_config_mod, only: nudging_method_convolution
+
implicit none
integer(kind=i_def), intent(inout) :: stencil_depths(:)
@@ -639,8 +643,10 @@ subroutine get_required_stencil_depth(stencil_depths, base_mesh_names, config)
! Configuration variables
character(len=str_def) :: prime_mesh_name
character(len=str_def) :: aerosol_mesh_name
+ character(len=str_def) :: nudging_mesh_name
logical(kind=l_def) :: use_multires_coupling
logical(kind=l_def) :: coarse_aerosol_transport
+ logical(kind=l_def) :: coarse_nudging
integer(kind=i_def) :: operators
integer(kind=i_def) :: fv_horizontal_order
integer(kind=i_def) :: panel_edge_treatment
@@ -648,24 +654,34 @@ subroutine get_required_stencil_depth(stencil_depths, base_mesh_names, config)
integer(kind=i_def) :: dep_pt_stencil_extent
integer(kind=i_def) :: ffsl_inner_order
integer(kind=i_def) :: ffsl_outer_order
+ integer(kind=i_def) :: theta_forcing
+ integer(kind=i_def) :: wind_forcing
+ integer(kind=i_def) :: nudging_method
+ integer(kind=i_def) :: spectral_stencil_extent
! ------------------------------------------------------------------------ !
! Get configuration variables
! ------------------------------------------------------------------------ !
prime_mesh_name = config%base_mesh%prime_mesh_name()
- operators = config%transport%operators()
- fv_horizontal_order = config%transport%fv_horizontal_order()
- panel_edge_treatment = config%transport%panel_edge_treatment()
- panel_edge_high_order = config%transport%panel_edge_high_order()
- dep_pt_stencil_extent = config%transport%dep_pt_stencil_extent()
- ffsl_inner_order = config%transport%ffsl_inner_order()
- ffsl_outer_order = config%transport%ffsl_outer_order()
+ operators = config%transport%operators()
+ fv_horizontal_order = config%transport%fv_horizontal_order()
+ panel_edge_treatment = config%transport%panel_edge_treatment()
+ panel_edge_high_order = config%transport%panel_edge_high_order()
+ dep_pt_stencil_extent = config%transport%dep_pt_stencil_extent()
+ ffsl_inner_order = config%transport%ffsl_inner_order()
+ ffsl_outer_order = config%transport%ffsl_outer_order()
+ theta_forcing = config%external_forcing%theta_forcing()
+ wind_forcing = config%external_forcing%wind_forcing()
+ nudging_method = config%nudging%nudging_method()
+ spectral_stencil_extent = config%nudging%spectral_stencil_extent()
use_multires_coupling = config%formulation%use_multires_coupling()
if (use_multires_coupling) then
aerosol_mesh_name = config%multires_coupling%aerosol_mesh_name()
coarse_aerosol_transport = config%multires_coupling%coarse_aerosol_transport()
+ nudging_mesh_name = config%multires_coupling%nudging_mesh_name()
+ coarse_nudging = config%multires_coupling%coarse_nudging()
end if
! ------------------------------------------------------------------------ !
@@ -738,6 +754,14 @@ subroutine get_required_stencil_depth(stencil_depths, base_mesh_names, config)
! No transport on this mesh, so set stencil depth to 2
stencil_depths(i) = 2
end if
+
+ if ( (theta_forcing == theta_forcing_nudging &
+ .or. wind_forcing == wind_forcing_nudging) &
+ .and. nudging_method == nudging_method_convolution &
+ .and. (use_multires_coupling .and. coarse_nudging &
+ .and. trim(base_mesh_names(i)) == trim(nudging_mesh_name)) ) then
+ stencil_depths(i) = MAX(stencil_depths(i), spectral_stencil_extent)
+ end if
end do
end subroutine get_required_stencil_depth
diff --git a/science/gungho/source/driver/gungho_driver_mod.F90 b/science/gungho/source/driver/gungho_driver_mod.F90
index 81c4b31886..f7d0a02894 100644
--- a/science/gungho/source/driver/gungho_driver_mod.F90
+++ b/science/gungho/source/driver/gungho_driver_mod.F90
@@ -358,17 +358,20 @@ end subroutine initialise
!>
subroutine step( modeldb )
+ use physics_constants_mod, only: update_nudging_weights
+
implicit none
type(modeldb_type), intent(inout) :: modeldb
- type(mesh_type), pointer :: mesh => null()
- type(mesh_type), pointer :: twod_mesh => null()
+ type(mesh_type), pointer :: mesh
+ type(mesh_type), pointer :: twod_mesh
+ type(mesh_type), pointer :: nudging_mesh
integer(kind=i_def) :: ts_start, rc
integer(tik) :: tid_first, tid_rest
#if defined(COUPLED) || defined(UM_PHYSICS)
- type( field_collection_type ), pointer :: depository => null()
+ type( field_collection_type ), pointer :: depository
#endif
type( field_collection_type ), pointer :: lbc_fields
@@ -379,7 +382,7 @@ subroutine step( modeldb )
type( field_collection_type ), pointer :: derived_fields
#ifdef UM_PHYSICS
- procedure(regridder), pointer :: regrid_operation => null()
+ procedure(regridder), pointer :: regrid_operation
logical(l_def) :: regrid_lowest_order
type( field_collection_type ), pointer :: surface_fields
@@ -388,6 +391,7 @@ subroutine step( modeldb )
integer(i_def) :: theta_forcing
integer(i_def) :: wind_forcing
+ character(len=str_def) :: nudging_mesh_name
read(timestep_start,*,iostat=rc) ts_start
if ( LPROF ) then
@@ -469,6 +473,7 @@ subroutine step( modeldb )
if ( external_forcing_is_loaded() ) then
theta_forcing = modeldb%config%external_forcing%theta_forcing()
wind_forcing = modeldb%config%external_forcing%wind_forcing()
+ nudging_mesh_name = modeldb%config%multires_coupling%nudging_mesh_name()
if ( theta_forcing == theta_forcing_nudging &
.or. wind_forcing == wind_forcing_nudging) then
@@ -476,6 +481,9 @@ subroutine step( modeldb )
call update_variable_fields( &
model_axes%nudging_times_list, modeldb%clock, derived_fields &
)
+
+ nudging_mesh => mesh_collection%get_mesh(trim(adjustl(nudging_mesh_name)))
+ call update_nudging_weights(modeldb%clock, modeldb%config, nudging_mesh)
end if
end if
diff --git a/science/gungho/source/kernel/external_forcing/convolution_2d_kernel_mod.F90 b/science/gungho/source/kernel/external_forcing/convolution_2d_kernel_mod.F90
new file mode 100644
index 0000000000..8f6338087b
--- /dev/null
+++ b/science/gungho/source/kernel/external_forcing/convolution_2d_kernel_mod.F90
@@ -0,0 +1,110 @@
+!-----------------------------------------------------------------------------
+! (C) Crown copyright Met Office. All rights reserved.
+! The file LICENCE, distributed with this code, contains details of the terms
+! under which the code may be used.
+!-----------------------------------------------------------------------------
+!> @brief Computes weights for convolution for spectral nudging.
+!> @details This kernel computes weights for performing a convolution that is
+!! used for nudging the spectrum of a field towards the spectrum of
+!! a reference field.
+!! Only implemented for the lowest-order elements
+module convolution_2d_kernel_mod
+
+ use argument_mod, only: arg_type, &
+ GH_FIELD, &
+ GH_REAL, GH_READ, GH_WRITE, &
+ CELL_COLUMN, STENCIL, REGION, &
+ ANY_DISCONTINUOUS_SPACE_1, &
+ ANY_DISCONTINUOUS_SPACE_9
+
+ use constants_mod, only: r_def, i_def
+ use kernel_mod, only: kernel_type
+
+ implicit none
+
+ private
+
+ !-----------------------------------------------------------------------------
+ ! Public types
+ !-----------------------------------------------------------------------------
+ !> The type declaration for the kernel.
+ !> Contains the metadata needed by the Psy layer
+ type, public, extends(kernel_type) :: convolution_2d_kernel_type
+ private
+ type(arg_type) :: meta_args(3) = (/ &
+ arg_type(GH_FIELD, GH_REAL, GH_WRITE, ANY_DISCONTINUOUS_SPACE_1), &
+ arg_type(GH_FIELD, GH_REAL, GH_READ, ANY_DISCONTINUOUS_SPACE_1, &
+ STENCIL(REGION)), &
+ arg_type(GH_FIELD, GH_REAL, GH_READ, ANY_DISCONTINUOUS_SPACE_9) &
+ /)
+ integer :: operates_on = CELL_COLUMN
+ contains
+ procedure, nopass :: convolution_2d_code
+ end type
+
+ !-----------------------------------------------------------------------------
+ ! Contained functions/subroutines
+ !-----------------------------------------------------------------------------
+ public :: convolution_2d_code
+
+contains
+
+!> @brief Computes weights for performing convolution for spectral nudging.
+!> @param[in] nlayers Number of layers
+!> @param[in,out] filtered_field The filtered field to compute
+!> @param[in] field_in Field to be filtered
+!> @param[in] stencil_size Size of the stencil used in the convolution
+!> @param[in] stencil_map Map of the stencil used in the convolution
+!> @param[in] weights Weights for performing the convolution
+!> @param[in] ndf_3d Number of DoFs per cell in 3D field
+!> @param[in] undf_3d Number of DoFs in this partition in 3D field
+!> @param[in] map_3d DoF map for the 3D field
+!> @param[in] ndf_2d Num of DoFs per cell in weights field
+!> @param[in] undf_2d Num of DoFs in this partition in weights field
+!> @param[in] map_2d DoF map for the weights field
+subroutine convolution_2d_code(nlayers, &
+ filtered_field, &
+ field_in, &
+ stencil_size, &
+ stencil_map, &
+ weights, &
+ ndf_3d, &
+ undf_3d, &
+ map_3d, &
+ ndf_2d, &
+ undf_2d, &
+ map_2d)
+
+ implicit none
+
+ ! Arguments
+ integer(kind=i_def), intent(in) :: nlayers
+ integer(kind=i_def), intent(in) :: ndf_2d, ndf_3d
+ integer(kind=i_def), intent(in) :: undf_2d, undf_3d
+ integer(kind=i_def), intent(in) :: stencil_size
+ integer(kind=i_def), intent(in) :: map_2d(ndf_2d)
+ integer(kind=i_def), intent(in) :: map_3d(ndf_3d)
+ integer(kind=i_def), intent(in) :: stencil_map(ndf_3d, stencil_size)
+ real(kind=r_def), intent(in) :: weights(undf_2d)
+ real(kind=r_def), intent(in) :: field_in(undf_3d)
+ real(kind=r_def), intent(inout) :: filtered_field(undf_3d)
+
+ ! Local variables
+ integer(kind=i_def) :: i, idx_w, idx_s, idx_f, nl
+
+ nl = nlayers - 2 + ndf_3d ! nlayers for Wtheta, nlayers - 1 for W3
+ idx_f = map_3d(1)
+
+ do i = 1, stencil_size
+ idx_w = map_2d(1) + i - 1 ! Multidata index
+ idx_s = stencil_map(1, i) ! Stencil index
+
+ filtered_field(idx_f:idx_f+nl) = ( &
+ filtered_field(idx_f:idx_f+nl) &
+ + weights(idx_w) * field_in(idx_s:idx_s+nl) &
+ )
+ end do
+
+end subroutine convolution_2d_code
+
+end module convolution_2d_kernel_mod
diff --git a/science/gungho/source/kernel/external_forcing/convolution_2d_weights_kernel_mod.F90 b/science/gungho/source/kernel/external_forcing/convolution_2d_weights_kernel_mod.F90
new file mode 100644
index 0000000000..6ce081fb3e
--- /dev/null
+++ b/science/gungho/source/kernel/external_forcing/convolution_2d_weights_kernel_mod.F90
@@ -0,0 +1,263 @@
+!-----------------------------------------------------------------------------
+! (C) Crown copyright Met Office. All rights reserved.
+! The file LICENCE, distributed with this code, contains details of the terms
+! under which the code may be used.
+!-----------------------------------------------------------------------------
+!> @brief Computes weights for convolution for spectral nudging.
+!> @details This kernel computes weights for performing a convolution that is
+!! used for nudging the spectrum of a field towards the spectrum of
+!! a reference field.
+!! Only implemented for the lowest-order elements
+module convolution_2d_weights_kernel_mod
+
+ use argument_mod, only: arg_type, func_type, &
+ GH_FIELD, GH_SCALAR, &
+ GH_REAL, GH_INTEGER, &
+ GH_READ, GH_WRITE, GH_BASIS, &
+ CELL_COLUMN, STENCIL, REGION, &
+ GH_EVALUATOR, &
+ ANY_DISCONTINUOUS_SPACE_3, &
+ ANY_DISCONTINUOUS_SPACE_7, &
+ ANY_DISCONTINUOUS_SPACE_9
+ use constants_mod, only: r_def, i_def, PI, EPS
+ use coord_transform_mod, only: central_angle
+ use kernel_mod, only: kernel_type
+ use sci_chi_transform_mod, only: chi2llr
+
+
+ implicit none
+
+ private
+
+ !-----------------------------------------------------------------------------
+ ! Public types
+ !-----------------------------------------------------------------------------
+ !> The type declaration for the kernel.
+ !> Contains the metadata needed by the Psy layer
+ type, public, extends(kernel_type) :: convolution_2d_weights_kernel_type
+ private
+ type(arg_type) :: meta_args(9) = (/ &
+ arg_type(GH_FIELD, GH_REAL, GH_WRITE, ANY_DISCONTINUOUS_SPACE_9), &
+ arg_type(GH_FIELD*3, GH_REAL, GH_READ, ANY_DISCONTINUOUS_SPACE_7, &
+ STENCIL(REGION)), &
+ arg_type(GH_FIELD, GH_REAL, GH_READ, ANY_DISCONTINUOUS_SPACE_3, &
+ STENCIL(REGION)), &
+ arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
+ arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
+ arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
+ arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
+ arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
+ arg_type(GH_SCALAR, GH_REAL, GH_READ) &
+ /)
+ type(func_type) :: meta_funcs(1) = (/ &
+ func_type(ANY_DISCONTINUOUS_SPACE_7, GH_BASIS) &
+ /)
+ integer :: operates_on = CELL_COLUMN
+ integer :: gh_shape = GH_EVALUATOR
+ contains
+ procedure, nopass :: convolution_2d_weights_code
+ end type
+
+ !-----------------------------------------------------------------------------
+ ! Contained functions/subroutines
+ !-----------------------------------------------------------------------------
+ public :: convolution_2d_weights_code
+
+contains
+
+!> @brief Computes weights for performing convolution for spectral nudging.
+!> @param[in] nlayers Number of layers
+!> @param[in,out] weights Weights to be computed: a multidata 2D field
+!> @param[in] chi1 First coordinate field
+!> @param[in] chi2 Second coordinate field
+!> @param[in] chi3 Third coordinate field
+!> @param[in] stencil_size_chi Size of the stencil for the chi fields
+!> @param[in] stencil_map_chi Stencil map for the coordinate fields
+!> @param[in] panel_id Panel ID field
+!> @param[in] stencil_size_pid Size of the stencil for the panel ID
+!> @param[in] stencil_map_pid Stencil map for the panel ID field
+!> @param[in] kmax Maximum wavenumber in filter
+!> @param[in] kmin Minimum wavenumber in filter
+!> @param[in] geometry Geometry of the mesh
+!> @param[in] topology Topology of the mesh
+!> @param[in] coord_system System used by coordinate fields
+!> @param[in] scaled_radius Scaled radius of the planet
+!> @param[in] ndf_2d Num DoFs per cell for weights field
+!> @param[in] undf_2d Num DoFs in this partition for weights field
+!> @param[in] map_2d DoFmap for weights field
+!> @param[in] ndf_chi Num DoFs per cell for coordinate fields
+!> @param[in] undf_chi Num DoFs in this partition for chi fields
+!> @param[in] map_chi DoFmap for coordinate fields
+!> @param[in] basis_chi Basis funcs for chi fields evaluated at W3
+!> @param[in] ndf_pid Num DoFs per cell for panel ID
+!> @param[in] undf_pid Num DoFs in this partition for panel ID
+!> @param[in] map_pid DoFmap for panel ID field
+subroutine convolution_2d_weights_code(nlayers, &
+ weights, &
+ chi1, &
+ chi2, &
+ chi3, &
+ stencil_size_chi, &
+ stencil_map_chi, &
+ panel_id, &
+ stencil_size_pid, &
+ stencil_map_pid, &
+ kmax, &
+ kmin, &
+ geometry, &
+ topology, &
+ coord_system, &
+ scaled_radius, &
+ ndf_2d, &
+ undf_2d, &
+ map_2d, &
+ ndf_chi, &
+ undf_chi, &
+ map_chi, &
+ basis_chi, &
+ ndf_pid, &
+ undf_pid, &
+ map_pid)
+
+ implicit none
+
+ ! Arguments
+ integer(kind=i_def), intent(in) :: nlayers
+ integer(kind=i_def), intent(in) :: ndf_2d, ndf_chi, ndf_pid
+ integer(kind=i_def), intent(in) :: undf_2d, undf_chi, undf_pid
+ integer(kind=i_def), intent(in) :: stencil_size_chi, stencil_size_pid
+ integer(kind=i_def), intent(in) :: map_2d(ndf_2d)
+ integer(kind=i_def), intent(in) :: map_chi(ndf_chi)
+ integer(kind=i_def), intent(in) :: map_pid(ndf_pid)
+ integer(kind=i_def), intent(in) :: stencil_map_chi(ndf_chi, stencil_size_chi)
+ integer(kind=i_def), intent(in) :: stencil_map_pid(ndf_pid, stencil_size_pid)
+ real(kind=r_def), intent(in) :: basis_chi(1, ndf_chi, ndf_2d)
+ integer(kind=i_def), intent(in) :: kmax, kmin
+ integer(kind=i_def), intent(in) :: geometry, topology, coord_system
+ real(kind=r_def), intent(in) :: scaled_radius
+ real(kind=r_def), intent(in) :: panel_id(undf_pid)
+ real(kind=r_def), intent(in) :: chi1(undf_chi)
+ real(kind=r_def), intent(in) :: chi2(undf_chi)
+ real(kind=r_def), intent(in) :: chi3(undf_chi)
+ real(kind=r_def), intent(inout) :: weights(undf_2d)
+
+ ! Internal variables
+ integer(kind=i_def) :: i, l, df, idx_w
+ integer(kind=i_def) :: panel_c, panel_i
+ real(kind=r_def) :: chi1_c, chi2_c, chi3_c
+ real(kind=r_def) :: chi1_i, chi2_i, chi3_i
+ real(kind=r_def) :: lon_c, lat_c, lon_i, lat_i
+ real(kind=r_def) :: radius
+ real(kind=r_def) :: dist, sum_weights
+ real(kind=r_def) :: P_0, P_1, P_l, P_lm1, P_lm2, l_real
+
+ ! Convolution is a 2D operation, so we can just act in the lowest layer
+ ! Get coordinates for the centre of this column
+ chi1_c = 0.0_r_def
+ chi2_c = 0.0_r_def
+ chi3_c = 0.0_r_def
+ do df = 1, ndf_chi
+ chi1_c = chi1_c + basis_chi(1, df, 1) * chi1(map_chi(df))
+ chi2_c = chi2_c + basis_chi(1, df, 1) * chi2(map_chi(df))
+ chi3_c = chi3_c + basis_chi(1, df, 1) * chi3(map_chi(df))
+ end do
+
+ ! Convert to longitude and latitude
+ panel_c = INT(panel_id(map_pid(1)), i_def)
+ call chi2llr( &
+ chi1_c, chi2_c, chi3_c, panel_c, &
+ geometry, topology, coord_system, scaled_radius, &
+ lon_c, lat_c, radius &
+ )
+
+ ! Loop through cells in the stencil
+ do i = 1, stencil_size_chi
+ ! Get coordinates for this cell
+ chi1_i = 0.0_r_def
+ chi2_i = 0.0_r_def
+ chi3_i = 0.0_r_def
+ do df = 1, ndf_chi
+ chi1_i = chi1_i + basis_chi(1, df, 1) * chi1(stencil_map_chi(df, i))
+ chi2_i = chi2_i + basis_chi(1, df, 1) * chi2(stencil_map_chi(df, i))
+ chi3_i = chi3_i + basis_chi(1, df, 1) * chi3(stencil_map_chi(df, i))
+ end do
+
+ panel_i = INT(panel_id(stencil_map_pid(1, i)), i_def)
+ call chi2llr( &
+ chi1_i, chi2_i, chi3_i, panel_i, &
+ geometry, topology, coord_system, scaled_radius, &
+ lon_i, lat_i, radius &
+ )
+
+ ! Use central angle to get distance on sphere
+ if (ABS(lon_c - lon_i) < 10.0_r_def*EPSILON(1.0_r_def) &
+ .and. ABS(lat_c - lat_i) < 10.0_r_def*EPSILON(1.0_r_def)) then
+ dist = 0.0_r_def
+ else
+ ! Only call this for points that are not identical, which gets around
+ ! potential issues in the central angle routine
+ call central_angle(lon_c, lat_c, lon_i, lat_i, dist)
+ end if
+
+ ! Convert distance to cos(dist) for use in spherical harmonics
+ dist = COS(dist)
+
+ ! Set weight for this cell in the stencil
+ idx_w = map_2d(1) + i - 1 ! Multidata index
+
+ ! Convolving function: enveloped Legendre polynomial -----------------------
+ weights(idx_w) = 0.0_r_def
+
+ ! First Legendre polynomials
+ P_0 = 1.0_r_def
+ P_1 = dist
+
+ if (kmin == 0) then
+ weights(idx_w) = weights(idx_w) + 1.0_r_def/(4.0_r_def*PI) * P_0
+ end if
+ if (kmin <= 1 .and. kmax >= 1) then
+ weights(idx_w) = weights(idx_w) + 3.0_r_def/(4.0_r_def*PI) * P_1
+ end if
+
+ ! Obtain further Legendre polynomials using recurrence relation
+ P_lm2 = P_0
+ P_lm1 = P_1
+ if (kmax >= 2) then
+ do l = 2, kmax
+ l_real = REAL(l, r_def)
+ ! Determine next Legendre polynomial P_l
+ P_l = ( &
+ (2.0_r_def*l_real - 1.0_r_def) * dist * P_lm1 &
+ - (l_real - 1.0_r_def) * P_lm2 &
+ ) / l_real
+ ! Update convolution weight based on Legendre polynomial:
+ ! (2l+1)/(4pi)) P_l
+ weights(idx_w) = weights(idx_w) + ( &
+ (2.0_r_def*l_real + 1.0_r_def ) / (4.0_r_def*PI) * P_l &
+ )
+ ! Update previous two polynomials
+ P_lm2 = P_lm1
+ P_lm1 = P_l
+ end do
+ end if
+
+ ! Apply envelope to weights, to ensure they go smoothly to zero at the
+ ! edge of the stencil, which can help avoid amplifying some wavenumbers
+ weights(idx_w) = weights(idx_w) * EXP( &
+ -0.5_r_def * (dist / ( &
+ 2.0_r_def*PI/(1.0_r_def + REAL(kmax, r_def)/3.0_r_def) &
+ ))**2 &
+ )
+ end do
+
+ ! Normalise the weights
+ idx_w = map_2d(1)
+ sum_weights = SUM(weights(idx_w : idx_w+stencil_size_chi-1))
+ if (ABS(sum_weights) > 0.0_r_def) then
+ weights(idx_w : idx_w+stencil_size_chi-1) = &
+ weights(idx_w : idx_w+stencil_size_chi-1) / sum_weights
+ end if
+
+end subroutine convolution_2d_weights_code
+
+end module convolution_2d_weights_kernel_mod
diff --git a/science/gungho/source/kernel/external_forcing/ecmwf_level_support_mod.F90 b/science/gungho/source/kernel/external_forcing/ecmwf_level_support_mod.F90
new file mode 100644
index 0000000000..699fe7010a
--- /dev/null
+++ b/science/gungho/source/kernel/external_forcing/ecmwf_level_support_mod.F90
@@ -0,0 +1,163 @@
+!-------------------------------------------------------------------------------
+! (c) Crown copyright Met Office. All rights reserved.
+! The file LICENCE, distributed with this code, contains details of the terms
+! under which the code may be used.
+!-------------------------------------------------------------------------------
+!> @brief Contains coefficients used for vertically-interpolating from ECMWF
+!! ERA or AIFS levels
+module ecmwf_level_support_mod
+
+ use constants_mod, only: r_def
+
+ implicit none
+
+ ! Height coefficients for ECMWF 137 level model
+ ! ***************************************************************
+ ! Values corresponding to ECMWF ERA-5 based 137-level dataset
+ ! **************************************************************
+
+ ! A(k) parameters for ECMWF half levels
+ real(kind=r_def), parameter :: ak_ec_137(0:137) = [ 0.000000_r_def, &
+ 2.000365_r_def, 3.102241_r_def, 4.666084_r_def, 6.827977_r_def, 9.746966_r_def, &
+ 13.605424_r_def, 18.608931_r_def, 24.985718_r_def, 32.985710_r_def, 42.879242_r_def, &
+ 54.955463_r_def, 69.520576_r_def, 86.895882_r_def, 107.415741_r_def, 131.425507_r_def, &
+ ! 16
+ 159.279404_r_def, 191.338562_r_def, 227.968948_r_def, 269.539581_r_def, 316.420746_r_def, &
+ 368.982361_r_def, 427.592499_r_def, 492.616028_r_def, 564.413452_r_def, 643.339905_r_def, &
+ 729.744141_r_def, 823.967834_r_def, 926.344910_r_def, 1037.201172_r_def, 1156.853638_r_def, &
+ ! 31
+ 1285.610352_r_def, 1423.770142_r_def, 1571.622925_r_def, 1729.448975_r_def, 1897.519287_r_def, &
+ 2076.095947_r_def, 2265.431641_r_def, 2465.770508_r_def, 2677.348145_r_def, 2900.391357_r_def, &
+ 3135.119385_r_def, 3381.743652_r_def, 3640.468262_r_def, 3911.490479_r_def, 4194.930664_r_def, &
+ ! 46
+ 4490.817383_r_def, 4799.149414_r_def, 5119.895020_r_def, 5452.990723_r_def, 5798.344727_r_def, &
+ 6156.074219_r_def, 6526.946777_r_def, 6911.870605_r_def, 7311.869141_r_def, 7727.412109_r_def, &
+ 8159.354004_r_def, 8608.525391_r_def, 9076.400391_r_def, 9562.682617_r_def, 10065.978516_r_def, &
+ ! 61
+ 10584.631836_r_def, 11116.662109_r_def, 11660.067383_r_def, 12211.547852_r_def, 12766.873047_r_def, &
+ 13324.668945_r_def, 13881.331055_r_def, 14432.139648_r_def, 14975.615234_r_def, 15508.256836_r_def, &
+ 16026.115234_r_def, 16527.322266_r_def, 17008.789063_r_def, 17467.613281_r_def, 17901.621094_r_def, &
+ ! 76
+ 18308.433594_r_def, 18685.718750_r_def, 19031.289063_r_def, 19343.511719_r_def, 19620.042969_r_def, &
+ 19859.390625_r_def, 20059.931641_r_def, 20219.664063_r_def, 20337.863281_r_def, 20412.308594_r_def, &
+ 20442.078125_r_def, 20425.718750_r_def, 20361.816406_r_def, 20249.511719_r_def, 20087.085938_r_def, &
+ ! 91
+ 19874.025391_r_def, 19608.572266_r_def, 19290.226563_r_def, 18917.460938_r_def, 18489.707031_r_def, &
+ 18006.925781_r_def, 17471.839844_r_def, 16888.687500_r_def, 16262.046875_r_def, 15596.695313_r_def, &
+ 14898.453125_r_def, 14173.324219_r_def, 13427.769531_r_def, 12668.257813_r_def, 11901.339844_r_def, &
+ ! 106
+ 11133.304688_r_def, 10370.175781_r_def, 9617.515625_r_def, 8880.453125_r_def, 8163.375000_r_def, &
+ 7470.343750_r_def, 6804.421875_r_def, 6168.531250_r_def, 5564.382813_r_def, 4993.796875_r_def, &
+ 4457.375000_r_def, 3955.960938_r_def, 3489.234375_r_def, 3057.265625_r_def, 2659.140625_r_def, &
+ ! 121
+ 2294.242188_r_def, 1961.500000_r_def, 1659.476563_r_def, 1387.546875_r_def, 1143.250000_r_def, &
+ 926.507813_r_def, 734.992188_r_def, 568.062500_r_def, 424.414063_r_def, 302.476563_r_def, &
+ 202.484375_r_def, 122.101563_r_def, 62.781250_r_def, 22.835938_r_def, 3.757813_r_def, &
+ ! 136
+ 0.000000_r_def, 0.000000_r_def ]
+
+ ! B(k) parameters for ECMWF half levels
+ real(kind=r_def), parameter :: bk_ec_137(0:137) = [ &
+ 0.000000_r_def, &
+ 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, &
+ 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, &
+ 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, &
+ ! 16
+ 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, &
+ 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, &
+ 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, &
+ ! 31
+ 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, &
+ 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, &
+ 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, &
+ ! 46
+ 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, &
+ 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000007_r_def, &
+ 0.000024_r_def, 0.000059_r_def, 0.000112_r_def, 0.000199_r_def, 0.000340_r_def, &
+ ! 61
+ 0.000562_r_def, 0.000890_r_def, 0.001353_r_def, 0.001992_r_def, 0.002857_r_def, &
+ 0.003971_r_def, 0.005378_r_def, 0.007133_r_def, 0.009261_r_def, 0.011806_r_def, &
+ 0.014816_r_def, 0.018318_r_def, 0.022355_r_def, 0.026964_r_def, 0.032176_r_def, &
+ ! 76
+ 0.038026_r_def, 0.044548_r_def, 0.051773_r_def, 0.059728_r_def, 0.068448_r_def, &
+ 0.077958_r_def, 0.088286_r_def, 0.099462_r_def, 0.111505_r_def, 0.124448_r_def, &
+ 0.138313_r_def, 0.153125_r_def, 0.168910_r_def, 0.185689_r_def, 0.203491_r_def, &
+ ! 91
+ 0.222333_r_def, 0.242244_r_def, 0.263242_r_def, 0.285354_r_def, 0.308598_r_def, &
+ 0.332939_r_def, 0.358254_r_def, 0.384363_r_def, 0.411125_r_def, 0.438391_r_def, &
+ 0.466003_r_def, 0.493800_r_def, 0.521619_r_def, 0.549301_r_def, 0.576692_r_def, &
+ ! 106
+ 0.603648_r_def, 0.630036_r_def, 0.655736_r_def, 0.680643_r_def, 0.704669_r_def, &
+ 0.727739_r_def, 0.749797_r_def, 0.770798_r_def, 0.790717_r_def, 0.809536_r_def, &
+ 0.827256_r_def, 0.843881_r_def, 0.859432_r_def, 0.873929_r_def, 0.887408_r_def, &
+ ! 121
+ 0.899900_r_def, 0.911448_r_def, 0.922096_r_def, 0.931881_r_def, 0.940860_r_def, &
+ 0.949064_r_def, 0.956550_r_def, 0.963352_r_def, 0.969513_r_def, 0.975078_r_def, &
+ 0.980072_r_def, 0.984542_r_def, 0.988500_r_def, 0.991984_r_def, 0.995003_r_def, &
+ ! 136
+ 0.997630_r_def, 1.000000_r_def ]
+
+ ! Height coefficients for ECMWF AIFS 88 level data
+ ! ***************************************************************
+ ! Values corresponding to ECMWF ERA-5 based 137-level dataset
+ ! From level 137 to 50, i.e. the lowest ~20km
+ ! **************************************************************
+ ! A(k) parameters for ECMWF half levels
+ real(kind=r_def), parameter :: ak_ec_137_50(0:88) = [ &
+ ! 49
+ 5452.990723_r_def, 5798.344727_r_def, &
+ 6156.074219_r_def, 6526.946777_r_def, 6911.870605_r_def, 7311.869141_r_def, 7727.412109_r_def, &
+ 8159.354004_r_def, 8608.525391_r_def, 9076.400391_r_def, 9562.682617_r_def, 10065.978516_r_def, &
+ ! 61
+ 10584.631836_r_def, 11116.662109_r_def, 11660.067383_r_def, 12211.547852_r_def, 12766.873047_r_def, &
+ 13324.668945_r_def, 13881.331055_r_def, 14432.139648_r_def, 14975.615234_r_def, 15508.256836_r_def, &
+ 16026.115234_r_def, 16527.322266_r_def, 17008.789063_r_def, 17467.613281_r_def, 17901.621094_r_def, &
+ ! 76
+ 18308.433594_r_def, 18685.718750_r_def, 19031.289063_r_def, 19343.511719_r_def, 19620.042969_r_def, &
+ 19859.390625_r_def, 20059.931641_r_def, 20219.664063_r_def, 20337.863281_r_def, 20412.308594_r_def, &
+ 20442.078125_r_def, 20425.718750_r_def, 20361.816406_r_def, 20249.511719_r_def, 20087.085938_r_def, &
+ ! 91
+ 19874.025391_r_def, 19608.572266_r_def, 19290.226563_r_def, 18917.460938_r_def, 18489.707031_r_def, &
+ 18006.925781_r_def, 17471.839844_r_def, 16888.687500_r_def, 16262.046875_r_def, 15596.695313_r_def, &
+ 14898.453125_r_def, 14173.324219_r_def, 13427.769531_r_def, 12668.257813_r_def, 11901.339844_r_def, &
+ ! 106
+ 11133.304688_r_def, 10370.175781_r_def, 9617.515625_r_def, 8880.453125_r_def, 8163.375000_r_def, &
+ 7470.343750_r_def, 6804.421875_r_def, 6168.531250_r_def, 5564.382813_r_def, 4993.796875_r_def, &
+ 4457.375000_r_def, 3955.960938_r_def, 3489.234375_r_def, 3057.265625_r_def, 2659.140625_r_def, &
+ ! 121
+ 2294.242188_r_def, 1961.500000_r_def, 1659.476563_r_def, 1387.546875_r_def, 1143.250000_r_def, &
+ 926.507813_r_def, 734.992188_r_def, 568.062500_r_def, 424.414063_r_def, 302.476563_r_def, &
+ 202.484375_r_def, 122.101563_r_def, 62.781250_r_def, 22.835938_r_def, 3.757813_r_def, &
+ ! 136
+ 0.000000_r_def, 0.000000_r_def ]
+
+ ! B(k) parameters for ECMWF half levels
+ real(kind=r_def), parameter :: bk_ec_137_50(0:88) = [ &
+ ! 46
+ 0.000000_r_def, 0.000000_r_def, &
+ 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000000_r_def, 0.000007_r_def, &
+ 0.000024_r_def, 0.000059_r_def, 0.000112_r_def, 0.000199_r_def, 0.000340_r_def, &
+ ! 61
+ 0.000562_r_def, 0.000890_r_def, 0.001353_r_def, 0.001992_r_def, 0.002857_r_def, &
+ 0.003971_r_def, 0.005378_r_def, 0.007133_r_def, 0.009261_r_def, 0.011806_r_def, &
+ 0.014816_r_def, 0.018318_r_def, 0.022355_r_def, 0.026964_r_def, 0.032176_r_def, &
+ ! 76
+ 0.038026_r_def, 0.044548_r_def, 0.051773_r_def, 0.059728_r_def, 0.068448_r_def, &
+ 0.077958_r_def, 0.088286_r_def, 0.099462_r_def, 0.111505_r_def, 0.124448_r_def, &
+ 0.138313_r_def, 0.153125_r_def, 0.168910_r_def, 0.185689_r_def, 0.203491_r_def, &
+ ! 91
+ 0.222333_r_def, 0.242244_r_def, 0.263242_r_def, 0.285354_r_def, 0.308598_r_def, &
+ 0.332939_r_def, 0.358254_r_def, 0.384363_r_def, 0.411125_r_def, 0.438391_r_def, &
+ 0.466003_r_def, 0.493800_r_def, 0.521619_r_def, 0.549301_r_def, 0.576692_r_def, &
+ ! 106
+ 0.603648_r_def, 0.630036_r_def, 0.655736_r_def, 0.680643_r_def, 0.704669_r_def, &
+ 0.727739_r_def, 0.749797_r_def, 0.770798_r_def, 0.790717_r_def, 0.809536_r_def, &
+ 0.827256_r_def, 0.843881_r_def, 0.859432_r_def, 0.873929_r_def, 0.887408_r_def, &
+ ! 121
+ 0.899900_r_def, 0.911448_r_def, 0.922096_r_def, 0.931881_r_def, 0.940860_r_def, &
+ 0.949064_r_def, 0.956550_r_def, 0.963352_r_def, 0.969513_r_def, 0.975078_r_def, &
+ 0.980072_r_def, 0.984542_r_def, 0.988500_r_def, 0.991984_r_def, 0.995003_r_def, &
+ ! 136
+ 0.997630_r_def, 1.000000_r_def ]
+
+end module ecmwf_level_support_mod
diff --git a/science/gungho/source/kernel/external_forcing/nudging_vert_interp_kernel_mod.F90 b/science/gungho/source/kernel/external_forcing/nudging_vert_interp_kernel_mod.F90
new file mode 100644
index 0000000000..fd98c0fa79
--- /dev/null
+++ b/science/gungho/source/kernel/external_forcing/nudging_vert_interp_kernel_mod.F90
@@ -0,0 +1,264 @@
+!-------------------------------------------------------------------------------
+! (c) Crown copyright Met Office. All rights reserved.
+! The file LICENCE, distributed with this code, contains details of the terms
+! under which the code may be used.
+!-------------------------------------------------------------------------------
+!> @brief Performs vertical remapping of nudging reference fields
+module nudging_vert_interp_kernel_mod
+
+ use argument_mod, only: arg_type, &
+ GH_FIELD, GH_SCALAR, &
+ GH_REAL, GH_INTEGER, &
+ GH_WRITE, GH_READ, &
+ ANY_DISCONTINUOUS_SPACE_1, &
+ ANY_DISCONTINUOUS_SPACE_2, &
+ CELL_COLUMN
+
+ use constants_mod, only: r_def, i_def, l_def
+ use fs_continuity_mod, only: Wtheta, W3
+ use kernel_mod, only: kernel_type
+
+ implicit none
+
+ private
+
+!-------------------------------------------------------------------------------
+! Public types
+!-------------------------------------------------------------------------------
+ type, public, extends(kernel_type) :: nudging_vert_interp_kernel_type
+ private
+ type(arg_type) :: meta_args(12) = (/ &
+ arg_type(GH_FIELD, GH_REAL, GH_WRITE, Wtheta), & ! Interpolated theta field
+ arg_type(GH_FIELD, GH_REAL, GH_WRITE, W3), & ! Interpolated U field
+ arg_type(GH_FIELD, GH_REAL, GH_WRITE, W3), & ! Interpolated V field
+ arg_type(GH_FIELD, GH_REAL, GH_READ, ANY_DISCONTINUOUS_SPACE_1), & ! Input T/theta field
+ arg_type(GH_FIELD, GH_REAL, GH_READ, ANY_DISCONTINUOUS_SPACE_1), & ! Input U field
+ arg_type(GH_FIELD, GH_REAL, GH_READ, ANY_DISCONTINUOUS_SPACE_1), & ! Input V field
+ arg_type(GH_FIELD, GH_REAL, GH_READ, Wtheta), & ! Exner on wtheta
+ arg_type(GH_FIELD, GH_REAL, GH_READ, W3), & ! Exner on w3
+ arg_type(GH_FIELD, GH_REAL, GH_READ, ANY_DISCONTINUOUS_SPACE_2), & ! Nudging pressure field
+ arg_type(GH_SCALAR, GH_REAL, GH_READ), & ! p_zero
+ arg_type(GH_SCALAR, GH_REAL, GH_READ), & ! kappa
+ arg_type(GH_SCALAR, GH_INTEGER, GH_READ) & ! nudge_levels
+ /)
+ integer :: operates_on = CELL_COLUMN
+contains
+ procedure, nopass :: nudging_vert_interp_code
+end type nudging_vert_interp_kernel_type
+
+!-------------------------------------------------------------------------------
+! Contained functions/subroutines
+!-------------------------------------------------------------------------------
+public :: nudging_vert_interp_code
+
+contains
+ !-----------------------------------------------------------------------------
+ ! Contained functions/subroutines
+ !-----------------------------------------------------------------------------
+ !> @brief Performs vertical remapping of nudging reference fields
+ !> @param[in] nlayers Number of layers in model mesh
+ !> @param[in,out] theta_mod_levs theta field in Wtheta
+ !> @param[in,out] u_mod_levs zonal wind at W3 on model levels
+ !> @param[in,out] v_mod_levs meridional wind at W3 on model levels
+ !> @param[in] T_nudge_levs input nudging T or theta field
+ !> @param[in] u_nudge_levs input nudging zonal wind
+ !> @param[in] v_nudge_levs input nudging v wind
+ !> @param[in] exner_in_wth exner pressure in Wtheta
+ !> @param[in] exner_in_w3 exner pressure in W3
+ !> @param[in] ln_surfp_nudge surface pressure from nudging data
+ !> @param[in] p_zero Reference pressure for Exner calculation
+ !> @param[in] kappa Rd/Cp, used in definition of Exner
+ !> @param[in] ndf_wth number of degrees of freedom per cell
+ !> @param[in] undf_wth num of DoFs in this partition for Wtheta
+ !> @param[in] map_wth index of DoFs for lowest cell for Wtheta
+ !> @param[in] ndf_w3 number of degrees of freedom per cell for W3
+ !> @param[in] undf_w3 num of DoFs in this partition for W3
+ !> @param[in] map_w3 index of DoFs for lowest cell for W3
+ !> @param[in] ndf_ec num DoFs per cell for input nudging fields
+ !> @param[in] undf_ec num DoFs in this partition for nudging field
+ !> @param[in] map_ec dofmap for nudging fields
+ !> @param[in] ndf_ec_2d num DoFs per cell for surface pressure
+ !> @param[in] undf_ec_2d num DoFs in this partition for surf pressure
+ !> @param[in] map_ec_2d dofmap for surf pressure field
+ subroutine nudging_vert_interp_code( &
+ nlayers, &
+ theta_mod_levs, u_mod_levs, v_mod_levs, &
+ T_nudge_levs, u_nudge_levs, v_nudge_levs, &
+ exner_in_wth, exner_in_w3, ln_surfp_nudge, &
+ p_zero, kappa, nudge_levels, &
+ ndf_wth, undf_wth, map_wth, &
+ ndf_w3, undf_w3, map_w3, &
+ ndf_ec, undf_ec, map_ec, &
+ ndf_ec2d, undf_ec2d, map_ec2d &
+ )
+
+ use ecmwf_level_support_mod, only: ak_ec_137, bk_ec_137, &
+ ak_ec_137_50, bk_ec_137_50
+
+ implicit none
+
+ ! Arguments
+ integer(kind=i_def), intent(in) :: nlayers
+ integer(kind=i_def), intent(in) :: ndf_wth, undf_wth
+ integer(kind=i_def), intent(in) :: map_wth(ndf_wth)
+ integer(kind=i_def), intent(in) :: ndf_w3, undf_w3
+ integer(kind=i_def), intent(in) :: map_w3(ndf_w3)
+ integer(kind=i_def), intent(in) :: ndf_ec, undf_ec
+ integer(kind=i_def), intent(in) :: map_ec(ndf_ec)
+ integer(kind=i_def), intent(in) :: ndf_ec2d, undf_ec2d
+ integer(kind=i_def), intent(in) :: map_ec2d(ndf_ec2d)
+ integer(kind=i_def), intent(in) :: nudge_levels
+
+ real(kind=r_def), intent(inout) :: theta_mod_levs(undf_wth)
+ real(kind=r_def), intent(inout) :: u_mod_levs(undf_w3)
+ real(kind=r_def), intent(inout) :: v_mod_levs(undf_w3)
+ real(kind=r_def), intent(inout) :: T_nudge_levs(undf_ec)
+ real(kind=r_def), intent(in) :: u_nudge_levs(undf_ec)
+ real(kind=r_def), intent(in) :: v_nudge_levs(undf_ec)
+ real(kind=r_def), intent(in) :: exner_in_wth(undf_wth)
+ real(kind=r_def), intent(in) :: exner_in_w3(undf_w3)
+ real(kind=r_def), intent(in) :: ln_surfp_nudge(undf_ec2d)
+ real(kind=r_def), intent(in) :: p_zero, kappa
+
+ ! Local variables
+ ! Pointers to height coefficients for the current dataset
+ real(kind=r_def) :: ak_ec(0:nudge_levels)
+ real(kind=r_def) :: bk_ec(0:nudge_levels)
+
+ ! Coefficients interpolated on full levels
+ real(kind=r_def), dimension(nudge_levels) :: ak_int, bk_int
+
+ ! Pressure profiles
+ real(kind=r_def), dimension(nudge_levels) :: p_nudge_levels
+ real(kind=r_def), dimension(nlayers) :: press_in_wth
+ real(kind=r_def), dimension(0:nlayers-1) :: press_in_w3
+
+ ! Interim copies of nudging variables - for inverting data
+ real(kind=r_def), dimension(nudge_levels) :: theta_nudge_int
+ real(kind=r_def), dimension(nudge_levels) :: u_nudge_int
+ real(kind=r_def), dimension(nudge_levels) :: v_nudge_int
+ real(kind=r_def) :: step_wth, step_w3, delta
+
+ ! Loop counters
+ integer(kind=i_def) :: ec_idx, wt_idx, w3_idx
+ integer(kind=i_def) :: j, k, l, m
+ integer(kind=i_def) :: kth, lth, mth
+
+ ! Set height parameters, based on the number of nudging levels
+ select case (nudge_levels)
+ case (137)
+ ak_ec(:) = ak_ec_137(:)
+ bk_ec(:) = bk_ec_137(:)
+ case (88)
+ ak_ec(:) = ak_ec_137_50(:)
+ bk_ec(:) = bk_ec_137_50(:)
+ end select
+
+ ec_idx = map_ec(1)
+ w3_idx = map_w3(1)
+ wt_idx = map_wth(1)
+
+ ! Contruct height parameters on full levels
+ do k = 1, nudge_levels
+
+ ! Convert the levels to the same order as model
+ ! (i.e start at the bottom of the atmosphere)
+ j = nudge_levels + 1 - k
+
+ ak_int(k) = 0.5_r_def*(ak_ec(j-1) + ak_ec(j))
+ bk_int(k) = 0.5_r_def*(bk_ec(j-1) + bk_ec(j))
+
+ ! construct pressure profile for nudging data
+ p_nudge_levels(k) = ak_int(k) &
+ + bk_int(k) * exp(ln_surfp_nudge( map_ec2d(1)))
+ end do
+
+ ! Construct pressure profiles on model levels
+ do k = 0, nlayers - 1
+ kth = k + 1
+ press_in_wth(kth) = p_zero * exner_in_wth(wt_idx+kth)**(1.0_r_def/kappa)
+ press_in_w3(k) = p_zero * exner_in_w3(w3_idx+k)**(1.0_r_def/kappa)
+ end do
+
+ ! Copy to interim arrays
+ ! Convert T to theta using the pressure profile generated here.
+ ! Input field on W3 mesh goes from 0 -> nudge_levels-1
+ do k = 0, nudge_levels - 1
+ theta_nudge_int(k+1) = &
+ T_nudge_levs(ec_idx+k) * (p_zero / p_nudge_levels(k+1))**kappa
+ u_nudge_int(k+1) = u_nudge_levs(ec_idx+k)
+ v_nudge_int(k+1) = v_nudge_levs(ec_idx+k)
+ end do
+
+ ! Now interpolate from nudging levels to model levels
+ ! Initialise the data loop
+ l = 1
+ m = l + 1
+ lth = 1
+ mth = lth + 1
+
+ ! Increment L until Nudging Pressures straddle 1st model Pressure level
+ ! Separately for wth and w3 grids
+ do while(press_in_w3(1) < p_nudge_levels(m) .and. m < nudge_levels)
+ l = l + 1
+ m = l + 1
+ end do
+
+ do while (press_in_wth(1) < p_nudge_levels(mth) .and. mth < nudge_levels)
+ lth = lth + 1
+ mth = lth + 1
+ end do
+
+ ! Loop over model levels interpolating on to these levels
+ do k = 0, nlayers - 1
+ kth = k + 1
+
+ ! Calculate interpolation weights in log-pressure space
+ step_w3 = log(press_in_w3(k)) - log(p_nudge_levels(l))
+ step_w3 = step_w3/(log(p_nudge_levels(l+1)) - log(p_nudge_levels(l)))
+
+ step_wth = log(press_in_wth(kth)) - log(p_nudge_levels(lth))
+ step_wth = step_wth/(log(p_nudge_levels(lth+1)) - log(p_nudge_levels(lth)))
+
+ ! Limit large extrapolations - keep step between -1.0 <-> 2.0
+ step_w3 = max( min(step_w3, 2.0_r_def), -1.0_r_def )
+ step_wth = max( min(step_wth, 2.0_r_def), -1.0_r_def )
+
+ ! Interpolate the analyses on to the model levels
+ delta = u_nudge_int(m) - u_nudge_int(l)
+ u_mod_levs(w3_idx+k) = u_nudge_int( l ) + delta * step_w3
+
+ delta = v_nudge_int(m) - v_nudge_int(l)
+ v_mod_levs(w3_idx+k) = v_nudge_int(l) + delta * step_w3
+
+ delta = theta_nudge_int(mth) - theta_nudge_int(lth)
+ theta_mod_levs(wt_idx+kth) = theta_nudge_int(lth) + delta * step_wth
+
+ ! If the pressure of the next nudge_level is greater than the next
+ ! model level then increment to the nudge level
+ ! Also check that we have not exceeded the number of nudge or model
+ ! levels
+ if (kth < nlayers) then
+ mth = lth + 1
+ do while (press_in_wth(kth+1) < p_nudge_levels(mth) .and. mth < nudge_levels)
+ lth = lth + 1
+ mth = lth + 1
+ end do
+ end if
+
+ if (k < nlayers - 1) then
+ m = l + 1
+ do while (press_in_w3(k+1) < p_nudge_levels(m) .and. m < nudge_levels)
+ l = l + 1
+ m = l + 1
+ end do
+ end if
+
+ end do ! Loop over levels
+
+ ! Set bottom value of theta to the value of the next level up
+ theta_mod_levs(wt_idx) = theta_mod_levs(wt_idx+1)
+
+ end subroutine nudging_vert_interp_code
+
+end module nudging_vert_interp_kernel_mod
diff --git a/science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90 b/science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90
new file mode 100644
index 0000000000..e9a206cf29
--- /dev/null
+++ b/science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90
@@ -0,0 +1,118 @@
+!-----------------------------------------------------------------------------
+! (C) Crown copyright Met Office. All rights reserved.
+! The file LICENCE, distributed with this code, contains details of the terms
+! under which the code may be used.
+!-----------------------------------------------------------------------------
+!> @brief Computes weights for scaling the spectral nudging increment.
+!> @details This kernel computes the height-dependent weights used in spectral
+!! nudging, to allow the nudging to be targeted at certain vertical
+!! heights in the atmosphere.
+!! The weights are currently ramped linearly from 0 at the surface to
+!! 2km, and then held constant above this height
+!! Only implemented for the lowest-order elements
+module nudging_weights_kernel_mod
+
+ use argument_mod, only: arg_type, &
+ GH_FIELD, GH_SCALAR, &
+ GH_REAL, GH_INTEGER, &
+ GH_READ, GH_WRITE, &
+ CELL_COLUMN
+ use constants_mod, only: r_def, i_def
+ use fs_continuity_mod, only: W3
+ use kernel_mod, only: kernel_type
+
+ implicit none
+
+ private
+
+ !-----------------------------------------------------------------------------
+ ! Public types
+ !-----------------------------------------------------------------------------
+ !> The type declaration for the kernel.
+ !> Contains the metadata needed by the Psy layer
+ type, public, extends(kernel_type) :: nudging_weights_kernel_type
+ private
+ type(arg_type) :: meta_args(5) = (/ &
+ arg_type(GH_FIELD, GH_REAL, GH_WRITE, W3), &
+ arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
+ arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
+ arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
+ arg_type(GH_SCALAR, GH_INTEGER, GH_READ) &
+ /)
+ integer :: operates_on = CELL_COLUMN
+ contains
+ procedure, nopass :: nudging_weights_code
+ end type
+
+ !-----------------------------------------------------------------------------
+ ! Contained functions/subroutines
+ !-----------------------------------------------------------------------------
+ public :: nudging_weights_code
+
+contains
+
+!> @brief Computes weights for scaling the spectral nudging increment.
+!> @param[in] nlayers Number of layers
+!> @param[in,out] weights The weights to be computed
+!> @param[in] bottom_nudge_level Level below which nudging is turned off
+!> @param[in] top_nudge_level Level above which nudging is turned off
+!> @param[in] bottom_nudge_width Width of the ramp at the bottom
+!> @param[in] top_nudge_width Width of the ramp at the top
+!> @param[in] ndf Num DoFs per cell
+!> @param[in] undf Num DoFs in this partition
+!> @param[in] map DoFmap for this column
+subroutine nudging_weights_code(nlayers, &
+ weights, &
+ bottom_nudge_level, &
+ top_nudge_level, &
+ bottom_nudge_width, &
+ top_nudge_width, &
+ ndf, &
+ undf, &
+ map)
+
+ implicit none
+
+ ! Arguments
+ integer(kind=i_def), intent(in) :: nlayers
+ integer(kind=i_def), intent(in) :: ndf
+ integer(kind=i_def), intent(in) :: undf
+ integer(kind=i_def), intent(in) :: map(ndf)
+ integer(kind=i_def), intent(in) :: bottom_nudge_level
+ integer(kind=i_def), intent(in) :: top_nudge_level
+ integer(kind=i_def), intent(in) :: bottom_nudge_width
+ integer(kind=i_def), intent(in) :: top_nudge_width
+ real(kind=r_def), intent(inout) :: weights(undf)
+
+
+ ! Local variables
+ integer(kind=i_def) :: k, idx, nl
+
+ nl = nlayers - 2 + ndf ! nlayers for Wtheta, nlayers - 1 for W3
+ idx = map(1)
+
+ do k = 0, bottom_nudge_level-bottom_nudge_width-1
+ weights(idx + k) = 0.0_r_def
+ end do
+
+ do k = bottom_nudge_level-bottom_nudge_width, bottom_nudge_level+bottom_nudge_width
+ weights(idx + k) = real(k - (bottom_nudge_level - bottom_nudge_width), r_def) / &
+ real(2*bottom_nudge_width, r_def)
+ end do
+
+ do k = bottom_nudge_level+bottom_nudge_width+1, top_nudge_level-top_nudge_width-1
+ weights(idx + k) = 1.0_r_def
+ end do
+
+ do k = top_nudge_level-top_nudge_width, top_nudge_level+top_nudge_width
+ weights(idx + k) = 1.0_r_def - real(k - (top_nudge_level - top_nudge_width), r_def) / &
+ real(2*top_nudge_width, r_def)
+ end do
+
+ do k = top_nudge_level+top_nudge_width+1, nl
+ weights(idx + k) = 0.0_r_def
+ end do
+
+end subroutine nudging_weights_code
+
+end module nudging_weights_kernel_mod
diff --git a/science/gungho/unit-test/kernel/external_forcing/convolution_2d_kernel_mod_test.pf b/science/gungho/unit-test/kernel/external_forcing/convolution_2d_kernel_mod_test.pf
new file mode 100644
index 0000000000..69a3fd49e0
--- /dev/null
+++ b/science/gungho/unit-test/kernel/external_forcing/convolution_2d_kernel_mod_test.pf
@@ -0,0 +1,98 @@
+!-----------------------------------------------------------------------------
+! (C) Crown copyright Met Office. All rights reserved.
+! The file LICENCE, distributed with this code, contains details of the terms
+! under which the code may be used.
+!-------------------------------------------------------------------------------
+module convolution_2d_kernel_mod_test
+
+ use constants_mod, only : i_def, r_def, PI
+ use funit
+
+
+ implicit none
+
+ private
+ public :: test_all
+
+contains
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ @Test
+ subroutine test_all( )
+
+ use convolution_2d_kernel_mod, only: convolution_2d_code
+
+ implicit none
+
+ integer(kind=i_def), parameter :: nlayers = 1
+ integer(kind=i_def), parameter :: stencil_size = 25
+ integer(kind=i_def), parameter :: ndf_2d = 1
+ integer(kind=i_def), parameter :: undf_2d = stencil_size
+ integer(kind=i_def), parameter :: ndf_3d = 1 ! This simplifies the test!
+ integer(kind=i_def), parameter :: undf_3d = stencil_size
+ integer(kind=i_def), parameter :: kmax = 40
+ integer(kind=i_def), parameter :: kmin = 4
+ real(kind=r_def), parameter :: dx = 20.0_r_def
+ real(kind=r_def), parameter :: dy = 10.0_r_def
+
+ real(kind=r_def), parameter :: tol = 10.0_r_def*epsilon(1.0_r_def)
+
+ integer(kind=i_def) :: map_3d(ndf_3d)
+ integer(kind=i_def) :: map_2d(ndf_2d)
+ integer(kind=i_def) :: stencil_map(ndf_3d, stencil_size)
+ real(kind=r_def) :: answer
+ real(kind=r_def) :: filtered_field(undf_3d)
+ real(kind=r_def) :: field_in(undf_3d)
+ real(kind=r_def) :: weights(undf_2d)
+
+ integer(kind=i_def) :: i, j, ij
+ real(kind=r_def) :: dist, chi1, chi2
+
+ map_3d(1) = 13
+ map_2d(1) = 1
+
+ ! Populate maps / weights
+ ! Set up regular grid of coordinates
+ do i = 1, 5
+ do j = 1, 5
+ ij = (i-1)*5 + j
+ stencil_map(1, ij) = ij
+ ! Coordinates ----------------------------------------------------------
+ chi1 = dx*REAL(i, r_def)
+ chi2 = dy*REAL(j, r_def)
+ ! Weights --------------------------------------------------------------
+ dist = sqrt((chi1 - 3.0_r_def*dx)**2 + (chi2 - 3.0_r_def*dy)**2)
+ if (dist < 1.0e-3_r_def) then
+ weights(ij) = 2.0_r_def*REAL(kmax - kmin, r_def)
+ else
+ weights(ij) = ( &
+ SIN(2.0_r_def*PI*kmax*dist) / (PI*dist) &
+ - SIN(2.0_r_def*PI*kmin*dist) / (PI*dist) &
+ )
+ end if
+ ! Input field ----------------------------------------------------------
+ ! Make it linear in x
+ field_in(ij) = 100.0_r_def + 19.0_r_def*REAL(i, r_def)
+ end do
+ end do
+
+ ! Field is linear and convolution is symmetric, so answer should be central value
+ answer = field_in(13)
+
+ ! Normalise weights
+ weights(:) = weights(:) / SUM(weights(:))
+
+ ! Initialise output field to zero
+ filtered_field(:) = 0.0_r_def
+
+ call convolution_2d_code( &
+ nlayers, filtered_field, field_in, stencil_size, stencil_map, &
+ weights, ndf_3d, undf_3d, map_3d, ndf_2d, undf_2d, map_2d &
+ )
+
+ ! Only central value (13 out of 25) is set
+ @assertEqual(answer, filtered_field(13), tol)
+
+ end subroutine test_all
+
+end module convolution_2d_kernel_mod_test
diff --git a/science/gungho/unit-test/kernel/external_forcing/convolution_2d_weights_kernel_mod_test.pf b/science/gungho/unit-test/kernel/external_forcing/convolution_2d_weights_kernel_mod_test.pf
new file mode 100644
index 0000000000..102574c871
--- /dev/null
+++ b/science/gungho/unit-test/kernel/external_forcing/convolution_2d_weights_kernel_mod_test.pf
@@ -0,0 +1,158 @@
+!-----------------------------------------------------------------------------
+! (C) Crown copyright Met Office. All rights reserved.
+! The file LICENCE, distributed with this code, contains details of the terms
+! under which the code may be used.
+!-------------------------------------------------------------------------------
+module convolution_2d_weights_kernel_mod_test
+
+ use constants_mod, only : i_def, r_def, PI
+ use funit
+
+
+ implicit none
+
+ private
+ public :: test_all
+
+ @TestCase
+ type, extends(TestCase), public :: convolution_2d_weights_test_type
+ private
+ contains
+ procedure setUp
+ procedure tearDown
+ procedure test_all
+ end type convolution_2d_weights_test_type
+
+contains
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ subroutine setUp(this)
+
+ implicit none
+
+ class(convolution_2d_weights_test_type), intent(inout) :: this
+
+ end subroutine setUp
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ subroutine tearDown(this)
+
+ implicit none
+
+ class(convolution_2d_weights_test_type), intent(inout) :: this
+
+ end subroutine tearDown
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ @Test
+ subroutine test_all(this)
+
+ use convolution_2d_weights_kernel_mod, only: convolution_2d_weights_code
+ use coord_transform_mod, only: central_angle
+ use base_mesh_config_mod, only: geometry_spherical, &
+ topology_non_periodic
+ use finite_element_config_mod, only: coord_system_native
+
+ implicit none
+
+ class(convolution_2d_weights_test_type), intent(inout) :: this
+
+ integer(kind=i_def), parameter :: nlayers = 1
+ integer(kind=i_def), parameter :: stencil_size = 25
+ integer(kind=i_def), parameter :: ndf_2d = 1
+ integer(kind=i_def), parameter :: undf_2d = stencil_size
+ integer(kind=i_def), parameter :: ndf_chi = 1 ! This simplifies the test!
+ integer(kind=i_def), parameter :: undf_chi = stencil_size
+ integer(kind=i_def), parameter :: ndf_pid = 1
+ integer(kind=i_def), parameter :: undf_pid = stencil_size
+ integer(kind=i_def), parameter :: kmax = 4
+ integer(kind=i_def), parameter :: kmin = 2
+ real(kind=r_def), parameter :: lon_c = PI/6.0_r_def
+ real(kind=r_def), parameter :: lat_c = PI/12.0_r_def
+ real(kind=r_def), parameter :: dlon = PI/48.0_r_def
+ real(kind=r_def), parameter :: dlat = PI/96.0_r_def
+
+ real(kind=r_def), parameter :: tol = 10.0_r_def*epsilon(1.0_r_def)
+
+ integer(kind=i_def) :: map_chi(ndf_chi)
+ integer(kind=i_def) :: map_pid(ndf_pid)
+ integer(kind=i_def) :: map_2d(ndf_2d)
+ integer(kind=i_def) :: stencil_map_chi(ndf_chi, stencil_size)
+ integer(kind=i_def) :: stencil_map_pid(ndf_pid, stencil_size)
+ real(kind=r_def) :: answer(undf_2d)
+ real(kind=r_def) :: chi1(undf_chi), chi2(undf_chi), chi3(undf_chi)
+ real(kind=r_def) :: basis_chi(1, ndf_chi, ndf_2d)
+ real(kind=r_def) :: panel_id(undf_pid)
+ real(kind=r_def) :: weights(undf_2d)
+
+ integer(kind=i_def) :: geometry, topology, coord_system
+ real(kind=r_def) :: scaled_radius
+ integer(kind=i_def) :: i, j, ij
+ real(kind=r_def) :: dist
+
+ ! Map is just for central column
+ map_chi(1) = 13
+ map_pid(1) = 13
+ map_2d(1) = 1
+
+ panel_id(:) = 1.0_r_def
+ chi3(:) = 0.0_r_def
+ basis_chi(:,:,:) = 1.0_r_def ! Chi is at cell centres
+
+ geometry = geometry_spherical
+ topology = topology_non_periodic
+ coord_system = coord_system_native
+ scaled_radius = 1.0_r_def
+
+ ! Populate maps / coordinates / weights
+ ! Set up regular grid of coordinates
+ do i = 1, 5
+ do j = 1, 5
+ ij = (i-1)*5 + j
+ stencil_map_chi(1, ij) = ij
+ stencil_map_pid(1, ij) = ij
+ ! Coordinates ----------------------------------------------------------
+ chi1(ij) = lon_c + dlon*REAL(i-3, r_def)
+ chi2(ij) = lat_c + dlat*REAL(j-3, r_def)
+ ! Known answer ---------------------------------------------------------
+ call central_angle(lon_c, lat_c, chi1(ij), chi2(ij), dist)
+ dist = COS(dist)
+
+ ! The Legendre polynomials for degrees 2 to 4 are:
+ ! P_2(x) = 0.5*(3*x^2 - 1)
+ ! P_3(x) = 0.5*(5*x^3 - 3*x)
+ ! P_4(x) = (1/8)*(35*x^4 - 30*x^2 + 3)
+ ! For the convolution filter, each function is weighted by (2*l+1)
+ answer(ij) = ( &
+ 5.0_r_def*0.5_r_def*(3.0_r_def*dist**2 - 1.0_r_def) &
+ + 7.0_r_def*0.5_r_def*(5.0_r_def*dist**3 - 3.0_r_def*dist) &
+ + 9.0_r_def*(1.0_r_def/8.0_r_def)*( &
+ 35.0_r_def*dist**4 - 30.0_r_def*dist**2 + 3.0_r_def &
+ ) &
+ ) / (4.0_r_def*PI) * EXP( &
+ -0.5_r_def * (dist / ( &
+ 2.0_r_def*PI/(1.0_r_def + REAL(kmax, r_def)/3.0_r_def) &
+ ))**2 &
+ )
+ end do
+ end do
+
+ ! Normalise answer, since stencil is restricted in space
+ answer(:) = answer(:) / SUM(answer(:))
+
+ ! Initialise weights to zero
+ weights(:) = 0.0_r_def
+
+ call convolution_2d_weights_code( &
+ nlayers, weights, chi1, chi2, chi3, stencil_size, stencil_map_chi, &
+ panel_id, stencil_size, stencil_map_pid, kmax, kmin, &
+ geometry, topology, coord_system, scaled_radius, &
+ ndf_2d, undf_2d, map_2d, ndf_chi, undf_chi, map_chi, basis_chi, &
+ ndf_pid, undf_pid, map_pid &
+ )
+
+ @assertEqual(answer, weights, tol)
+
+ end subroutine test_all
+
+end module convolution_2d_weights_kernel_mod_test
diff --git a/science/gungho/unit-test/kernel/external_forcing/nudging_vert_interp_kernel_mod_test.pf b/science/gungho/unit-test/kernel/external_forcing/nudging_vert_interp_kernel_mod_test.pf
new file mode 100644
index 0000000000..4bae07d526
--- /dev/null
+++ b/science/gungho/unit-test/kernel/external_forcing/nudging_vert_interp_kernel_mod_test.pf
@@ -0,0 +1,146 @@
+!-----------------------------------------------------------------------------
+! (C) Crown copyright Met Office. All rights reserved.
+! 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
+! GitHub Copilot (Claude Sonnet 5).
+!-------------------------------------------------------------------------------
+module nudging_vert_interp_kernel_mod_test
+
+ use constants_mod, only : i_def, r_def
+ use funit
+
+ implicit none
+
+ private
+ public :: test_all
+
+contains
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ @Test
+ subroutine test_all( )
+
+ use nudging_vert_interp_kernel_mod, only: nudging_vert_interp_code
+ use ecmwf_level_support_mod, only: ak_ec_137, bk_ec_137
+
+ implicit none
+
+ integer(kind=i_def), parameter :: nlayers = 5
+ integer(kind=i_def), parameter :: nudge_levels = 137
+
+ integer(kind=i_def), parameter :: ndf_wth = 1
+ integer(kind=i_def), parameter :: undf_wth = nlayers + 1
+ integer(kind=i_def), parameter :: ndf_w3 = 1
+ integer(kind=i_def), parameter :: undf_w3 = nlayers
+ integer(kind=i_def), parameter :: ndf_ec = 1
+ integer(kind=i_def), parameter :: undf_ec = nudge_levels
+ integer(kind=i_def), parameter :: ndf_ec2d = 1
+ integer(kind=i_def), parameter :: undf_ec2d = 1
+
+ real(kind=r_def), parameter :: p_zero = 100000.0_r_def
+ real(kind=r_def), parameter :: kappa = 2.0_r_def/7.0_r_def
+ real(kind=r_def), parameter :: surfp_nudge = 101325.0_r_def
+ real(kind=r_def), parameter :: tol = 1.0e-6_r_def
+
+ integer(kind=i_def) :: map_wth(ndf_wth), map_w3(ndf_w3)
+ integer(kind=i_def) :: map_ec(ndf_ec), map_ec2d(ndf_ec2d)
+
+ real(kind=r_def) :: theta_mod_levs(undf_wth)
+ real(kind=r_def) :: u_mod_levs(undf_w3)
+ real(kind=r_def) :: v_mod_levs(undf_w3)
+ real(kind=r_def) :: T_nudge_levs(undf_ec)
+ real(kind=r_def) :: u_nudge_levs(undf_ec)
+ real(kind=r_def) :: v_nudge_levs(undf_ec)
+ real(kind=r_def) :: exner_in_wth(undf_wth)
+ real(kind=r_def) :: exner_in_w3(undf_w3)
+ real(kind=r_def) :: ln_surfp_nudge(undf_ec2d)
+
+ real(kind=r_def) :: ak_int, bk_int, p_nudge, theta_target
+ real(kind=r_def) :: press_in_w3(0:nlayers-1)
+ real(kind=r_def) :: press_in_wth(nlayers)
+
+ real(kind=r_def) :: theta_answer(undf_wth)
+ real(kind=r_def) :: u_answer(undf_w3)
+ real(kind=r_def) :: v_answer(undf_w3)
+
+ integer(kind=i_def) :: k, j
+
+ map_wth(1) = 1
+ map_w3(1) = 1
+ map_ec(1) = 1
+ map_ec2d(1) = 1
+
+ ln_surfp_nudge(1) = log(surfp_nudge)
+
+ ! Build synthetic nudging profiles for u, v and theta that vary linearly
+ ! with log(pressure), so that the kernel's log-pressure interpolation
+ ! reproduces them exactly (away from the top/bottom of the profile)
+ do k = 1, nudge_levels
+ j = nudge_levels + 1 - k
+ ak_int = 0.5_r_def*(ak_ec_137(j-1) + ak_ec_137(j))
+ bk_int = 0.5_r_def*(bk_ec_137(j-1) + bk_ec_137(j))
+ p_nudge = ak_int + bk_int*surfp_nudge
+
+ u_nudge_levs(k) = 5.0_r_def*log(p_nudge) - 20.0_r_def
+ v_nudge_levs(k) = -2.0_r_def*log(p_nudge) + 15.0_r_def
+ theta_target = 500.0_r_def - 15.0_r_def*log(p_nudge)
+ T_nudge_levs(k) = theta_target * (p_nudge/p_zero)**kappa
+ end do
+
+ ! Model level pressures
+ press_in_w3 = (/ 90000.0_r_def, 75000.0_r_def, 60000.0_r_def, &
+ 45000.0_r_def, 30000.0_r_def /)
+ press_in_wth = (/ 85000.0_r_def, 70000.0_r_def, 55000.0_r_def, &
+ 40000.0_r_def, 25000.0_r_def /)
+
+ do k = 0, nlayers - 1
+ exner_in_w3(map_w3(1)+k) = (press_in_w3(k)/p_zero)**kappa
+ end do
+
+ do k = 1, nlayers
+ exner_in_wth(map_wth(1)+k) = (press_in_wth(k)/p_zero)**kappa
+ end do
+ ! Bottom Wtheta value is not used by the kernel, other than being
+ ! overwritten with the value of the level above at the end
+ exner_in_wth(map_wth(1)) = exner_in_wth(map_wth(1)+1)
+
+ theta_mod_levs(:) = 0.0_r_def
+ u_mod_levs(:) = 0.0_r_def
+ v_mod_levs(:) = 0.0_r_def
+
+ ! Expected answers, obtained from an independent reference implementation
+ ! of the interpolation algorithm using the same input data. Note that the
+ ! bottom u/v level does not reproduce the analytic profile exactly, since
+ ! the kernel's search for the bracketing nudging levels at the bottom
+ ! model level is based on the pressure of the level above
+ u_answer = (/ 36.261930665360950_r_def, 36.126216962592230_r_def, &
+ 35.010499206021194_r_def, 33.572088843762280_r_def, &
+ 31.544763303221465_r_def /)
+ v_answer = (/ -7.504772266144382_r_def, -7.450486785036894_r_def, &
+ -7.004199682408476_r_def, -6.428835537504913_r_def, &
+ -5.617905321288585_r_def /)
+ theta_answer = (/ 329.7439019679132_r_def, 329.7439019679132_r_def, &
+ 332.65624218452757_r_def, 336.2736730367809_r_def, &
+ 341.0504790035589_r_def, 348.100533442245_r_def /)
+
+ call nudging_vert_interp_code( &
+ nlayers, &
+ theta_mod_levs, u_mod_levs, v_mod_levs, &
+ T_nudge_levs, u_nudge_levs, v_nudge_levs, &
+ exner_in_wth, exner_in_w3, ln_surfp_nudge, &
+ p_zero, kappa, nudge_levels, &
+ ndf_wth, undf_wth, map_wth, &
+ ndf_w3, undf_w3, map_w3, &
+ ndf_ec, undf_ec, map_ec, &
+ ndf_ec2d, undf_ec2d, map_ec2d &
+ )
+
+ @assertEqual(u_answer, u_mod_levs, tol)
+ @assertEqual(v_answer, v_mod_levs, tol)
+ @assertEqual(theta_answer, theta_mod_levs, tol)
+
+ end subroutine test_all
+
+end module nudging_vert_interp_kernel_mod_test
diff --git a/science/gungho/unit-test/kernel/external_forcing/nudging_weights_kernel_mod_test.pf b/science/gungho/unit-test/kernel/external_forcing/nudging_weights_kernel_mod_test.pf
new file mode 100644
index 0000000000..ad9ab153f2
--- /dev/null
+++ b/science/gungho/unit-test/kernel/external_forcing/nudging_weights_kernel_mod_test.pf
@@ -0,0 +1,55 @@
+!-----------------------------------------------------------------------------
+! (C) Crown copyright Met Office. All rights reserved.
+! The file LICENCE, distributed with this code, contains details of the terms
+! under which the code may be used.
+!-------------------------------------------------------------------------------
+module nudging_weights_kernel_mod_test
+
+ use constants_mod, only : i_def, r_def, l_def
+ use funit
+
+
+ implicit none
+
+ private
+ public :: test_all
+
+contains
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ @Test
+ subroutine test_all( )
+
+ use nudging_weights_kernel_mod, only: nudging_weights_code
+
+ implicit none
+
+ integer(kind=i_def), parameter :: nlayers = 10
+ integer(kind=i_def), parameter :: ndf = 1
+ integer(kind=i_def), parameter :: undf = nlayers
+ real(kind=r_def), parameter :: tol = 10.0_r_def*epsilon(1.0_r_def)
+
+ integer(kind=i_def) :: map(ndf)
+ real(kind=r_def) :: answer(undf)
+ real(kind=r_def) :: weights(undf)
+ integer(kind=i_def) :: bottom_level, top_level, bottom_width, top_width
+
+ map(1) = 1
+ bottom_level = 2
+ top_level = 7
+ bottom_width = 1
+ top_width = 2
+ answer = (/ 0.0_r_def, 0.0_r_def, 0.5_r_def, 1.0_r_def, 1.0_r_def, &
+ 1.0_r_def, 0.75_r_def, 0.5_r_def, 0.25_r_def, 0.0_r_def /)
+ weights(:) = 0.0_r_def
+
+ call nudging_weights_code( &
+ nlayers, weights, bottom_level, top_level, bottom_width, top_width, &
+ ndf, undf, map &
+ )
+
+ @assertEqual(answer, weights, tol)
+
+ end subroutine test_all
+
+end module nudging_weights_kernel_mod_test
From 8d14f6d1700a91ea7fb141c4dd44cafbca5d4bc0 Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Tue, 28 Jul 2026 13:32:25 +0100
Subject: [PATCH 02/35] remove nudging_source, fix various bugs and get azspice
suite running
---
.../lfric_atm/metadata/lfric_dictionary.xml | 6 +++-
.../opt/rose-app-nudging-newtonian.conf | 7 ++--
.../opt/rose-app-nudging-spectral.conf | 7 ++--
rose-stem/app/lfric_atm/rose-app.conf | 1 -
.../site/meto/groups/groups_lfric_atm.cylc | 7 ----
.../lfric-gungho/HEAD/rose-meta.conf | 13 -------
.../gungho/rose-meta/lfric-gungho/versions.py | 35 +++++++++++++++++++
.../physics/external_forcing_alg_mod.X90 | 6 +++-
.../driver/create_nudging_fields_mod.f90 | 8 ++---
.../nudging_weights_kernel_mod.F90 | 4 +--
10 files changed, 61 insertions(+), 33 deletions(-)
diff --git a/applications/lfric_atm/metadata/lfric_dictionary.xml b/applications/lfric_atm/metadata/lfric_dictionary.xml
index e866da1d00..272f51dde6 100644
--- a/applications/lfric_atm/metadata/lfric_dictionary.xml
+++ b/applications/lfric_atm/metadata/lfric_dictionary.xml
@@ -430,6 +430,10 @@
-
+
+
+
+
+
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf
index 0ae8acd5b1..6cd596e829 100644
--- a/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf
@@ -10,10 +10,13 @@ wind_forcing='nudging'
nudging_directory='/data/users/mohit.dalvi/lfric/data/nudge/era/C48'
nudging_filename='era5_nudge_L137_C48_MG_2021032400-2706'
-[!!namelist:multires_coupling]
+[namelist:formulation]
+use_multires_coupling=.true.
+
+[namelist:multires_coupling]
aerosol_mesh_name='dynamics'
coarse_nudging=.false.
-!!nudging_mesh_name='dynamics'
+nudging_mesh_name='dynamics'
orography_mesh_name='dynamics'
physics_mesh_name='dynamics'
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf
index 644e816e93..b6352184bc 100644
--- a/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf
@@ -10,10 +10,13 @@ wind_forcing='nudging'
nudging_directory='/data/users/mohit.dalvi/lfric/data/nudge/era/C48'
nudging_filename='era5_nudge_L137_C48_MG_2021032400-2706'
-[!!namelist:multires_coupling]
+[namelist:formulation]
+use_multires_coupling=.true.
+
+[namelist:multires_coupling]
aerosol_mesh_name='dynamics'
coarse_nudging=.false.
-!!nudging_mesh_name='dynamics'
+nudging_mesh_name='dynamics'
orography_mesh_name='dynamics'
physics_mesh_name='dynamics'
diff --git a/rose-stem/app/lfric_atm/rose-app.conf b/rose-stem/app/lfric_atm/rose-app.conf
index 1e4ebb11f6..f49ab7d81e 100644
--- a/rose-stem/app/lfric_atm/rose-app.conf
+++ b/rose-stem/app/lfric_atm/rose-app.conf
@@ -1457,7 +1457,6 @@ nudge_data_levels=137
nudging_level_bottom=5
nudging_level_top=52
nudging_method='convolution'
-nudging_source='era'
nudging_spinup_start=3600.0
nudging_spinup_end=7200.0
nudging_width_bottom=1
diff --git a/rose-stem/site/meto/groups/groups_lfric_atm.cylc b/rose-stem/site/meto/groups/groups_lfric_atm.cylc
index c89ba5c986..a66a7f1cfa 100644
--- a/rose-stem/site/meto/groups/groups_lfric_atm.cylc
+++ b/rose-stem/site/meto/groups/groups_lfric_atm.cylc
@@ -179,12 +179,6 @@
"lfric_atm_scm_hd209458b-BiP2x2-50000x50000_ex1a_cce_fast-debug-32bit",
"lfric_atm_canned_ex1a_cce_fast-debug-32bit",
],
- "lfric_atm_ex1a_nudging": [
- "lfric_atm_nwp_gal9_nudging-spectral-C48_MG_ex1a_cce_fast-debug-32bit",
- "lfric_atm_nwp_gal9_nudging-spectral-coarse-C48_MG_ex1a_cce_fast-debug-32bit",
- "lfric_atm_nwp_gal9_nudging-newtonian-C48_MG_azspice_gnu_fast-debug-32bit",
- "lfric_atm_nwp_gal9_nudging-newtonian-coarse-C48_MG_ex1a_cce_fast-debug-32bit",
- ],
"lfric_atm_ex1a_developer": [
"lfric_atm_nwp_ex1a_developer",
"lfric_atm_clim_gal9-C12_ex1a_cce_fast-debug-32bit",
@@ -204,7 +198,6 @@
"lfric_atm_hd209458b-C24_ex1a_cce_fast-debug-32bit",
"lfric_atm_camembert_case3_gj1214b-C12_ex1a_cce_fast-debug-32bit",
"lfric_atm_ex1a_threaded",
- "lfric_atm_ex1a_nudging",
],
"lfric_atm_ex1a_weekly": [
"lfric_atm_nwp_gal9-C224_MG_ex1a_cce_production-32bit",
diff --git a/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf b/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
index 28c2e15574..7576f8e6aa 100644
--- a/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
+++ b/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
@@ -4014,19 +4014,6 @@ ns=namelist/Science/Nudging
sort-key=Panel-A03
type=real
-[namelist:nudging=nudging_source]
-compulsory=true
-description=The source of the reference states for nudging
-!enumeration=true
-help=Nudging has been implemented to nudge
- = (a) using Re-analyses (e.g. ERA) data
- = (b) using model (e.g. NWP, M/L) data
-!kind=default
-ns=namelist/Science/Nudging
-sort-key=Panel-A01
-value-titles=ERA, AIFS
-values='era', 'aifs'
-
[namelist:nudging=nudging_spinup_end]
compulsory=true
description=Time at which to end nudging spinup (seconds)
diff --git a/science/gungho/rose-meta/lfric-gungho/versions.py b/science/gungho/rose-meta/lfric-gungho/versions.py
index 01e65a2b91..496587fda3 100644
--- a/science/gungho/rose-meta/lfric-gungho/versions.py
+++ b/science/gungho/rose-meta/lfric-gungho/versions.py
@@ -31,3 +31,38 @@ def upgrade(self, config, meta_config=None):
# Add settings
return config, self.reports
"""
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudge_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time"], "3600.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "432000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "864000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_kmax"], "20"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_kmin"], "2"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_source"]
+ )
+ return config, self.reports
diff --git a/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90 b/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
index ee6110f388..7b227d99d1 100644
--- a/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
+++ b/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
@@ -403,6 +403,8 @@ contains
case ( wind_forcing_nudging )
+ call du_forcing%initialise(u%get_function_space())
+
if (time_now >= nudging_spinup_start) then
! Extract fields for spectral nudging
call derived_fields%get_field('u_in_w3', u_in_w3)
@@ -411,7 +413,6 @@ contains
call derived_fields%get_field('v_in_w3_nudging_ref', v_in_w3_ref)
! Initialise components for wind increment
- call du_forcing%initialise(u%get_function_space())
call du_in_w3%initialise(u_in_w3%get_function_space())
call dv_in_w3%initialise(v_in_w3%get_function_space())
call dw_in_wth%initialise(theta%get_function_space())
@@ -423,6 +424,9 @@ contains
! Combine increments to obtain full wind increment
call set_wind(config, du_forcing, du_in_w3, dv_in_w3, dw_in_wth)
+
+ else
+ call invoke(setval_c(du_forcing, 0.0_r_def))
end if
case( wind_forcing_profile )
diff --git a/science/gungho/source/driver/create_nudging_fields_mod.f90 b/science/gungho/source/driver/create_nudging_fields_mod.f90
index 8e9f55f7f2..fa30e43155 100644
--- a/science/gungho/source/driver/create_nudging_fields_mod.f90
+++ b/science/gungho/source/driver/create_nudging_fields_mod.f90
@@ -104,7 +104,7 @@ subroutine process_nudging_fields(config, processor)
! remapping from nudging to model grid. Hence this field is needed
! when any one of theta or wind is forced.
call processor%apply(make_spec( &
- 'surface_pressure_nudging_ext_ref', main%none, W3, &
+ 'surface_pressure_nudging_ext_ref', main%derived, W3, &
coarse=coarse_nudging, &
coarse_mesh_name=nudging_mesh_name, &
twod=.true., time_axis=axis%nudging &
@@ -112,7 +112,7 @@ subroutine process_nudging_fields(config, processor)
if ( theta_forcing == theta_forcing_nudging ) then
call processor%apply(make_spec( &
- 'temperature_nudging_ext_ref', main%none, W3, twod=.true., &
+ 'temperature_nudging_ext_ref', main%derived, W3, twod=.true., &
coarse=coarse_nudging, &
coarse_mesh_name=nudging_mesh_name, &
mult='nudging_levels', time_axis=axis%nudging &
@@ -121,13 +121,13 @@ subroutine process_nudging_fields(config, processor)
if ( wind_forcing == wind_forcing_nudging ) then
call processor%apply(make_spec( &
- 'u_nudging_ext_ref', main%none, W3, twod=.true., &
+ 'u_nudging_ext_ref', main%derived, W3, twod=.true., &
coarse=coarse_nudging, &
coarse_mesh_name=nudging_mesh_name, &
mult='nudging_levels', time_axis=axis%nudging &
))
call processor%apply(make_spec( &
- 'v_nudging_ext_ref', main%none, W3, twod=.true., &
+ 'v_nudging_ext_ref', main%derived, W3, twod=.true., &
coarse=coarse_nudging, &
coarse_mesh_name=nudging_mesh_name, &
mult='nudging_levels', time_axis=axis%nudging &
diff --git a/science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90 b/science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90
index e9a206cf29..b6c4fc63ba 100644
--- a/science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90
+++ b/science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90
@@ -97,7 +97,7 @@ subroutine nudging_weights_code(nlayers, &
do k = bottom_nudge_level-bottom_nudge_width, bottom_nudge_level+bottom_nudge_width
weights(idx + k) = real(k - (bottom_nudge_level - bottom_nudge_width), r_def) / &
- real(2*bottom_nudge_width, r_def)
+ real(MAX(1, 2*bottom_nudge_width), r_def)
end do
do k = bottom_nudge_level+bottom_nudge_width+1, top_nudge_level-top_nudge_width-1
@@ -106,7 +106,7 @@ subroutine nudging_weights_code(nlayers, &
do k = top_nudge_level-top_nudge_width, top_nudge_level+top_nudge_width
weights(idx + k) = 1.0_r_def - real(k - (top_nudge_level - top_nudge_width), r_def) / &
- real(2*top_nudge_width, r_def)
+ real(MAX(1, 2*top_nudge_width), r_def)
end do
do k = top_nudge_level+top_nudge_width+1, nl
From c92980119ebdf97d82cd88105f331267ddd1b609 Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Tue, 28 Jul 2026 14:22:03 +0100
Subject: [PATCH 03/35] rose meta tweak
---
science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf | 1 -
1 file changed, 1 deletion(-)
diff --git a/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf b/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
index 7576f8e6aa..67f2096fde 100644
--- a/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
+++ b/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
@@ -3822,7 +3822,6 @@ description=Whether to apply nudging on a coarser mesh than that used
help=The mesh is given by the nudging_mesh_name option.
!kind=default
sort-key=Section-A09
-trigger=namelist:multires_coupling=nudging_mesh_name: .true. ;
type=logical
[namelist:multires_coupling=coarse_rad_aerosol]
From 54864ec82a629d99d284e63060f674c73bb69e77 Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Tue, 28 Jul 2026 14:52:11 +0100
Subject: [PATCH 04/35] attempt to improve upgrade macro
---
science/gungho/rose-meta/lfric-gungho/versions.py | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/science/gungho/rose-meta/lfric-gungho/versions.py b/science/gungho/rose-meta/lfric-gungho/versions.py
index 496587fda3..d0a23aa0d0 100644
--- a/science/gungho/rose-meta/lfric-gungho/versions.py
+++ b/science/gungho/rose-meta/lfric-gungho/versions.py
@@ -40,6 +40,17 @@ class vn32_t670(MacroUpgrade):
def upgrade(self, config, meta_config=None):
# Commands From: rose-meta/lfric-gungho
+ # Set nudging mesh name to dynamics mesh name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:nudging", "nudging_mesh_name"]
+ )
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:nudging", "dynamics_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudge_method"], dynamics_mesh_name
+ )
# Add new nudging namelist options
self.add_setting(
config, ["namelist:nudging", "nudge_method"], "'convolution'"
@@ -62,6 +73,7 @@ def upgrade(self, config, meta_config=None):
self.add_setting(
config, ["namelist:nudging", "spectral_stencil_extent"], "12"
)
+ # Remove retired setting
self.remove_setting(
config, ["namelist:nudging", "nudging_source"]
)
From bf37c8e72f71501ec2135adc776e866ef97401df Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Tue, 28 Jul 2026 15:55:29 +0100
Subject: [PATCH 05/35] another attempt to fix upgrade macro
---
science/gungho/rose-meta/lfric-gungho/versions.py | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/science/gungho/rose-meta/lfric-gungho/versions.py b/science/gungho/rose-meta/lfric-gungho/versions.py
index d0a23aa0d0..e5d6e33f97 100644
--- a/science/gungho/rose-meta/lfric-gungho/versions.py
+++ b/science/gungho/rose-meta/lfric-gungho/versions.py
@@ -42,14 +42,15 @@ def upgrade(self, config, meta_config=None):
# Commands From: rose-meta/lfric-gungho
# Set nudging mesh name to dynamics mesh name
nudging_mesh_name = self.get_setting_value(
- config, ["namelist:nudging", "nudging_mesh_name"]
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
)
dynamics_mesh_name = self.get_setting_value(
- config, ["namelist:nudging", "dynamics_mesh_name"]
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
)
if nudging_mesh_name == "''":
self.change_setting_value(
- config, ["namelist:nudging", "nudge_method"], dynamics_mesh_name
+ config, ["namelist:multires_coupling", "nudge_method"],
+ dynamics_mesh_name
)
# Add new nudging namelist options
self.add_setting(
From cde3772f0794c9c86fd541c612e9a62044fa4b59 Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Tue, 28 Jul 2026 16:20:33 +0100
Subject: [PATCH 06/35] final upgrade macro
---
.../gungho/rose-meta/lfric-gungho/versions.py | 29 ++++---------------
1 file changed, 5 insertions(+), 24 deletions(-)
diff --git a/science/gungho/rose-meta/lfric-gungho/versions.py b/science/gungho/rose-meta/lfric-gungho/versions.py
index e5d6e33f97..cf520201ff 100644
--- a/science/gungho/rose-meta/lfric-gungho/versions.py
+++ b/science/gungho/rose-meta/lfric-gungho/versions.py
@@ -20,18 +20,16 @@ def __repr__(self):
"""
Copy this template and complete to add your macro
-
class vnXX_txxx(MacroUpgrade):
# Upgrade macro for by
-
BEFORE_TAG = "vnX.X"
AFTER_TAG = "vnX.X_txxx"
-
def upgrade(self, config, meta_config=None):
# Add settings
return config, self.reports
"""
+
class vn32_t670(MacroUpgrade):
"""Upgrade macro for ticket #670 by Thomas Bendall."""
@@ -40,18 +38,6 @@ class vn32_t670(MacroUpgrade):
def upgrade(self, config, meta_config=None):
# Commands From: rose-meta/lfric-gungho
- # Set nudging mesh name to dynamics mesh name
- nudging_mesh_name = self.get_setting_value(
- config, ["namelist:multires_coupling", "nudging_mesh_name"]
- )
- dynamics_mesh_name = self.get_setting_value(
- config, ["namelist:multires_coupling", "dynamics_mesh_name"]
- )
- if nudging_mesh_name == "''":
- self.change_setting_value(
- config, ["namelist:multires_coupling", "nudge_method"],
- dynamics_mesh_name
- )
# Add new nudging namelist options
self.add_setting(
config, ["namelist:nudging", "nudge_method"], "'convolution'"
@@ -65,17 +51,12 @@ def upgrade(self, config, meta_config=None):
self.add_setting(
config, ["namelist:nudging", "nudging_spinup_end"], "864000.0"
)
- self.add_setting(
- config, ["namelist:nudging", "spectral_kmax"], "20"
- )
- self.add_setting(
- config, ["namelist:nudging", "spectral_kmin"], "2"
- )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
self.add_setting(
config, ["namelist:nudging", "spectral_stencil_extent"], "12"
)
# Remove retired setting
- self.remove_setting(
- config, ["namelist:nudging", "nudging_source"]
- )
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+
return config, self.reports
From 96e903f711dc0fa9be62f39d379594550f754b6c Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Tue, 28 Jul 2026 17:35:31 +0100
Subject: [PATCH 07/35] be neater with check on nudging config
---
.../configuration/check_configuration_mod.F90 | 44 ++++++++++++-------
1 file changed, 27 insertions(+), 17 deletions(-)
diff --git a/science/gungho/source/configuration/check_configuration_mod.F90 b/science/gungho/source/configuration/check_configuration_mod.F90
index ead552c9bf..1fb5405b3e 100644
--- a/science/gungho/source/configuration/check_configuration_mod.F90
+++ b/science/gungho/source/configuration/check_configuration_mod.F90
@@ -626,7 +626,8 @@ end subroutine check_configuration
subroutine get_required_stencil_depth(stencil_depths, base_mesh_names, config)
use external_forcing_config_mod, only: theta_forcing_nudging, &
- wind_forcing_nudging
+ wind_forcing_nudging, &
+ external_forcing_is_loaded
use nudging_config_mod, only: nudging_method_convolution
implicit none
@@ -647,6 +648,7 @@ subroutine get_required_stencil_depth(stencil_depths, base_mesh_names, config)
logical(kind=l_def) :: use_multires_coupling
logical(kind=l_def) :: coarse_aerosol_transport
logical(kind=l_def) :: coarse_nudging
+ logical(kind=l_def) :: use_spectral_nudging
integer(kind=i_def) :: operators
integer(kind=i_def) :: fv_horizontal_order
integer(kind=i_def) :: panel_edge_treatment
@@ -664,17 +666,13 @@ subroutine get_required_stencil_depth(stencil_depths, base_mesh_names, config)
! ------------------------------------------------------------------------ !
prime_mesh_name = config%base_mesh%prime_mesh_name()
- operators = config%transport%operators()
- fv_horizontal_order = config%transport%fv_horizontal_order()
- panel_edge_treatment = config%transport%panel_edge_treatment()
- panel_edge_high_order = config%transport%panel_edge_high_order()
- dep_pt_stencil_extent = config%transport%dep_pt_stencil_extent()
- ffsl_inner_order = config%transport%ffsl_inner_order()
- ffsl_outer_order = config%transport%ffsl_outer_order()
- theta_forcing = config%external_forcing%theta_forcing()
- wind_forcing = config%external_forcing%wind_forcing()
- nudging_method = config%nudging%nudging_method()
- spectral_stencil_extent = config%nudging%spectral_stencil_extent()
+ operators = config%transport%operators()
+ fv_horizontal_order = config%transport%fv_horizontal_order()
+ panel_edge_treatment = config%transport%panel_edge_treatment()
+ panel_edge_high_order = config%transport%panel_edge_high_order()
+ dep_pt_stencil_extent = config%transport%dep_pt_stencil_extent()
+ ffsl_inner_order = config%transport%ffsl_inner_order()
+ ffsl_outer_order = config%transport%ffsl_outer_order()
use_multires_coupling = config%formulation%use_multires_coupling()
if (use_multires_coupling) then
@@ -684,6 +682,21 @@ subroutine get_required_stencil_depth(stencil_depths, base_mesh_names, config)
coarse_nudging = config%multires_coupling%coarse_nudging()
end if
+ use_spectral_nudging = .false.
+ if (external_forcing_is_loaded()) then
+ theta_forcing = config%external_forcing%theta_forcing()
+ wind_forcing = config%external_forcing%wind_forcing()
+ if (theta_forcing == theta_forcing_nudging &
+ .or. wind_forcing == wind_forcing_nudging) then
+ nudging_method = config%nudging%nudging_method()
+ if (nudging_method == nudging_method_convolution &
+ .and. use_multires_coupling) then
+ spectral_stencil_extent = config%nudging%spectral_stencil_extent()
+ use_spectral_nudging = .true.
+ end if
+ end if
+ end if
+
! ------------------------------------------------------------------------ !
! Set default depth
! ------------------------------------------------------------------------ !
@@ -755,11 +768,8 @@ subroutine get_required_stencil_depth(stencil_depths, base_mesh_names, config)
stencil_depths(i) = 2
end if
- if ( (theta_forcing == theta_forcing_nudging &
- .or. wind_forcing == wind_forcing_nudging) &
- .and. nudging_method == nudging_method_convolution &
- .and. (use_multires_coupling .and. coarse_nudging &
- .and. trim(base_mesh_names(i)) == trim(nudging_mesh_name)) ) then
+ if ( use_spectral_nudging &
+ .and. trim(base_mesh_names(i)) == trim(nudging_mesh_name) ) then
stencil_depths(i) = MAX(stencil_depths(i), spectral_stencil_extent)
end if
end do
From 00198e4dad8d755f25477db174c30cefecea4b9f Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Tue, 28 Jul 2026 18:33:10 +0100
Subject: [PATCH 08/35] actually get upgrade macro working properly
---
science/gungho/rose-meta/lfric-gungho/versions.py | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/science/gungho/rose-meta/lfric-gungho/versions.py b/science/gungho/rose-meta/lfric-gungho/versions.py
index cf520201ff..cda8137336 100644
--- a/science/gungho/rose-meta/lfric-gungho/versions.py
+++ b/science/gungho/rose-meta/lfric-gungho/versions.py
@@ -58,5 +58,20 @@ def upgrade(self, config, meta_config=None):
)
# Remove retired setting
self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
return config, self.reports
From b3edeeea3ef6d4876c31f2fcf886a44796a190da Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Tue, 28 Jul 2026 20:07:16 +0100
Subject: [PATCH 09/35] tidy up so that multires_coupling is not necessary for
nudging
---
.../source/algorithm/physics/nudging_alg_mod.x90 | 11 ++++++++++-
.../source/configuration/check_configuration_mod.F90 | 8 ++++++--
science/gungho/source/driver/gungho_driver_mod.F90 | 9 ++++++++-
3 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/science/gungho/source/algorithm/physics/nudging_alg_mod.x90 b/science/gungho/source/algorithm/physics/nudging_alg_mod.x90
index 6400e24457..71346fcf52 100644
--- a/science/gungho/source/algorithm/physics/nudging_alg_mod.x90
+++ b/science/gungho/source/algorithm/physics/nudging_alg_mod.x90
@@ -83,7 +83,6 @@ contains
! Get config variables
! ------------------------------------------------------------------------ !
- coarse_nudging = config%multires_coupling%coarse_nudging()
use_multires_coupling = config%formulation%use_multires_coupling()
nudging_method = config%nudging%nudging_method()
spectral_stencil_extent = config%nudging%spectral_stencil_extent()
@@ -94,6 +93,16 @@ contains
p_zero = config%planet%p_zero()
kappa = config%planet%kappa()
+ ! Getting nudging mesh name
+ coarse_nudging = .false.
+ nudging_mesh_name = config%base_mesh%prime_mesh_name()
+ if (use_multires_coupling) then
+ coarse_nudging = config%multires_coupling%coarse_nudging()
+ if (coarse_nudging) then
+ nudging_mesh_name = config%multires_coupling%nudging_mesh_name()
+ end if
+ end if
+
! ------------------------------------------------------------------------ !
! Set pointers and initialise fields
! ------------------------------------------------------------------------ !
diff --git a/science/gungho/source/configuration/check_configuration_mod.F90 b/science/gungho/source/configuration/check_configuration_mod.F90
index 1fb5405b3e..30e14c9910 100644
--- a/science/gungho/source/configuration/check_configuration_mod.F90
+++ b/science/gungho/source/configuration/check_configuration_mod.F90
@@ -680,6 +680,8 @@ subroutine get_required_stencil_depth(stencil_depths, base_mesh_names, config)
coarse_aerosol_transport = config%multires_coupling%coarse_aerosol_transport()
nudging_mesh_name = config%multires_coupling%nudging_mesh_name()
coarse_nudging = config%multires_coupling%coarse_nudging()
+ else
+ coarse_nudging = .false.
end if
use_spectral_nudging = .false.
@@ -689,10 +691,12 @@ subroutine get_required_stencil_depth(stencil_depths, base_mesh_names, config)
if (theta_forcing == theta_forcing_nudging &
.or. wind_forcing == wind_forcing_nudging) then
nudging_method = config%nudging%nudging_method()
- if (nudging_method == nudging_method_convolution &
- .and. use_multires_coupling) then
+ if (nudging_method == nudging_method_convolution) then
spectral_stencil_extent = config%nudging%spectral_stencil_extent()
use_spectral_nudging = .true.
+ if (.not. coarse_nudging) then
+ nudging_mesh_name = prime_mesh_name
+ end if
end if
end if
end if
diff --git a/science/gungho/source/driver/gungho_driver_mod.F90 b/science/gungho/source/driver/gungho_driver_mod.F90
index f7d0a02894..88f9f46e96 100644
--- a/science/gungho/source/driver/gungho_driver_mod.F90
+++ b/science/gungho/source/driver/gungho_driver_mod.F90
@@ -473,10 +473,17 @@ subroutine step( modeldb )
if ( external_forcing_is_loaded() ) then
theta_forcing = modeldb%config%external_forcing%theta_forcing()
wind_forcing = modeldb%config%external_forcing%wind_forcing()
- nudging_mesh_name = modeldb%config%multires_coupling%nudging_mesh_name()
if ( theta_forcing == theta_forcing_nudging &
.or. wind_forcing == wind_forcing_nudging) then
+ ! Getting nudging mesh name
+ nudging_mesh_name = modeldb%config%base_mesh%prime_mesh_name()
+ if ( modeldb%config%formulation%use_multires_coupling() ) then
+ if ( modeldb%config%multires_coupling%coarse_nudging() ) then
+ nudging_mesh_name = modeldb%config%multires_coupling%nudging_mesh_name()
+ end if
+ end if
+
derived_fields => modeldb%fields%get_field_collection("derived_fields")
call update_variable_fields( &
model_axes%nudging_times_list, modeldb%clock, derived_fields &
From 6d4e9078cb9e923f427353de35cf8850b47d5f07 Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Tue, 28 Jul 2026 21:07:03 +0100
Subject: [PATCH 10/35] protect physics settings and fix upgrade macro
---
science/gungho/rose-meta/lfric-gungho/versions.py | 2 +-
.../source/algorithm/physics/external_forcing_alg_mod.X90 | 8 +++++---
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/science/gungho/rose-meta/lfric-gungho/versions.py b/science/gungho/rose-meta/lfric-gungho/versions.py
index cda8137336..9c4e8dd8f3 100644
--- a/science/gungho/rose-meta/lfric-gungho/versions.py
+++ b/science/gungho/rose-meta/lfric-gungho/versions.py
@@ -40,7 +40,7 @@ def upgrade(self, config, meta_config=None):
# Commands From: rose-meta/lfric-gungho
# Add new nudging namelist options
self.add_setting(
- config, ["namelist:nudging", "nudge_method"], "'convolution'"
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
)
self.add_setting(
config, ["namelist:nudging", "nudging_relax_time"], "3600.0"
diff --git a/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90 b/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
index 7b227d99d1..245eb4b3b2 100644
--- a/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
+++ b/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
@@ -171,8 +171,10 @@ contains
real(kind=r_def) :: diffusion_coefficient
real(kind=r_def) :: kappa
real(kind=r_def) :: nudging_spinup_start
- integer(kind=i_def) :: cloud, scheme
logical(kind=l_def) :: write_diag, use_xios_io
+#ifdef UM_PHYSICS
+ integer(kind=i_def) :: cloud, scheme
+#endif
if ( LPROF ) call start_timing( id, 'external_forcing' )
@@ -187,8 +189,6 @@ contains
filtering_order = config%external_forcing%filtering_order()
diffusion_order = config%external_forcing%diffusion_order()
diffusion_coefficient = config%external_forcing%diffusion_coefficient()
- cloud = config%section_choice%cloud()
- scheme = config%cloud%scheme()
write_diag = config%io%write_diag()
use_xios_io = config%io%use_xios_io()
kappa = config%planet%kappa()
@@ -522,6 +522,8 @@ contains
#ifdef UM_PHYSICS
if ( theta_forcing_done .or. vapour_forcing_done ) then
+ cloud = config%section_choice%cloud()
+ scheme = config%cloud%scheme()
! pc2 response to forcing if requested
if (cloud == cloud_um .and. scheme == scheme_pc2 .and. &
pc2_force_response) then
From 3e84f8f0cd4529de82912d1fec773dbaafb55414 Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Tue, 28 Jul 2026 22:13:10 +0100
Subject: [PATCH 11/35] ... and fix another rogue config variable
---
.../source/algorithm/physics/external_forcing_alg_mod.X90 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90 b/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
index 245eb4b3b2..a365de2581 100644
--- a/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
+++ b/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
@@ -192,7 +192,6 @@ contains
write_diag = config%io%write_diag()
use_xios_io = config%io%use_xios_io()
kappa = config%planet%kappa()
- nudging_spinup_start = config%nudging%nudging_spinup_start()
mesh => theta%get_mesh()
height_wth => get_height_fv(config, mesh, Wtheta)
@@ -200,6 +199,7 @@ contains
! If nudging is to be applied, obtain reference fields
if ( theta_forcing == theta_forcing_nudging .or. &
wind_forcing == wind_forcing_nudging ) then
+ nudging_spinup_start = config%nudging%nudging_spinup_start()
call set_nudging_reference_fields(config, derived_fields, exner)
end if
From 13ae8ee098e67049a2458678773965aa64989128 Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Wed, 29 Jul 2026 09:02:03 +0100
Subject: [PATCH 12/35] rose-meta fix
---
science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf b/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
index 67f2096fde..2a8bc8f786 100644
--- a/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
+++ b/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
@@ -4002,7 +4002,7 @@ values='convolution', 'newtonian'
[namelist:nudging=nudging_relax_time]
compulsory=true
description=Time scale for nudging (seconds)
-fail_if=this < 0.0 ;
+fail-if=this < 0.0 ;
help=The nudging scheme applies an increment to nudge (parts of the
=spectrum of) a field towards a specified reference state. This parameter
=gives the time scale (in seconds) after which the spectrum of the field
@@ -4016,7 +4016,7 @@ type=real
[namelist:nudging=nudging_spinup_end]
compulsory=true
description=Time at which to end nudging spinup (seconds)
-fail_if=this < 0.0 ;
+fail-if=this < 0.0 ;
help=This option allows the nudging to be ramped up linearly over some
=spin-up period. This parameter specifies the length of time after the start
=of the model simulation to end the nudging spinup, in seconds.
@@ -4028,7 +4028,7 @@ type=real
[namelist:nudging=nudging_spinup_start]
compulsory=true
description=Time at which to start nudging spinup (seconds)
-fail_if=this < 0.0 ;
+fail-if=this < 0.0 ;
help=This option allows the nudging to be ramped up linearly over some
=spin-up period. This parameter specifies the length of time after the start
=of the model simulation to begin the nudging spinup, in seconds.
From 7f291c51f3dd2abe703fd2fc953ea54348f4bdf8 Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Wed, 29 Jul 2026 09:28:33 +0100
Subject: [PATCH 13/35] further clean up of handling config
---
.../physics/external_forcing_alg_mod.X90 | 20 ++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90 b/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
index a365de2581..c4bae2aaee 100644
--- a/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
+++ b/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
@@ -43,7 +43,7 @@ module external_forcing_alg_mod
#endif
! Derived Types
use config_mod, only: config_type
- use constants_mod, only: i_def, r_def, l_def
+ use constants_mod, only: i_def, r_def, l_def, IMDI, RMDI
use field_mod, only: field_type
use field_collection_mod, only: field_collection_type
use integer_field_mod, only: integer_field_type
@@ -58,8 +58,9 @@ module external_forcing_alg_mod
get_face_selector_ns
use mesh_mod, only: mesh_type
use mr_indices_mod, only: nummr
- use section_choice_config_mod, only: cloud_um
- use cloud_config_mod, only: scheme_pc2
+ use section_choice_config_mod, only: cloud_um, section_choice_is_loaded
+ use cloud_config_mod, only: scheme_pc2, cloud_is_loaded
+ use nudging_config_mod, only: nudging_is_loaded
use reference_element_mod, only: T
use sci_set_any_dof_kernel_mod, only: set_any_dof_kernel_type
@@ -172,9 +173,7 @@ contains
real(kind=r_def) :: kappa
real(kind=r_def) :: nudging_spinup_start
logical(kind=l_def) :: write_diag, use_xios_io
-#ifdef UM_PHYSICS
integer(kind=i_def) :: cloud, scheme
-#endif
if ( LPROF ) call start_timing( id, 'external_forcing' )
@@ -192,6 +191,12 @@ contains
write_diag = config%io%write_diag()
use_xios_io = config%io%use_xios_io()
kappa = config%planet%kappa()
+ cloud = IMDI
+ scheme = IMDI
+ nudging_spinup_start = RMDI
+ if (section_choice_is_loaded()) cloud = config%section_choice%cloud()
+ if (cloud_is_loaded()) scheme = config%cloud%scheme()
+ if (nudging_is_loaded()) nudging_spinup_start = config%nudging%nudging_spinup_start()
mesh => theta%get_mesh()
height_wth => get_height_fv(config, mesh, Wtheta)
@@ -199,11 +204,10 @@ contains
! If nudging is to be applied, obtain reference fields
if ( theta_forcing == theta_forcing_nudging .or. &
wind_forcing == wind_forcing_nudging ) then
- nudging_spinup_start = config%nudging%nudging_spinup_start()
call set_nudging_reference_fields(config, derived_fields, exner)
end if
- if ( theta_forcing /= theta_forcing_none .or. theta_relaxation .or. &
+ if ( theta_forcing /= theta_forcing_none .or. theta_relaxation .or. &
vertadvect_forcing ) then
call dtheta_forcing%initialise( vector_space = theta%get_function_space() )
call invoke( setval_c(dtheta_forcing, 0.0_r_def) )
@@ -522,8 +526,6 @@ contains
#ifdef UM_PHYSICS
if ( theta_forcing_done .or. vapour_forcing_done ) then
- cloud = config%section_choice%cloud()
- scheme = config%cloud%scheme()
! pc2 response to forcing if requested
if (cloud == cloud_um .and. scheme == scheme_pc2 .and. &
pc2_force_response) then
From 0e5e31252cfb6b0cd8c8d98070baf83392044862 Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Wed, 29 Jul 2026 11:36:54 +0100
Subject: [PATCH 14/35] remove defunct kgos
---
...9_nudging-era-C48_MG_azspice_gnu_fast-debug-32bit.txt | 9 ---------
...ng-era-coarse-C48_MG_azspice_gnu_fast-debug-32bit.txt | 9 ---------
2 files changed, 18 deletions(-)
delete mode 100644 rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal9_nudging-era-C48_MG_azspice_gnu_fast-debug-32bit.txt
delete mode 100644 rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal9_nudging-era-coarse-C48_MG_azspice_gnu_fast-debug-32bit.txt
diff --git a/rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal9_nudging-era-C48_MG_azspice_gnu_fast-debug-32bit.txt b/rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal9_nudging-era-C48_MG_azspice_gnu_fast-debug-32bit.txt
deleted file mode 100644
index 223e55a9fb..0000000000
--- a/rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal9_nudging-era-C48_MG_azspice_gnu_fast-debug-32bit.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-Inner product checksum rho = 48D818B2
-Inner product checksum theta = 53948CAD
-Inner product checksum u = 6AA1D21A
-Inner product checksum mr1 = 41C960BC
-Inner product checksum mr2 = 39A9CFF3
-Inner product checksum mr3 = 37844180
-Inner product checksum mr4 = 3923F952
-Inner product checksum mr5 = 0
-Inner product checksum mr6 = 0
diff --git a/rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal9_nudging-era-coarse-C48_MG_azspice_gnu_fast-debug-32bit.txt b/rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal9_nudging-era-coarse-C48_MG_azspice_gnu_fast-debug-32bit.txt
deleted file mode 100644
index 223e55a9fb..0000000000
--- a/rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal9_nudging-era-coarse-C48_MG_azspice_gnu_fast-debug-32bit.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-Inner product checksum rho = 48D818B2
-Inner product checksum theta = 53948CAD
-Inner product checksum u = 6AA1D21A
-Inner product checksum mr1 = 41C960BC
-Inner product checksum mr2 = 39A9CFF3
-Inner product checksum mr3 = 37844180
-Inner product checksum mr4 = 3923F952
-Inner product checksum mr5 = 0
-Inner product checksum mr6 = 0
From a45c0ff722f0b7821597a373f9ea8f6cbc47e9b7 Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Wed, 29 Jul 2026 16:24:35 +0100
Subject: [PATCH 15/35] fix convolution weights kernel
---
.../convolution_2d_weights_kernel_mod.F90 | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/science/gungho/source/kernel/external_forcing/convolution_2d_weights_kernel_mod.F90 b/science/gungho/source/kernel/external_forcing/convolution_2d_weights_kernel_mod.F90
index 6ce081fb3e..4c1119104d 100644
--- a/science/gungho/source/kernel/external_forcing/convolution_2d_weights_kernel_mod.F90
+++ b/science/gungho/source/kernel/external_forcing/convolution_2d_weights_kernel_mod.F90
@@ -148,7 +148,7 @@ subroutine convolution_2d_weights_code(nlayers, &
real(kind=r_def) :: chi1_i, chi2_i, chi3_i
real(kind=r_def) :: lon_c, lat_c, lon_i, lat_i
real(kind=r_def) :: radius
- real(kind=r_def) :: dist, sum_weights
+ real(kind=r_def) :: dist, cos_dist, sum_weights
real(kind=r_def) :: P_0, P_1, P_l, P_lm1, P_lm2, l_real
! Convolution is a 2D operation, so we can just act in the lowest layer
@@ -200,7 +200,7 @@ subroutine convolution_2d_weights_code(nlayers, &
end if
! Convert distance to cos(dist) for use in spherical harmonics
- dist = COS(dist)
+ cos_dist = COS(dist)
! Set weight for this cell in the stencil
idx_w = map_2d(1) + i - 1 ! Multidata index
@@ -210,7 +210,7 @@ subroutine convolution_2d_weights_code(nlayers, &
! First Legendre polynomials
P_0 = 1.0_r_def
- P_1 = dist
+ P_1 = cos_dist
if (kmin == 0) then
weights(idx_w) = weights(idx_w) + 1.0_r_def/(4.0_r_def*PI) * P_0
@@ -227,7 +227,7 @@ subroutine convolution_2d_weights_code(nlayers, &
l_real = REAL(l, r_def)
! Determine next Legendre polynomial P_l
P_l = ( &
- (2.0_r_def*l_real - 1.0_r_def) * dist * P_lm1 &
+ (2.0_r_def*l_real - 1.0_r_def) * cos_dist * P_lm1 &
- (l_real - 1.0_r_def) * P_lm2 &
) / l_real
! Update convolution weight based on Legendre polynomial:
@@ -245,7 +245,7 @@ subroutine convolution_2d_weights_code(nlayers, &
! edge of the stencil, which can help avoid amplifying some wavenumbers
weights(idx_w) = weights(idx_w) * EXP( &
-0.5_r_def * (dist / ( &
- 2.0_r_def*PI/(1.0_r_def + REAL(kmax, r_def)/3.0_r_def) &
+ 2.0_r_def*PI/(1.0_r_def + REAL(kmax, r_def)/6.0_r_def) &
))**2 &
)
end do
From d98e02dd4105fac92af74fb907dc2f2327fbf9d7 Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Thu, 30 Jul 2026 15:52:17 +0100
Subject: [PATCH 16/35] add new namelist option and tweak convolution
---
.../lfric-gungho/HEAD/rose-meta.conf | 19 ++++++++++++++++++-
.../gungho/rose-meta/lfric-gungho/versions.py | 5 ++++-
.../physics_constants_mod.X90 | 6 +++++-
.../convolution_2d_weights_kernel_mod.F90 | 16 ++++++++++------
.../nudging_weights_kernel_mod.F90 | 4 ++--
.../convolution_2d_weights_kernel_mod_test.pf | 12 ++++++------
6 files changed, 45 insertions(+), 17 deletions(-)
diff --git a/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf b/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
index 2a8bc8f786..0705044576 100644
--- a/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
+++ b/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
@@ -3996,6 +3996,7 @@ sort-key=Panel-A02
trigger=namelist:nudging=spectral_stencil_extent: this == "'convolution'";
=namelist:nudging=spectral_kmax: this != "'newtonian'";
=namelist:nudging=spectral_kmin: this != "'newtonian'";
+ =namelist:nudging=spectral_envelope_width: this == "'convolution'";
value-titles=Convolution, Newtonian
values='convolution', 'newtonian'
@@ -4055,6 +4056,20 @@ ns=namelist/Science/Nudging
sort-key=Panel-A05d
type=integer
+[namelist:nudging=spectral_envelope_width]
+compulsory=true
+description=Width of Gaussian envelope to apply to the spectral nudging filter
+help=The filter used by the spectral nudging scheme is the product of the
+ =sum of Legendre polynomials with a Gaussian envelope. Since the convolution
+ =is performed on a finite stencil, it is possible for some wavenumbers to
+ =be amplified rather than damped. The Gaussian envelope helps to avoid this
+ =effect. This number is the width of the Gaussian envelope in radians,
+ =corresponding to physical space.
+!kind=default
+ns=namelist/Science/Nudging
+sort-key=Panel-A06c
+type=real
+
[namelist:nudging=spectral_kmax]
compulsory=true
description=Maximum wavenumber to use in spectral nudging filter
@@ -4084,7 +4099,9 @@ compulsory=true
description=Sets the stencil extent to be used in spectral nudging
help=The spectral nudging scheme uses a convolution to nudge parts of the
=spectrum towards a specified reference state. This parameter sets the
- =stencil extent to be used in the convolution.
+ =stencil extent to be used in the convolution. Note that if this is not
+ =large enough to resolve the filter, it can cause numerical instability as
+ =some wavelengths can be amplified rather than damped through the nudging.
!kind=default
ns=namelist/Science/Nudging
sort-key=Panel-A07
diff --git a/science/gungho/rose-meta/lfric-gungho/versions.py b/science/gungho/rose-meta/lfric-gungho/versions.py
index 9c4e8dd8f3..5964d7516f 100644
--- a/science/gungho/rose-meta/lfric-gungho/versions.py
+++ b/science/gungho/rose-meta/lfric-gungho/versions.py
@@ -43,7 +43,7 @@ def upgrade(self, config, meta_config=None):
config, ["namelist:nudging", "nudging_method"], "'convolution'"
)
self.add_setting(
- config, ["namelist:nudging", "nudging_relax_time"], "3600.0"
+ config, ["namelist:nudging", "nudging_relax_time"], "21600.0"
)
self.add_setting(
config, ["namelist:nudging", "nudging_spinup_start"], "432000.0"
@@ -56,6 +56,9 @@ def upgrade(self, config, meta_config=None):
self.add_setting(
config, ["namelist:nudging", "spectral_stencil_extent"], "12"
)
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "12"
+ )
# Remove retired setting
self.remove_setting(config, ["namelist:nudging", "nudging_source"])
# If nudging_mesh_name is still the default '' value, set it to
diff --git a/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90 b/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
index 10b7bdd2ba..f324e1d141 100644
--- a/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
+++ b/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
@@ -438,6 +438,7 @@ contains
logical(kind=l_def) :: constant_exists
integer(kind=i_def) :: geometry, topology, coord_system
integer(kind=i_def) :: spectral_kmax, spectral_kmin
+ real(kind=r_def) :: envelope_width
real(kind=r_def) :: scaled_radius
integer(kind=tik) :: id
@@ -461,6 +462,7 @@ contains
scaled_radius = config%planet%scaled_radius()
spectral_kmax = config%nudging%spectral_kmax()
spectral_kmin = config%nudging%spectral_kmin()
+ envelope_width = config%nudging%spectral_envelope_width()
stencil_depth = config%nudging%spectral_stencil_extent()
ndata = (2*stencil_depth + 1)**2
@@ -476,11 +478,13 @@ contains
fs => function_space_collection%get_fs(twod_mesh, 0, 0, W3, ndata)
call convolution_weights_inventory%add_field(convolution_weights, fs, local_mesh)
- call invoke( convolution_2d_weights_kernel_type(convolution_weights, &
+ call invoke( setval_c(convolution_weights, 0.0_r_def), &
+ convolution_2d_weights_kernel_type(convolution_weights, &
chi, stencil_depth, &
panel_id, stencil_depth, &
spectral_kmax, &
spectral_kmin, &
+ envelope_width, &
geometry, topology, &
coord_system, &
scaled_radius) )
diff --git a/science/gungho/source/kernel/external_forcing/convolution_2d_weights_kernel_mod.F90 b/science/gungho/source/kernel/external_forcing/convolution_2d_weights_kernel_mod.F90
index 4c1119104d..8fc38f6d48 100644
--- a/science/gungho/source/kernel/external_forcing/convolution_2d_weights_kernel_mod.F90
+++ b/science/gungho/source/kernel/external_forcing/convolution_2d_weights_kernel_mod.F90
@@ -7,6 +7,10 @@
!> @details This kernel computes weights for performing a convolution that is
!! used for nudging the spectrum of a field towards the spectrum of
!! a reference field.
+!! The filter takes the form of a sum of Legendre polynomials which
+!! help select certain wavenumbers to be nudged. The filter is
+!! enveloped by a Gaussian function to ensure that the weights go
+!! smoothly to zero.
!! Only implemented for the lowest-order elements
module convolution_2d_weights_kernel_mod
@@ -36,7 +40,7 @@ module convolution_2d_weights_kernel_mod
!> Contains the metadata needed by the Psy layer
type, public, extends(kernel_type) :: convolution_2d_weights_kernel_type
private
- type(arg_type) :: meta_args(9) = (/ &
+ type(arg_type) :: meta_args(10) = (/ &
arg_type(GH_FIELD, GH_REAL, GH_WRITE, ANY_DISCONTINUOUS_SPACE_9), &
arg_type(GH_FIELD*3, GH_REAL, GH_READ, ANY_DISCONTINUOUS_SPACE_7, &
STENCIL(REGION)), &
@@ -44,6 +48,7 @@ module convolution_2d_weights_kernel_mod
STENCIL(REGION)), &
arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
+ arg_type(GH_SCALAR, GH_REAL, GH_READ), &
arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
@@ -78,6 +83,7 @@ module convolution_2d_weights_kernel_mod
!> @param[in] stencil_map_pid Stencil map for the panel ID field
!> @param[in] kmax Maximum wavenumber in filter
!> @param[in] kmin Minimum wavenumber in filter
+!> @param[in] sigma Width of the Gaussian envelope to filter
!> @param[in] geometry Geometry of the mesh
!> @param[in] topology Topology of the mesh
!> @param[in] coord_system System used by coordinate fields
@@ -104,6 +110,7 @@ subroutine convolution_2d_weights_code(nlayers, &
stencil_map_pid, &
kmax, &
kmin, &
+ sigma, &
geometry, &
topology, &
coord_system, &
@@ -133,6 +140,7 @@ subroutine convolution_2d_weights_code(nlayers, &
integer(kind=i_def), intent(in) :: stencil_map_pid(ndf_pid, stencil_size_pid)
real(kind=r_def), intent(in) :: basis_chi(1, ndf_chi, ndf_2d)
integer(kind=i_def), intent(in) :: kmax, kmin
+ real(kind=r_def), intent(in) :: sigma
integer(kind=i_def), intent(in) :: geometry, topology, coord_system
real(kind=r_def), intent(in) :: scaled_radius
real(kind=r_def), intent(in) :: panel_id(undf_pid)
@@ -243,11 +251,7 @@ subroutine convolution_2d_weights_code(nlayers, &
! Apply envelope to weights, to ensure they go smoothly to zero at the
! edge of the stencil, which can help avoid amplifying some wavenumbers
- weights(idx_w) = weights(idx_w) * EXP( &
- -0.5_r_def * (dist / ( &
- 2.0_r_def*PI/(1.0_r_def + REAL(kmax, r_def)/6.0_r_def) &
- ))**2 &
- )
+ weights(idx_w) = weights(idx_w) * EXP(-0.5_r_def * (dist / sigma)**2)
end do
! Normalise the weights
diff --git a/science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90 b/science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90
index b6c4fc63ba..74a9fa599a 100644
--- a/science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90
+++ b/science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90
@@ -91,7 +91,7 @@ subroutine nudging_weights_code(nlayers, &
nl = nlayers - 2 + ndf ! nlayers for Wtheta, nlayers - 1 for W3
idx = map(1)
- do k = 0, bottom_nudge_level-bottom_nudge_width-1
+ do k = 0, bottom_nudge_level-bottom_nudge_width-2
weights(idx + k) = 0.0_r_def
end do
@@ -100,7 +100,7 @@ subroutine nudging_weights_code(nlayers, &
real(MAX(1, 2*bottom_nudge_width), r_def)
end do
- do k = bottom_nudge_level+bottom_nudge_width+1, top_nudge_level-top_nudge_width-1
+ do k = bottom_nudge_level+bottom_nudge_width, top_nudge_level-top_nudge_width-1
weights(idx + k) = 1.0_r_def
end do
diff --git a/science/gungho/unit-test/kernel/external_forcing/convolution_2d_weights_kernel_mod_test.pf b/science/gungho/unit-test/kernel/external_forcing/convolution_2d_weights_kernel_mod_test.pf
index 102574c871..3e691bede9 100644
--- a/science/gungho/unit-test/kernel/external_forcing/convolution_2d_weights_kernel_mod_test.pf
+++ b/science/gungho/unit-test/kernel/external_forcing/convolution_2d_weights_kernel_mod_test.pf
@@ -72,6 +72,10 @@ contains
real(kind=r_def), parameter :: dlon = PI/48.0_r_def
real(kind=r_def), parameter :: dlat = PI/96.0_r_def
+ ! Gaussian envelope width, set to very large value to disable the envelope
+ ! for this unit-test
+ real(kind=r_def), parameter :: sigma = 1.0E16_r_def
+
real(kind=r_def), parameter :: tol = 10.0_r_def*epsilon(1.0_r_def)
integer(kind=i_def) :: map_chi(ndf_chi)
@@ -129,11 +133,7 @@ contains
+ 9.0_r_def*(1.0_r_def/8.0_r_def)*( &
35.0_r_def*dist**4 - 30.0_r_def*dist**2 + 3.0_r_def &
) &
- ) / (4.0_r_def*PI) * EXP( &
- -0.5_r_def * (dist / ( &
- 2.0_r_def*PI/(1.0_r_def + REAL(kmax, r_def)/3.0_r_def) &
- ))**2 &
- )
+ ) / (4.0_r_def*PI)
end do
end do
@@ -145,7 +145,7 @@ contains
call convolution_2d_weights_code( &
nlayers, weights, chi1, chi2, chi3, stencil_size, stencil_map_chi, &
- panel_id, stencil_size, stencil_map_pid, kmax, kmin, &
+ panel_id, stencil_size, stencil_map_pid, kmax, kmin, sigma, &
geometry, topology, coord_system, scaled_radius, &
ndf_2d, undf_2d, map_2d, ndf_chi, undf_chi, map_chi, basis_chi, &
ndf_pid, undf_pid, map_pid &
From bb03d057927cc383f6fa1ac1672d733b7e25f7ee Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Thu, 30 Jul 2026 17:14:55 +0100
Subject: [PATCH 17/35] fix bug in weights
---
.../kernel/external_forcing/nudging_weights_kernel_mod.F90 | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90 b/science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90
index 74a9fa599a..47291cc7b7 100644
--- a/science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90
+++ b/science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90
@@ -91,7 +91,7 @@ subroutine nudging_weights_code(nlayers, &
nl = nlayers - 2 + ndf ! nlayers for Wtheta, nlayers - 1 for W3
idx = map(1)
- do k = 0, bottom_nudge_level-bottom_nudge_width-2
+ do k = 0, bottom_nudge_level-bottom_nudge_width-1
weights(idx + k) = 0.0_r_def
end do
@@ -104,7 +104,7 @@ subroutine nudging_weights_code(nlayers, &
weights(idx + k) = 1.0_r_def
end do
- do k = top_nudge_level-top_nudge_width, top_nudge_level+top_nudge_width
+ do k = top_nudge_level-top_nudge_width+1, top_nudge_level+top_nudge_width
weights(idx + k) = 1.0_r_def - real(k - (top_nudge_level - top_nudge_width), r_def) / &
real(MAX(1, 2*top_nudge_width), r_def)
end do
From 1ed52cedc1acea1542f503ea88ffd85e87d92031 Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Thu, 30 Jul 2026 21:02:10 +0100
Subject: [PATCH 18/35] rejig vertical nudging weights
---
.../lfric-gungho/HEAD/rose-meta.conf | 67 +++++++++++------
.../gungho/rose-meta/lfric-gungho/versions.py | 11 +++
.../physics_constants_mod.X90 | 10 +--
.../nudging_weights_kernel_mod.F90 | 75 +++++++++++--------
.../nudging_weights_kernel_mod_test.pf | 56 +++++++++-----
5 files changed, 143 insertions(+), 76 deletions(-)
diff --git a/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf b/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
index 0705044576..142da5a3ee 100644
--- a/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
+++ b/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
@@ -3955,7 +3955,7 @@ title=Nudging
compulsory=true
description=Number of levels for the Nudging reference data
help=The number of levels i.e. Z-dimension for the T,U,V reference fields
- =to be read from the Nudging input files
+ =to be read from the Nudging input files.
!kind=default
ns=namelist/Science/Nudging
sort-key=Panel-A01b
@@ -3963,19 +3963,53 @@ type=integer
[namelist:nudging=nudging_level_bottom]
compulsory=true
-description=Bottom level to apply nudging from
+description=Bottom level to apply full-strength nudging from
fail-if=this < 0;
-help=
+fail-if=this > namelist:nudging=nudging_level_top;
+help=Applies to both W3 and Wtheta fields. Wtheta fields are counted from 0 at
+ =the surface, while W3 fields are counted from 1/2 in the lowest level.
!kind=default
ns=namelist/Science/Nudging
sort-key=Panel-A05a
type=integer
+[namelist:nudging=nudging_level_taper_bottom]
+compulsory=true
+description=Level above which to ramp up nudging from 0 to full strength
+fail-if=this < 0;
+fail-if=this > namelist:nudging=nudging_level_bottom;
+help=Above this level, nudging is tapered up to nudging_level_bottom.
+ =At this level and below, nudging is 0, unless this is equal to
+ =nudging_level_bottom.
+ =Applies to both W3 and Wtheta fields. Wtheta fields are counted from 0 at
+ =the surface, while W3 fields are counted from 1/2 in the lowest level.
+!kind=default
+ns=namelist/Science/Nudging
+sort-key=Panel-A05c
+type=integer
+
+[namelist:nudging=nudging_level_taper_top]
+compulsory=true
+description=Level below which nudging is tapered
+fail-if=this < 0;
+fail-if=this < namelist:nudging=nudging_level_top;
+help=Below this level, nudging is tapered from 1 at nudging_level_top to 0
+ =at nudging_level_taper_top. At this level and above, nudging is 0, unless
+ =this is equal to nudging_level_top.
+ =Applies to both W3 and Wtheta fields. Wtheta fields are counted from 0 at
+ =the surface, while W3 fields are counted from 1/2 in the lowest level.
+!kind=default
+ns=namelist/Science/Nudging
+sort-key=Panel-A05d
+type=integer
+
[namelist:nudging=nudging_level_top]
compulsory=true
-description=Top level to apply nudging from
+description=Top level to apply full-strength nudging to
fail-if=this > namelist:extrusion=number_of_layers;
-help=
+fail-if=this < namelist:nudging=nudging_level_bottom;
+help=Applies to both W3 and Wtheta fields. Wtheta fields are counted from 0 at
+ =the surface, while W3 fields are counted from 1/2 in the lowest level.
!kind=default
ns=namelist/Science/Nudging
sort-key=Panel-A05b
@@ -4038,27 +4072,10 @@ ns=namelist/Science/Nudging
sort-key=Panel-A04a
type=real
-[namelist:nudging=nudging_width_bottom]
-compulsory=true
-description=Width of tapering of nudging at the bottom of the nudging region
-help=
-!kind=default
-ns=namelist/Science/Nudging
-sort-key=Panel-A05c
-type=integer
-
-[namelist:nudging=nudging_width_top]
-compulsory=true
-description=Width of tapering of nudging at the top of the nudging region
-help=
-!kind=default
-ns=namelist/Science/Nudging
-sort-key=Panel-A05d
-type=integer
-
[namelist:nudging=spectral_envelope_width]
compulsory=true
description=Width of Gaussian envelope to apply to the spectral nudging filter
+fail-if=this <= 0.0;
help=The filter used by the spectral nudging scheme is the product of the
=sum of Legendre polynomials with a Gaussian envelope. Since the convolution
=is performed on a finite stencil, it is possible for some wavenumbers to
@@ -4073,6 +4090,8 @@ type=real
[namelist:nudging=spectral_kmax]
compulsory=true
description=Maximum wavenumber to use in spectral nudging filter
+fail-if=this < 0;
+fail-if=this < namelist:nudging=spectral_kmin;
help=The spectral nudging scheme uses a convolution to nudge parts of the
=spectrum towards a specified reference state. The convolution replicates
=a low-pass top-hat filter in spectral space. This parameter sets the
@@ -4085,6 +4104,7 @@ type=integer
[namelist:nudging=spectral_kmin]
compulsory=true
description=Minimum wavenumber to use in spectral nudging filter
+fail-if=this < 0;
help=The spectral nudging scheme uses a convolution to nudge parts of the
=spectrum towards a specified reference state. The convolution replicates
=a low-pass top-hat filter in spectral space. This parameter sets the
@@ -4097,6 +4117,7 @@ type=integer
[namelist:nudging=spectral_stencil_extent]
compulsory=true
description=Sets the stencil extent to be used in spectral nudging
+fail-if=this < 1;
help=The spectral nudging scheme uses a convolution to nudge parts of the
=spectrum towards a specified reference state. This parameter sets the
=stencil extent to be used in the convolution. Note that if this is not
diff --git a/science/gungho/rose-meta/lfric-gungho/versions.py b/science/gungho/rose-meta/lfric-gungho/versions.py
index 5964d7516f..ec4ad8d359 100644
--- a/science/gungho/rose-meta/lfric-gungho/versions.py
+++ b/science/gungho/rose-meta/lfric-gungho/versions.py
@@ -51,6 +51,15 @@ def upgrade(self, config, meta_config=None):
self.add_setting(
config, ["namelist:nudging", "nudging_spinup_end"], "864000.0"
)
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "864000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "1"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "54"
+ )
self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
self.add_setting(
@@ -61,6 +70,8 @@ def upgrade(self, config, meta_config=None):
)
# Remove retired setting
self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_bottom"])
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
# If nudging_mesh_name is still the default '' value, set it to
# match dynamics_mesh_name
nudging_mesh_name = self.get_setting_value(
diff --git a/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90 b/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
index f324e1d141..93e171c4f6 100644
--- a/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
+++ b/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
@@ -668,15 +668,15 @@ contains
! Configuration variables
integer(kind=i_def) :: nudging_level_bottom, nudging_level_top
- integer(kind=i_def) :: nudging_width_bottom, nudging_width_top
+ integer(kind=i_def) :: nudging_taper_bottom, nudging_taper_top
real(kind=r_def) :: nudging_spinup_start, nudging_spinup_end
real(kind=r_def) :: nudging_relax_time
! Get nudging configuration variables
nudging_level_bottom = config%nudging%nudging_level_bottom()
nudging_level_top = config%nudging%nudging_level_top()
- nudging_width_bottom = config%nudging%nudging_width_bottom()
- nudging_width_top = config%nudging%nudging_width_top()
+ nudging_taper_bottom = config%nudging%nudging_taper_bottom()
+ nudging_taper_top = config%nudging%nudging_taper_top()
nudging_spinup_start = config%nudging%nudging_spinup_start()
nudging_spinup_end = config%nudging%nudging_spinup_end()
nudging_relax_time = config%nudging%nudging_relax_time()
@@ -731,7 +731,7 @@ contains
call invoke( &
nudging_weights_kernel_type( &
nudging_weights, nudging_level_bottom, nudging_level_top, &
- nudging_width_bottom, nudging_width_top &
+ nudging_taper_bottom, nudging_taper_top &
), &
inc_a_times_X(nudging_spinup_ratio, nudging_weights) &
)
@@ -742,7 +742,7 @@ contains
call invoke( &
nudging_weights_kernel_type( &
nudging_weights, nudging_level_bottom, nudging_level_top, &
- nudging_width_bottom, nudging_width_top &
+ nudging_taper_bottom, nudging_taper_top &
), &
inc_a_times_X(nudging_spinup_ratio, nudging_weights) &
)
diff --git a/science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90 b/science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90
index 47291cc7b7..b848844fb9 100644
--- a/science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90
+++ b/science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90
@@ -7,8 +7,6 @@
!> @details This kernel computes the height-dependent weights used in spectral
!! nudging, to allow the nudging to be targeted at certain vertical
!! heights in the atmosphere.
-!! The weights are currently ramped linearly from 0 at the surface to
-!! 2km, and then held constant above this height
!! Only implemented for the lowest-order elements
module nudging_weights_kernel_mod
@@ -16,9 +14,9 @@ module nudging_weights_kernel_mod
GH_FIELD, GH_SCALAR, &
GH_REAL, GH_INTEGER, &
GH_READ, GH_WRITE, &
+ ANY_DISCONTINUOUS_SPACE_1, &
CELL_COLUMN
- use constants_mod, only: r_def, i_def
- use fs_continuity_mod, only: W3
+ use constants_mod, only: r_def, i_def, EPS
use kernel_mod, only: kernel_type
implicit none
@@ -33,7 +31,7 @@ module nudging_weights_kernel_mod
type, public, extends(kernel_type) :: nudging_weights_kernel_type
private
type(arg_type) :: meta_args(5) = (/ &
- arg_type(GH_FIELD, GH_REAL, GH_WRITE, W3), &
+ arg_type(GH_FIELD, GH_REAL, GH_WRITE, ANY_DISCONTINUOUS_SPACE_1), &
arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
@@ -54,10 +52,12 @@ module nudging_weights_kernel_mod
!> @brief Computes weights for scaling the spectral nudging increment.
!> @param[in] nlayers Number of layers
!> @param[in,out] weights The weights to be computed
-!> @param[in] bottom_nudge_level Level below which nudging is turned off
-!> @param[in] top_nudge_level Level above which nudging is turned off
-!> @param[in] bottom_nudge_width Width of the ramp at the bottom
-!> @param[in] top_nudge_width Width of the ramp at the top
+!> @param[in] bottom_nudge_level Level at and above which nudging is full
+!! strength
+!> @param[in] top_nudge_level Level at and below which nudging is full
+!! strength
+!> @param[in] taper_bottom_level Level at and below which nudging is 0
+!> @param[in] taper_top_level Level at and above which nudging is 0
!> @param[in] ndf Num DoFs per cell
!> @param[in] undf Num DoFs in this partition
!> @param[in] map DoFmap for this column
@@ -65,8 +65,8 @@ subroutine nudging_weights_code(nlayers, &
weights, &
bottom_nudge_level, &
top_nudge_level, &
- bottom_nudge_width, &
- top_nudge_width, &
+ taper_bottom_level, &
+ taper_top_level, &
ndf, &
undf, &
map)
@@ -80,37 +80,52 @@ subroutine nudging_weights_code(nlayers, &
integer(kind=i_def), intent(in) :: map(ndf)
integer(kind=i_def), intent(in) :: bottom_nudge_level
integer(kind=i_def), intent(in) :: top_nudge_level
- integer(kind=i_def), intent(in) :: bottom_nudge_width
- integer(kind=i_def), intent(in) :: top_nudge_width
+ integer(kind=i_def), intent(in) :: taper_bottom_level
+ integer(kind=i_def), intent(in) :: taper_top_level
real(kind=r_def), intent(inout) :: weights(undf)
! Local variables
integer(kind=i_def) :: k, idx, nl
+ real(kind=r_def) :: k_real, w3_offset
+ real(kind=r_def) :: taper_bottom, nudging_bottom, nudging_top, taper_top
- nl = nlayers - 2 + ndf ! nlayers for Wtheta, nlayers - 1 for W3
+ nl = nlayers + ndf - 2 ! nlayers for W3, nlayers+1 for Wtheta
+ w3_offset = 0.5_r_def * real(2 - ndf, r_def) ! 0.5 for W3, 0.0 for Wtheta
idx = map(1)
- do k = 0, bottom_nudge_level-bottom_nudge_width-1
- weights(idx + k) = 0.0_r_def
- end do
+ ! Convert levels to reals for comparison with k_real
+ taper_bottom = real(taper_bottom_level, r_def)
+ nudging_bottom = real(bottom_nudge_level, r_def)
+ nudging_top = real(top_nudge_level, r_def)
+ taper_top = real(taper_top_level, r_def)
- do k = bottom_nudge_level-bottom_nudge_width, bottom_nudge_level+bottom_nudge_width
- weights(idx + k) = real(k - (bottom_nudge_level - bottom_nudge_width), r_def) / &
- real(MAX(1, 2*bottom_nudge_width), r_def)
- end do
+ do k = 0, nl
+ k_real = real(k, r_def) + w3_offset
- do k = bottom_nudge_level+bottom_nudge_width, top_nudge_level-top_nudge_width-1
- weights(idx + k) = 1.0_r_def
- end do
+ ! Below start of taper, weights = 0
+ if (k_real < taper_bottom + EPS) then
+ weights(idx + k) = 0.0_r_def
- do k = top_nudge_level-top_nudge_width+1, top_nudge_level+top_nudge_width
- weights(idx + k) = 1.0_r_def - real(k - (top_nudge_level - top_nudge_width), r_def) / &
- real(MAX(1, 2*top_nudge_width), r_def)
- end do
+ ! Ramp up from 0 to 1 between taper_bottom and nudging_bottom
+ else if (k_real < nudging_bottom + EPS) then
+ weights(idx + k) = (k_real - taper_bottom) / &
+ max(1.0_r_def, nudging_bottom - taper_bottom)
+
+ ! Full nudging between nudging_bottom and nudging_top
+ else if (k_real < nudging_top + EPS) then
+ weights(idx + k) = 1.0_r_def
+
+ ! Ramp down from 1 to 0 between nudging_top and taper_top
+ else if (k_real < taper_top + EPS) then
+ weights(idx + k) = 1.0_r_def - (k_real - nudging_top) / &
+ max(1.0_r_def, taper_top - nudging_top)
+
+ ! Above taper_top, weights = 0
+ else
+ weights(idx + k) = 0.0_r_def
- do k = top_nudge_level+top_nudge_width+1, nl
- weights(idx + k) = 0.0_r_def
+ end if
end do
end subroutine nudging_weights_code
diff --git a/science/gungho/unit-test/kernel/external_forcing/nudging_weights_kernel_mod_test.pf b/science/gungho/unit-test/kernel/external_forcing/nudging_weights_kernel_mod_test.pf
index ad9ab153f2..4bcedb7b6f 100644
--- a/science/gungho/unit-test/kernel/external_forcing/nudging_weights_kernel_mod_test.pf
+++ b/science/gungho/unit-test/kernel/external_forcing/nudging_weights_kernel_mod_test.pf
@@ -24,31 +24,51 @@ contains
implicit none
- integer(kind=i_def), parameter :: nlayers = 10
- integer(kind=i_def), parameter :: ndf = 1
- integer(kind=i_def), parameter :: undf = nlayers
+ integer(kind=i_def), parameter :: nlayers = 20
+ integer(kind=i_def), parameter :: ndf_w3 = 1
+ integer(kind=i_def), parameter :: undf_w3 = nlayers
+ integer(kind=i_def), parameter :: ndf_wt = 2
+ integer(kind=i_def), parameter :: undf_wt = nlayers + 1
real(kind=r_def), parameter :: tol = 10.0_r_def*epsilon(1.0_r_def)
- integer(kind=i_def) :: map(ndf)
- real(kind=r_def) :: answer(undf)
- real(kind=r_def) :: weights(undf)
- integer(kind=i_def) :: bottom_level, top_level, bottom_width, top_width
+ integer(kind=i_def) :: map_w3(ndf_w3), map_wt(ndf_wt)
+ real(kind=r_def) :: answer_w3(undf_w3), answer_wt(undf_wt)
+ real(kind=r_def) :: weights_w3(undf_w3), weights_wt(undf_wt)
+ integer(kind=i_def) :: bottom_level, top_level, bottom_taper, top_taper
- map(1) = 1
- bottom_level = 2
- top_level = 7
- bottom_width = 1
- top_width = 2
- answer = (/ 0.0_r_def, 0.0_r_def, 0.5_r_def, 1.0_r_def, 1.0_r_def, &
- 1.0_r_def, 0.75_r_def, 0.5_r_def, 0.25_r_def, 0.0_r_def /)
- weights(:) = 0.0_r_def
+ map_w3(1) = 1
+ map_wt(1) = 1
+ map_wt(2) = 2
+ bottom_taper = 2
+ bottom_level = 3
+ top_level = 16
+ top_taper = 18
+ answer_w3 = (/ 0.0_r_def, 0.0_r_def, 0.5_r_def, 1.0_r_def, 1.0_r_def, &
+ 1.0_r_def, 1.0_r_def, 1.0_r_def, 1.0_r_def, 1.0_r_def, &
+ 1.0_r_def, 1.0_r_def, 1.0_r_def, 1.0_r_def, 1.0_r_def, &
+ 1.0_r_def, 0.75_r_def, 0.25_r_def, 0.0_r_def, 0.0_r_def /)
+ answer_wt = (/ 0.0_r_def, 0.0_r_def, 0.0_r_def, 1.0_r_def, 1.0_r_def, &
+ 1.0_r_def, 1.0_r_def, 1.0_r_def, 1.0_r_def, 1.0_r_def, &
+ 1.0_r_def, 1.0_r_def, 1.0_r_def, 1.0_r_def, 1.0_r_def, &
+ 1.0_r_def, 1.0_r_def, 0.5_r_def, 0.0_r_def, 0.0_r_def, &
+ 0.0_r_def /)
+ weights_w3(:) = 100.0_r_def
call nudging_weights_code( &
- nlayers, weights, bottom_level, top_level, bottom_width, top_width, &
- ndf, undf, map &
+ nlayers, weights_w3, bottom_level, top_level, bottom_taper, top_taper, &
+ ndf_w3, undf_w3, map_w3 &
)
+ weights_wt(:) = 100.0_r_def
+ call nudging_weights_code( &
+ nlayers, weights_wt, bottom_level, top_level, bottom_taper, top_taper, &
+ ndf_wt, undf_wt, map_wt &
+ )
+
+ write(*,*) 'weights_w3 = ', weights_w3
+ write(*,*) 'weights_wt = ', weights_wt
- @assertEqual(answer, weights, tol)
+ @assertEqual(answer_w3, weights_w3, tol)
+ @assertEqual(answer_wt, weights_wt, tol)
end subroutine test_all
From dc7d83bb82713866369fda837830d0f202fc39bc Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Thu, 30 Jul 2026 21:10:52 +0100
Subject: [PATCH 19/35] fix fail-ifs
---
.../gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf b/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
index 142da5a3ee..949f6b21da 100644
--- a/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
+++ b/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
@@ -3965,7 +3965,7 @@ type=integer
compulsory=true
description=Bottom level to apply full-strength nudging from
fail-if=this < 0;
-fail-if=this > namelist:nudging=nudging_level_top;
+ =this > namelist:nudging=nudging_level_top;
help=Applies to both W3 and Wtheta fields. Wtheta fields are counted from 0 at
=the surface, while W3 fields are counted from 1/2 in the lowest level.
!kind=default
@@ -3977,7 +3977,7 @@ type=integer
compulsory=true
description=Level above which to ramp up nudging from 0 to full strength
fail-if=this < 0;
-fail-if=this > namelist:nudging=nudging_level_bottom;
+ =this > namelist:nudging=nudging_level_bottom;
help=Above this level, nudging is tapered up to nudging_level_bottom.
=At this level and below, nudging is 0, unless this is equal to
=nudging_level_bottom.
@@ -3992,7 +3992,7 @@ type=integer
compulsory=true
description=Level below which nudging is tapered
fail-if=this < 0;
-fail-if=this < namelist:nudging=nudging_level_top;
+ =this < namelist:nudging=nudging_level_top;
help=Below this level, nudging is tapered from 1 at nudging_level_top to 0
=at nudging_level_taper_top. At this level and above, nudging is 0, unless
=this is equal to nudging_level_top.
@@ -4007,7 +4007,7 @@ type=integer
compulsory=true
description=Top level to apply full-strength nudging to
fail-if=this > namelist:extrusion=number_of_layers;
-fail-if=this < namelist:nudging=nudging_level_bottom;
+ =this < namelist:nudging=nudging_level_bottom;
help=Applies to both W3 and Wtheta fields. Wtheta fields are counted from 0 at
=the surface, while W3 fields are counted from 1/2 in the lowest level.
!kind=default
@@ -4091,7 +4091,7 @@ type=real
compulsory=true
description=Maximum wavenumber to use in spectral nudging filter
fail-if=this < 0;
-fail-if=this < namelist:nudging=spectral_kmin;
+ =this < namelist:nudging=spectral_kmin;
help=The spectral nudging scheme uses a convolution to nudge parts of the
=spectrum towards a specified reference state. The convolution replicates
=a low-pass top-hat filter in spectral space. This parameter sets the
From d637e704046cbfcd4bf2a0d3eca54dacdb65e2af Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Fri, 31 Jul 2026 09:28:18 +0100
Subject: [PATCH 20/35] (hopefully) final renaming of some namelist variables
and fix a couple of compilation issues
---
.../lfric-gungho/HEAD/rose-meta.conf | 20 +++++++++----------
.../gungho/rose-meta/lfric-gungho/versions.py | 6 +++++-
.../algorithm/physics/nudging_alg_mod.x90 | 8 +++-----
.../physics_constants_mod.X90 | 14 ++++++-------
.../multidata_field_dimensions_mod.F90 | 4 ++--
5 files changed, 27 insertions(+), 25 deletions(-)
diff --git a/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf b/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
index 949f6b21da..4b5bb4f83c 100644
--- a/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
+++ b/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
@@ -3951,16 +3951,6 @@ ns=namelist/Science/Nudging
sort-key=Section-A14
title=Nudging
-[namelist:nudging=nudge_data_levels]
-compulsory=true
-description=Number of levels for the Nudging reference data
-help=The number of levels i.e. Z-dimension for the T,U,V reference fields
- =to be read from the Nudging input files.
-!kind=default
-ns=namelist/Science/Nudging
-sort-key=Panel-A01b
-type=integer
-
[namelist:nudging=nudging_level_bottom]
compulsory=true
description=Bottom level to apply full-strength nudging from
@@ -4072,6 +4062,16 @@ ns=namelist/Science/Nudging
sort-key=Panel-A04a
type=real
+[namelist:nudging=num_ref_data_levels]
+compulsory=true
+description=Number of levels for the Nudging reference data
+help=The number of levels i.e. Z-dimension for the T,U,V reference fields
+ =to be read from the Nudging input files.
+!kind=default
+ns=namelist/Science/Nudging
+sort-key=Panel-A01b
+type=integer
+
[namelist:nudging=spectral_envelope_width]
compulsory=true
description=Width of Gaussian envelope to apply to the spectral nudging filter
diff --git a/science/gungho/rose-meta/lfric-gungho/versions.py b/science/gungho/rose-meta/lfric-gungho/versions.py
index ec4ad8d359..bd63f1ebe1 100644
--- a/science/gungho/rose-meta/lfric-gungho/versions.py
+++ b/science/gungho/rose-meta/lfric-gungho/versions.py
@@ -57,6 +57,9 @@ def upgrade(self, config, meta_config=None):
self.add_setting(
config, ["namelist:nudging", "nudging_level_taper_bottom"], "1"
)
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
self.add_setting(
config, ["namelist:nudging", "nudging_level_taper_top"], "54"
)
@@ -68,10 +71,11 @@ def upgrade(self, config, meta_config=None):
self.add_setting(
config, ["namelist:nudging", "spectral_envelope_width"], "12"
)
- # Remove retired setting
+ # Remove retired settings
self.remove_setting(config, ["namelist:nudging", "nudging_source"])
self.remove_setting(config, ["namelist:nudging", "nudging_width_bottom"])
self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
# If nudging_mesh_name is still the default '' value, set it to
# match dynamics_mesh_name
nudging_mesh_name = self.get_setting_value(
diff --git a/science/gungho/source/algorithm/physics/nudging_alg_mod.x90 b/science/gungho/source/algorithm/physics/nudging_alg_mod.x90
index 71346fcf52..d029117560 100644
--- a/science/gungho/source/algorithm/physics/nudging_alg_mod.x90
+++ b/science/gungho/source/algorithm/physics/nudging_alg_mod.x90
@@ -72,7 +72,6 @@ contains
! Config variables
logical(kind=l_def) :: coarse_nudging, use_multires_coupling
integer(kind=i_def) :: nudging_method, spectral_stencil_extent
- integer(kind=i_def) :: nudge_data_levels
integer(kind=i_def) :: theta_forcing, wind_forcing
character(len=str_def) :: nudging_mesh_name
real(kind=r_def) :: p_zero, kappa
@@ -86,7 +85,6 @@ contains
use_multires_coupling = config%formulation%use_multires_coupling()
nudging_method = config%nudging%nudging_method()
spectral_stencil_extent = config%nudging%spectral_stencil_extent()
- nudge_data_levels = config%nudging%nudge_data_levels()
theta_forcing = config%external_forcing%theta_forcing()
wind_forcing = config%external_forcing%wind_forcing()
nudging_mesh_name = config%multires_coupling%nudging_mesh_name()
@@ -223,14 +221,14 @@ contains
! Config variables
integer(kind=i_def) :: theta_forcing, wind_forcing
- integer(kind=i_def) :: nudge_data_levels
+ integer(kind=i_def) :: num_ref_data_levels
real(kind=r_def) :: p_zero, kappa
logical(kind=l_def) :: coarse_nudging
! Get config variables
theta_forcing = config%external_forcing%theta_forcing()
wind_forcing = config%external_forcing%wind_forcing()
- nudge_data_levels = config%nudging%nudge_data_levels()
+ num_ref_data_levels = config%nudging%num_ref_data_levels()
p_zero = config%planet%p_zero()
kappa = config%planet%kappa()
coarse_nudging = config%multires_coupling%coarse_nudging()
@@ -284,7 +282,7 @@ contains
theta_ref, u_in_w3_ref, v_in_w3_ref, &
T_nudging_ext_ref, u_nudging_ext_ref, v_nudging_ext_ref, &
exner_in_wth, exner_in_w3, surfp_nudging_ext_ref, &
- p_zero, kappa, nudge_data_levels &
+ p_zero, kappa, num_ref_data_levels &
) &
)
diff --git a/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90 b/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
index 93e171c4f6..7004d54d67 100644
--- a/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
+++ b/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
@@ -518,8 +518,8 @@ contains
character(len=str_def) :: inventory_name
integer(kind=i_def) :: nudging_level_top
integer(kind=i_def) :: nudging_level_bottom
- integer(kind=i_def) :: nudging_width_top
- integer(kind=i_def) :: nudging_width_bottom
+ integer(kind=i_def) :: nudging_taper_top
+ integer(kind=i_def) :: nudging_taper_bottom
integer(kind=tik) :: id
if (fs_id == W3) then
@@ -544,8 +544,8 @@ contains
constant_exists = nudging_weights_inventory%paired_object_exists(mesh_id)
nudging_level_bottom = config%nudging%nudging_level_bottom()
nudging_level_top = config%nudging%nudging_level_top()
- nudging_width_bottom = config%nudging%nudging_width_bottom()
- nudging_width_top = config%nudging%nudging_width_top()
+ nudging_taper_bottom = config%nudging%nudging_level_taper_bottom()
+ nudging_taper_top = config%nudging%nudging_level_taper_top()
! Create object if it doesn't already exist
if (.not. constant_exists) then
@@ -558,7 +558,7 @@ contains
call invoke( &
nudging_weights_kernel_type( &
nudging_weights, nudging_level_bottom, nudging_level_top, &
- nudging_width_bottom, nudging_width_top &
+ nudging_taper_bottom, nudging_taper_top &
) &
)
@@ -675,8 +675,8 @@ contains
! Get nudging configuration variables
nudging_level_bottom = config%nudging%nudging_level_bottom()
nudging_level_top = config%nudging%nudging_level_top()
- nudging_taper_bottom = config%nudging%nudging_taper_bottom()
- nudging_taper_top = config%nudging%nudging_taper_top()
+ nudging_taper_bottom = config%nudging%nudging_level_taper_bottom()
+ nudging_taper_top = config%nudging%nudging_level_taper_top()
nudging_spinup_start = config%nudging%nudging_spinup_start()
nudging_spinup_end = config%nudging%nudging_spinup_end()
nudging_relax_time = config%nudging%nudging_relax_time()
diff --git a/science/gungho/source/physics/multidata_field_dimensions_mod.F90 b/science/gungho/source/physics/multidata_field_dimensions_mod.F90
index c9b6f96df6..df80fea040 100644
--- a/science/gungho/source/physics/multidata_field_dimensions_mod.F90
+++ b/science/gungho/source/physics/multidata_field_dimensions_mod.F90
@@ -168,7 +168,7 @@ function get_multidata_field_dimension(multidata_item) result (dim)
use aerosol_config_mod, only: l_radaer
use chemistry_config_mod, only: chem_scheme, chem_scheme_strattrop
use um_ukca_init_mod, only: n_phot_spc
- use nudging_config_mod, only: nudge_data_levels
+ use nudging_config_mod, only: num_ref_data_levels
#endif
use log_mod, only: log_event, LOG_LEVEL_ERROR, &
@@ -280,7 +280,7 @@ function get_multidata_field_dimension(multidata_item) result (dim)
dim = 1
end if
case ('nudging_levels')
- dim = nudge_data_levels
+ dim = num_ref_data_levels
case ('')
dim = 1 ! ordinary (non-multidata) field
#endif
From b71d223986d111362b8eebab7a0716be592a4d69 Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Fri, 31 Jul 2026 09:35:21 +0100
Subject: [PATCH 21/35] adjust default and optional namelist values
---
.../opt/rose-app-nudging-newtonian-coarse.conf | 3 +++
.../app/lfric_atm/opt/rose-app-nudging-newtonian.conf | 3 +++
.../opt/rose-app-nudging-spectral-coarse.conf | 3 +++
.../app/lfric_atm/opt/rose-app-nudging-spectral.conf | 3 +++
rose-stem/app/lfric_atm/rose-app.conf | 4 ++--
science/gungho/rose-meta/lfric-gungho/versions.py | 11 ++++-------
6 files changed, 18 insertions(+), 9 deletions(-)
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian-coarse.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian-coarse.conf
index 68f8c5ef52..dabbbfe011 100644
--- a/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian-coarse.conf
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian-coarse.conf
@@ -25,6 +25,9 @@ physics_mesh_name='dynamics'
[namelist:nudging]
nudging_method='newtonian'
+nudging_relax_time=3600.0
+nudging_spinup_start=3600.0
+nudging_spinup_end=7200.0
[namelist:section_choice]
external_forcing=.true.
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf
index 6cd596e829..d70f1ad4d2 100644
--- a/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf
@@ -22,6 +22,9 @@ physics_mesh_name='dynamics'
[namelist:nudging]
nudging_method='newtonian'
+nudging_relax_time=3600.0
+nudging_spinup_start=3600.0
+nudging_spinup_end=7200.0
[namelist:section_choice]
external_forcing=.true.
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf
index 9d4644405d..8e12a10bb6 100644
--- a/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf
@@ -24,6 +24,9 @@ orography_mesh_name='dynamics'
physics_mesh_name='dynamics'
[namelist:nudging]
+nudging_relax_time=3600.0
+nudging_spinup_start=3600.0
+nudging_spinup_end=7200.0
[namelist:section_choice]
external_forcing=.true.
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf
index b6352184bc..26bd67f584 100644
--- a/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf
@@ -21,6 +21,9 @@ orography_mesh_name='dynamics'
physics_mesh_name='dynamics'
[namelist:nudging]
+nudging_relax_time=3600.0
+nudging_spinup_start=3600.0
+nudging_spinup_end=7200.0
[namelist:section_choice]
external_forcing=.true.
diff --git a/rose-stem/app/lfric_atm/rose-app.conf b/rose-stem/app/lfric_atm/rose-app.conf
index f49ab7d81e..96659d23df 100644
--- a/rose-stem/app/lfric_atm/rose-app.conf
+++ b/rose-stem/app/lfric_atm/rose-app.conf
@@ -1457,8 +1457,8 @@ nudge_data_levels=137
nudging_level_bottom=5
nudging_level_top=52
nudging_method='convolution'
-nudging_spinup_start=3600.0
-nudging_spinup_end=7200.0
+nudging_spinup_start=43200.0
+nudging_spinup_end=86400.0
nudging_width_bottom=1
nudging_width_top=0
spectral_kmax=10
diff --git a/science/gungho/rose-meta/lfric-gungho/versions.py b/science/gungho/rose-meta/lfric-gungho/versions.py
index bd63f1ebe1..e66f89fc40 100644
--- a/science/gungho/rose-meta/lfric-gungho/versions.py
+++ b/science/gungho/rose-meta/lfric-gungho/versions.py
@@ -46,23 +46,20 @@ def upgrade(self, config, meta_config=None):
config, ["namelist:nudging", "nudging_relax_time"], "21600.0"
)
self.add_setting(
- config, ["namelist:nudging", "nudging_spinup_start"], "432000.0"
+ config, ["namelist:nudging", "nudging_spinup_start"], "43200.0"
)
self.add_setting(
- config, ["namelist:nudging", "nudging_spinup_end"], "864000.0"
+ config, ["namelist:nudging", "nudging_spinup_end"], "86400.0"
)
self.add_setting(
- config, ["namelist:nudging", "nudging_spinup_end"], "864000.0"
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "4"
)
self.add_setting(
- config, ["namelist:nudging", "nudging_level_taper_bottom"], "1"
+ config, ["namelist:nudging", "nudging_level_taper_top"], "54"
)
self.add_setting(
config, ["namelist:nudging", "num_ref_data_levels"], "137"
)
- self.add_setting(
- config, ["namelist:nudging", "nudging_level_taper_top"], "54"
- )
self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
self.add_setting(
From bcd8fe968c6075b3aa3b87b93d533fde5ce1a53e Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Fri, 31 Jul 2026 12:11:54 +0100
Subject: [PATCH 22/35] adjust envelope width
---
science/gungho/rose-meta/lfric-gungho/versions.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/science/gungho/rose-meta/lfric-gungho/versions.py b/science/gungho/rose-meta/lfric-gungho/versions.py
index e66f89fc40..83e389d43c 100644
--- a/science/gungho/rose-meta/lfric-gungho/versions.py
+++ b/science/gungho/rose-meta/lfric-gungho/versions.py
@@ -66,7 +66,7 @@ def upgrade(self, config, meta_config=None):
config, ["namelist:nudging", "spectral_stencil_extent"], "12"
)
self.add_setting(
- config, ["namelist:nudging", "spectral_envelope_width"], "12"
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
)
# Remove retired settings
self.remove_setting(config, ["namelist:nudging", "nudging_source"])
From e5a24307575b7d91542c3641b8c3db6a32a0efa2 Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Fri, 31 Jul 2026 16:54:02 +0100
Subject: [PATCH 23/35] add documentation
---
.../science_guide/nudging/convolution.rst | 193 ++
.../science_guide/nudging/formulation.rst | 101 +
.../source/science_guide/nudging/index.rst | 36 +
.../legendre_envelope_convolution_filter.svg | 1759 +++++++++++++++++
.../science_guide/nudging/multiresolution.rst | 64 +
.../source/science_guide/nudging/overview.rst | 49 +
.../nudging/vertical_treatment.rst | 115 ++
documentation/source/user_guide/index.rst | 1 +
.../source/user_guide/nudging/index.rst | 185 ++
9 files changed, 2503 insertions(+)
create mode 100644 documentation/source/science_guide/nudging/convolution.rst
create mode 100644 documentation/source/science_guide/nudging/formulation.rst
create mode 100644 documentation/source/science_guide/nudging/index.rst
create mode 100644 documentation/source/science_guide/nudging/legendre_envelope_convolution_filter.svg
create mode 100644 documentation/source/science_guide/nudging/multiresolution.rst
create mode 100644 documentation/source/science_guide/nudging/overview.rst
create mode 100644 documentation/source/science_guide/nudging/vertical_treatment.rst
create mode 100644 documentation/source/user_guide/nudging/index.rst
diff --git a/documentation/source/science_guide/nudging/convolution.rst b/documentation/source/science_guide/nudging/convolution.rst
new file mode 100644
index 0000000000..c35110195c
--- /dev/null
+++ b/documentation/source/science_guide/nudging/convolution.rst
@@ -0,0 +1,193 @@
+.. -----------------------------------------------------------------------------
+ (c) Crown copyright Met Office. All rights reserved.
+ The file LICENCE, distributed with this code, contains details of the terms
+ under which the code may be used.
+ -----------------------------------------------------------------------------
+.. _nudging_science_convolution:
+
+Filtering by Convolution
+========================
+
+The convolution method (see :ref:`nudging_science_formulation_convolution`)
+applies a scale-selective filter :math:`\mathcal{L}` to the difference
+between the model field and the reference field, so that only the large
+scales are nudged. This section explains why the filter is applied as a
+convolution in physical space, and derives the convolution kernel used on
+the sphere.
+
+Consider filtering a field :math:`f` so that only its large scales are
+retained. Writing the spectrum of :math:`f` as :math:`\mathcal{F}[f]`,
+where :math:`\mathcal{F}` denotes a transform to spectral space, the filter
+:math:`\mathcal{L}` acts as a multiplication of the spectrum by a function
+:math:`\ell` that varies with wavenumber:
+
+.. math:: :label: eq:nudging_spectral_filter
+
+ \mathcal{L}[f] = \mathcal{F}^{-1}\left[ \ell \; \mathcal{F}[f] \right] .
+
+It is vital that :math:`\ell` does not amplify any wavenumbers, which will
+cause the numerical model to go unstable. However, it is not always
+necessary for an analytic form of :math:`\ell` to be preserved
+exactly by the discretisation. For example, an idealised "top-hat" may be
+specified for :math:`\ell`, but it may be acceptable for the discretisation to
+only approximate this.
+
+Why a convolution?
+------------------
+
+The most direct way to apply :eq:`eq:nudging_spectral_filter` would be to
+transform :math:`f` to spectral space, multiply by :math:`\ell`, and
+transform back. However, for a parallel implementation in which :math:`f`
+is distributed geographically across many processors, this is very
+inefficient: the transform requires global gather and scatter
+communications and/or global summations, whose cost grows exponentially
+with the number of processors.
+
+An alternative is to note the convolution theorem: the spectrum of a
+convolved field is the product of the spectra of the field and of the
+convolving function. Defining the convolution of :math:`f` with a function
+:math:`c` as
+
+.. math:: :label: eq:nudging_convolution_def
+
+ \mathcal{C}[f, c] := \int_\Omega f(\boldsymbol{y}) \; c(\boldsymbol{y} - \boldsymbol{x}) \; \mathrm{d}\boldsymbol{y} \equiv f \ast c ,
+
+the convolution theorem gives
+:math:`\mathcal{F}\!\left[\mathcal{C}[f, c]\right] = \mathcal{F}[f] \,
+\mathcal{F}[c]`. Since the filter :eq:`eq:nudging_spectral_filter` acts as a
+multiplication in spectral space, it can therefore be applied as a
+convolution in physical space, provided the convolving function is chosen so
+that :math:`\mathcal{F}[c] = \ell`, i.e. :math:`c = \mathcal{F}^{-1}[\ell]`.
+
+The key advantage is that, if :math:`c` has compact support, the integral in
+:eq:`eq:nudging_convolution_def` reduces to an integral over a local
+neighbourhood of each point. On a distributed mesh this requires only
+communication of neighbouring data through *halo exchanges*, rather than the
+global communications demanded by a spectral transform.
+
+In practice the convolution is truncated by restricting it to a local
+stencil of finite extent. The truncated kernel is no longer exactly equal to
+:math:`\mathcal{F}^{-1}[\ell]`, but can remain a good approximation provided
+the truncation occurs where :math:`c` is already small. When the nudging of
+the spectrum does not need to be exact, this can be acceptable.
+
+The filter on the sphere
+------------------------
+
+To apply this approach on the cubed-sphere, the convolution kernel must be
+derived for the sphere. The cubed-sphere is non-orthogonal and has
+discontinuities at panel edges, which makes a separable
+longitude--latitude Fourier filter challenging. Instead, the field is
+expanded in spherical harmonics, which are appropriate for quasi-uniform
+meshes of the sphere. This use of a convolution for scale-selective
+filtering on a cubed-sphere follows the approach of Thatcher and McGregor
+(2009).
+
+The spherical harmonics, as functions of co-latitude :math:`\theta` and
+longitude :math:`\varphi`, may be written as
+
+.. math:: :label: eq:nudging_spherical_harmonic
+
+ Y_{lm}(\theta, \varphi) = N_{lm} \, e^{i m \varphi} \, P_{lm}(\cos\theta) ,
+
+where :math:`P_{lm}` are the associated Legendre polynomials and
+:math:`N_{lm}` is a normalisation chosen so that the :math:`Y_{lm}` are
+orthonormal over the sphere. A field :math:`f` is expanded as
+:math:`f(\theta,\varphi) = \sum_{l,m} a_{lm} Y_{lm}(\theta,\varphi)`.
+
+We seek the convolution kernel :math:`c` that retains only the spherical
+harmonic components with total wavenumber between :math:`k_{min}` and
+:math:`k_{max}`. Since the spherical harmonics form an orthonormal basis,
+this filter kernel can be written
+
+.. math:: :label: eq:nudging_general_kernel
+
+ c(\theta,\varphi,\theta',\varphi') =
+ \sum_{l=k_{min}}^{k_{max}} \sum_{m=-l}^{l}
+ F_{lm} \, Y_{lm}(\theta,\varphi) \, Y_{lm}^{\ast}(\theta',\varphi') .
+
+The kernel should depend only on the distance between two points, and not on
+their orientation. This requires that there is no dependence on :math:`m` or
+:math:`\varphi`, so that :math:`F_{lm} = F_{l0}`. The sum over :math:`m` can
+then be carried out using the spherical harmonic addition theorem,
+
+.. math:: :label: eq:nudging_addition_theorem
+
+ \sum_{m=-l}^{l} Y_{lm}(\theta,\varphi) \, Y_{lm}^{\ast}(\theta',\varphi')
+ = \frac{2l+1}{4\pi} \, P_l\!\left(\cos\gamma\right) ,
+
+where :math:`P_l` is the Legendre polynomial of degree :math:`l` and
+:math:`\gamma` is the great-circle (central) angle between the two points.
+Taking an idealised top-hat filter in spectral space, :math:`F_{l0} = 1`,
+the kernel becomes a weighted sum of Legendre polynomials of the central
+angle:
+
+.. math:: :label: eq:nudging_kernel
+
+ c(\gamma) = \sum_{l=k_{min}}^{k_{max}} \frac{2l+1}{4\pi} \, P_l(\cos\gamma) .
+
+The Legendre polynomials may be evaluated efficiently using the recurrence
+relation
+
+.. math:: :label: eq:nudging_legendre_recurrence
+
+ \begin{aligned}
+ P_0(\cos\gamma) &= 1 , \\
+ P_1(\cos\gamma) &= \cos\gamma , \\
+ P_l(\cos\gamma) &= \frac{2l-1}{l} \cos\gamma \, P_{l-1}(\cos\gamma)
+ - \frac{l-1}{l} \, P_{l-2}(\cos\gamma) .
+ \end{aligned}
+
+Equation :eq:`eq:nudging_kernel` is the kernel that corresponds exactly to
+the idealised top-hat spectral filter. It resembles a :math:`\mathrm{sinc}`
+function, (mainly!) decaying away from :math:`\gamma = 0`, and is evaluated at the
+points of the stencil surrounding each grid point to give the discrete
+convolution weights.
+
+Convolution Envelope
+--------------------
+
+However the series of Legendre polynomials does not strictly have compact support
+and extends over the whole sphere. An accurate convolution would therefore
+require a stencil covering the whole domain.
+
+Instead we truncate the filter to a local stencil. To avoid the spurious
+amplification of any wavenumbers, the filter is multiplied by a Gaussian
+envelope that decays to zero towards the edge of the stencil. The width of
+this envelope is a configurable parameter of the scheme, and must be chosen
+together with the stencil extent (see :ref:`nudging_user_index` for
+guidance).
+
+Thus the actual filter used is
+
+.. math:: :label: eq:nudging_enveloped_kernel
+
+ c(\gamma) = \left[ \sum_{l=k_{min}}^{k_{max}} \frac{2l+1}{4\pi}
+ P_l(\cos\gamma) \right]
+ \exp\left[ -\frac{1}{2} \left( \frac{\gamma}{\sigma} \right)^2 \right] ,
+
+where :math:`\gamma` is the great-circle angle from the central point and
+:math:`\sigma` is the width of the Gaussian envelope.
+
+:numref:`fig_nudging_envelope` illustrates the effect of the envelope. The
+underlying Legendre filter :eq:`eq:nudging_kernel` oscillates and decays only
+slowly with distance, so that it does not have compact support. Multiplying
+by the Gaussian envelope gives the enveloped filter, which decays smoothly to
+zero well within the stencil, allowing the convolution to be truncated to a
+local region with minimal error, provided the stencil is wide enough for the
+envelope to have decayed sufficiently by its edge.
+
+.. _fig_nudging_envelope:
+
+.. figure:: legendre_envelope_convolution_filter.svg
+ :width: 90%
+ :align: center
+
+ The spherical-harmonic convolution filter for a top-hat retaining total
+ wavenumbers from :math:`k_{min} = 2` to :math:`k_{max} = 20`, plotted
+ against great-circle distance. The underlying Legendre filter
+ (black dashed, :eq:`eq:nudging_kernel`) oscillates and decays only slowly,
+ whereas the enveloped filter (blue, :eq:`eq:nudging_enveloped_kernel`)
+ decays smoothly to zero. The red circles show the discrete filter values
+ sampled at the stencil points for a stencil of depth 12 on a C56 mesh.
+
diff --git a/documentation/source/science_guide/nudging/formulation.rst b/documentation/source/science_guide/nudging/formulation.rst
new file mode 100644
index 0000000000..eb199c79dd
--- /dev/null
+++ b/documentation/source/science_guide/nudging/formulation.rst
@@ -0,0 +1,101 @@
+.. -----------------------------------------------------------------------------
+ (c) Crown copyright Met Office. All rights reserved.
+ The file LICENCE, distributed with this code, contains details of the terms
+ under which the code may be used.
+ -----------------------------------------------------------------------------
+.. _nudging_science_formulation:
+
+Formulation
+===========
+
+Let :math:`X` denote a field being nudged (potential temperature
+:math:`\theta`, or one of the horizontal wind components :math:`u`, :math:`v`),
+and let :math:`X_{ref}` denote the corresponding reference field, having
+already been vertically interpolated onto the model's levels (see
+:ref:`nudging_science_vertical_treatment`). At each call to the nudging
+scheme, an increment :math:`\Delta X` is calculated and added to the
+model's tendency for :math:`X`.
+
+Two methods are available for calculating :math:`\Delta X`.
+
+.. _nudging_science_formulation_newtonian:
+
+Newtonian relaxation
+---------------------
+
+In Newtonian relaxation, the increment is simply proportional to the local
+difference between the field and its reference value:
+
+.. math:: :label: eq:nudging_newtonian
+
+ \Delta X = w \left( X_{ref} - X \right)
+
+where :math:`w` is a weight described in :ref:`nudging_science_weights`
+below. This relaxes :math:`X` directly towards :math:`X_{ref}` at every
+horizontal scale present in the reference data, with no spectral
+selectivity.
+
+.. _nudging_science_formulation_convolution:
+
+Convolution
+-----------
+
+In the convolution method, the local difference between the field and its
+reference value is first filtered so that only the large scales are
+retained, before being scaled by the same weight :math:`w`:
+
+.. math:: :label: eq:nudging_convolution
+
+ \Delta X = w \, \mathcal{L}\left[ X_{ref} - X \right]
+
+where :math:`\mathcal{L}` is a scale-selective filter that removes the
+short wavelengths. Rather than performing this filtering with a global
+spectral transform, :math:`\mathcal{L}` is applied as a convolution in
+physical space. The motivation for this choice, and the derivation of the
+convolution kernel on the sphere, are described in
+:ref:`nudging_science_convolution`.
+
+.. _nudging_science_weights:
+
+Nudging weights
+----------------
+
+In both of the above methods, the increment is scaled by a single weight
+:math:`w`, which combines a vertical tapering factor, a spin-up ramp, and a
+relaxation-timescale factor:
+
+.. math:: :label: eq:nudging_weight
+
+ w = w_{taper} \; r_{spinup} \; \min\left(1, \frac{\Delta t}{\tau}\right)
+
+where:
+
+* :math:`w_{taper}` is a height-dependent factor, computed as described in
+ :ref:`nudging_science_vertical_treatment`, that ramps the nudging on and
+ off over a chosen range of vertical levels;
+* :math:`r_{spinup}` is a factor that ramps linearly from 0 to 1 over a
+ configured spin-up period, described below;
+* :math:`\Delta t` is the model timestep and :math:`\tau` is a configured
+ relaxation timescale. If :math:`\tau` is set to zero, this factor is
+ instead set to 1, so that the field is relaxed fully towards the
+ reference state within a single timestep.
+
+Spin-up ramp
+~~~~~~~~~~~~
+
+The nudging weight can be ramped up gradually over a configurable spin-up
+period :math:`[t_{start}, t_{end}]`:, allowing forecasts over short time scales
+to be unperturbed.
+
+.. math:: :label: eq:nudging_spinup
+
+ r_{spinup} =
+ \begin{cases}
+ 0 & t \le t_{start} \\
+ \dfrac{t - t_{start}}{t_{end} - t_{start}} & t_{start} < t < t_{end} \\
+ 1 & t \ge t_{end}
+ \end{cases}
+
+where :math:`t` is the elapsed model time since the start of the run. If
+:math:`t_{start}` and :math:`t_{end}` are equal, the ramp is skipped and
+:math:`r_{spinup} = 1` once :math:`t` exceeds this value.
diff --git a/documentation/source/science_guide/nudging/index.rst b/documentation/source/science_guide/nudging/index.rst
new file mode 100644
index 0000000000..276128c66c
--- /dev/null
+++ b/documentation/source/science_guide/nudging/index.rst
@@ -0,0 +1,36 @@
+.. -----------------------------------------------------------------------------
+ (c) Crown copyright Met Office. All rights reserved.
+ The file LICENCE, distributed with this code, contains details of the terms
+ under which the code may be used.
+ -----------------------------------------------------------------------------
+.. _nudging_science_index:
+
+Nudging
+================
+
+Nudging is a technique for relaxing a free-running simulation towards an
+external reference state. It can be targeted at a chosen
+range of horizontal scales and a chosen range of vertical levels. This
+allows the large-scale state of the simulation to be kept close to the
+reference state, while still permitting the model to generate its own
+small-scale variability. Two approaches are available for this nudging:
+selecting only certain horizontal scales (known as spectral nudging), or
+relaxing the model state directly towards the reference state at every
+horizontal scale (known as Newtonian relaxation).
+
+This section describes the scientific formulation of nudging in GungHo. It
+begins with an overview of the method and the relaxation increment, before
+describing how the large scales can be selected using a convolution, how this
+may be computed efficiently on a coarse mesh, and finally how the reference
+data is prepared in the vertical. For details of how to configure and
+enable nudging, see the :ref:`nudging_user_index` section of the User
+Guide.
+
+.. toctree::
+ :maxdepth: 1
+
+ overview
+ formulation
+ convolution
+ multiresolution
+ vertical_treatment
diff --git a/documentation/source/science_guide/nudging/legendre_envelope_convolution_filter.svg b/documentation/source/science_guide/nudging/legendre_envelope_convolution_filter.svg
new file mode 100644
index 0000000000..e382c30198
--- /dev/null
+++ b/documentation/source/science_guide/nudging/legendre_envelope_convolution_filter.svg
@@ -0,0 +1,1759 @@
+
+
+
diff --git a/documentation/source/science_guide/nudging/multiresolution.rst b/documentation/source/science_guide/nudging/multiresolution.rst
new file mode 100644
index 0000000000..da86c81314
--- /dev/null
+++ b/documentation/source/science_guide/nudging/multiresolution.rst
@@ -0,0 +1,64 @@
+.. -----------------------------------------------------------------------------
+ (c) Crown copyright Met Office. All rights reserved.
+ The file LICENCE, distributed with this code, contains details of the terms
+ under which the code may be used.
+ -----------------------------------------------------------------------------
+.. _nudging_science_multiresolution:
+
+Multi-resolution Nudging
+==========================
+
+When the convolution method is used, the nudging increment can optionally be
+computed on a mesh that is coarser than the one used for the rest of the
+model. Since the nudging is usually desired only on large scales, this gives
+similar scientific behaviour but at considerably reduced cost..
+
+The convolution (see :ref:`nudging_science_convolution`) must span the range
+of scales that are being nudged. Nudging is typically applied at horizontal
+scales of the order of :math:`2000\,\text{km}`, which at a global
+NWP resolution corresponds to a stencil spanning many cells and, in a
+distributed implementation, many processor partitions. Computing the
+convolution directly on the model mesh would therefore require a very large
+halo depth and correspondingly large halo exchanges, which could be as
+expensive as the global spectral transforms the convolution is intended to
+avoid.
+
+The cost is greatly reduced by applying the convolution only to a
+coarse-grained form of the field. If the coarse mesh has of the order of a
+single cell per partition, then nudging over a given physical distance
+requires halo exchanges of only a modest depth, which is far more efficient.
+
+Coarse-grained convolution
+--------------------------
+
+To describe this, let :math:`\mathcal{A}` be a restriction operator that
+performs a local averaging to compute a coarse-grained field, and let
+:math:`\mathcal{R}` be the corresponding prolongation operator, which
+reconstructs a field on the fine mesh from a coarse-grained field using a
+linear reconstruction. The filter :math:`\mathcal{L}` of
+:eq:`eq:nudging_convolution` is then applied as
+
+.. math:: :label: eq:nudging_coarse_convolution
+
+ \mathcal{L}[X_{ref} - X] =
+ \mathcal{R}\Big[\, \overline{\mathcal{C}}\big[\,
+ \mathcal{A}[X_{ref} - X], \; \overline{c} \,\big] \Big] ,
+
+where :math:`\overline{\mathcal{C}}` denotes the convolution computed on the
+coarse mesh, and :math:`\overline{c}` is the corresponding coarse-mesh
+kernel. In words, the increment field is first restricted to the coarse
+mesh, the convolution is performed there, and the result is prolongated back
+onto the model's mesh, where it is applied to the model's tendency for
+:math:`X`.
+
+When coarse-mesh nudging is not used, no restriction or prolongation takes
+place, and the convolution is computed directly on the model's own mesh.
+
+The external reference fields used to compute the increment (see
+:ref:`nudging_science_vertical_treatment`) are themselves prepared on the
+coarse mesh when coarse-mesh nudging is used: in particular, the pressure
+fields used to construct the model pressure profile are first mapped onto
+the coarse mesh before being used in the vertical interpolation.
+
+For details of how to enable and configure coarse-mesh nudging, see the
+:ref:`nudging_user_index` section of the User Guide.
diff --git a/documentation/source/science_guide/nudging/overview.rst b/documentation/source/science_guide/nudging/overview.rst
new file mode 100644
index 0000000000..ccfa77d97f
--- /dev/null
+++ b/documentation/source/science_guide/nudging/overview.rst
@@ -0,0 +1,49 @@
+.. -----------------------------------------------------------------------------
+ (c) Crown copyright Met Office. All rights reserved.
+ The file LICENCE, distributed with this code, contains details of the terms
+ under which the code may be used.
+ -----------------------------------------------------------------------------
+.. _nudging_science_overview:
+
+Overview
+========
+
+Spectral nudging relaxes the potential temperature, :math:`\theta`, and the
+horizontal wind components, :math:`(u,v)`, towards reference fields derived
+from an external dataset (for example a reanalysis, or the driving model in
+a regional or limited-area configuration).
+
+At each application of the scheme, an increment is calculated for a given
+field :math:`X \in \{\theta, u, v\}` and added to the model's prognostic
+tendencies. Two methods are available for calculating this increment:
+
+* **Newtonian relaxation:** a pointwise relaxation of the field towards the
+ reference field, described in
+ :ref:`nudging_science_formulation_newtonian`.
+* **Convolution:** a physical-space convolution that emulates a spectral
+ low-pass (or band-pass) filter, so that only the large scales are nudged.
+ The rationale for this approach, and its derivation on the sphere, are
+ described in :ref:`nudging_science_convolution`.
+
+In both cases the increment is scaled by a set of weights which:
+
+* ramp the nudging increment smoothly up and down with height, targeting
+ nudging at a chosen range of vertical levels (see
+ :ref:`nudging_science_vertical_treatment`);
+* ramp the nudging on gradually at the start of a run, over a configurable
+ spin-up period;
+* scale the increment according to the model timestep and a configurable
+ relaxation timescale.
+
+These weights are described fully in :ref:`nudging_science_formulation`.
+
+The external reference data is supplied on its own set of pressure levels,
+and must be vertically interpolated onto the model's levels before it can be
+used; this process is described in
+:ref:`nudging_science_vertical_treatment`.
+
+Finally, when the convolution method is used, nudging can optionally be
+performed on a coarser mesh than the mesh used for the rest of the model,
+with the resulting increment prolongated back onto the model's mesh. This
+improves efficiency and is described in
+:ref:`nudging_science_multiresolution`.
diff --git a/documentation/source/science_guide/nudging/vertical_treatment.rst b/documentation/source/science_guide/nudging/vertical_treatment.rst
new file mode 100644
index 0000000000..da182c4c89
--- /dev/null
+++ b/documentation/source/science_guide/nudging/vertical_treatment.rst
@@ -0,0 +1,115 @@
+.. -----------------------------------------------------------------------------
+ (c) Crown copyright Met Office. All rights reserved.
+ The file LICENCE, distributed with this code, contains details of the terms
+ under which the code may be used.
+ -----------------------------------------------------------------------------
+.. _nudging_science_vertical_treatment:
+
+Vertical Treatment
+===================
+
+The external reference data used for nudging may be supplied on its own set of
+hybrid-pressure levels, which do not in general coincide with the model's
+levels. Before the reference fields can be used in the nudging increment,
+they must therefore be interpolated onto the model's levels, and the
+temperature field converted to potential temperature. A height-dependent
+weight is also computed, to allow nudging to be restricted to a chosen
+range of vertical levels.
+
+Reference pressure profile
+---------------------------
+
+The reference dataset's hybrid-pressure levels are defined by coefficients
+:math:`a_k`, :math:`b_k`. Given the reference surface pressure,
+:math:`p_{s,ref}`, the pressure on reference half-level :math:`k` is
+
+.. math:: :label: eq:nudging_ref_pressure
+
+ p_{ref,k} = a_k + b_k \, p_{s,ref} ,
+
+and full-level values are formed by averaging :math:`a_k` and :math:`b_k`
+between adjacent half levels.
+
+Model pressure profile
+------------------------
+
+The corresponding pressure on the model's levels is obtained from the
+model's Exner pressure, :math:`\Pi`, evaluated on both sets of model
+levels (those on which pressure is held, and those on which potential
+temperature is held):
+
+.. math:: :label: eq:nudging_model_pressure
+
+ p = p_0 \, \Pi^{1/\kappa}
+
+where :math:`p_0` is a reference pressure and
+:math:`\kappa = R_d / c_p`, with :math:`R_d` the gas constant for dry air
+and :math:`c_p` the specific heat capacity at constant pressure.
+
+Temperature to potential temperature
+--------------------------------------
+
+The reference dataset supplies temperature, :math:`T_{ref}`, rather than
+potential temperature. This is converted using the reference pressure
+profile computed above:
+
+.. math:: :label: eq:nudging_T_to_theta
+
+ \theta_{ref} = T_{ref} \left( \frac{p_0}{p_{ref}} \right)^{\kappa} .
+
+Vertical interpolation
+------------------------
+
+The reference fields (:math:`\theta_{ref}`, :math:`u_{ref}`,
+:math:`v_{ref}`) are interpolated from the reference pressure levels onto
+the model's pressure levels by linear interpolation in log-pressure:
+
+.. math:: :label: eq:nudging_log_p_interp
+
+ X_{ref}(p) = X_{ref,l} + \left( X_{ref,l+1} - X_{ref,l} \right)
+ \frac{\ln p - \ln p_{ref,l}}{\ln p_{ref,l+1} - \ln p_{ref,l}}
+
+where :math:`l` is the reference level immediately below the model level
+being interpolated to (in pressure). To avoid unbounded extrapolation
+above the top or below the bottom of the reference data, the interpolation
+factor is limited to the range :math:`[-1, 2]`. The lowest model
+:math:`\theta` level is set equal to the value interpolated for the level
+immediately above it.
+
+.. _nudging_science_vertical_taper:
+
+Vertical tapering
+-------------------
+
+To allow nudging to be targeted at a chosen range of vertical levels, a
+height-dependent weight, :math:`w_{taper}` (introduced in
+:eq:`eq:nudging_weight`), is computed for each function space
+(:math:`\mathbb{W}_3` and :math:`\mathbb{W}_\theta`). The weight ramps
+linearly from 0 to 1 between a lower taper level and a lower full-strength
+level, remains at 1 between the full-strength levels, and ramps back down
+to 0 between an upper full-strength level and an upper taper level:
+
+.. math:: :label: eq:nudging_vertical_taper
+
+ w_{taper}(\tilde{k}) =
+ \begin{cases}
+ 0 &
+ \tilde{k} \le k_{taper,bot} \\
+ \dfrac{\tilde{k} - k_{taper,bot}}{k_{bot} - k_{taper,bot}} &
+ k_{taper,bot} < \tilde{k} < k_{bot} \\
+ 1 &
+ k_{bot} \le \tilde{k} \le k_{top} \\
+ 1 - \dfrac{\tilde{k} - k_{top}}{k_{taper,top} - k_{top}} &
+ k_{top} < \tilde{k} < k_{taper,top} \\
+ 0 &
+ \tilde{k} \ge k_{taper,top}
+ \end{cases}
+
+where :math:`k_{bot}` and :math:`k_{top}` are the model levels marking the
+bottom and top of the region over which nudging is applied at full
+strength, and :math:`k_{taper,bot}` and :math:`k_{taper,top}` are the
+levels below and above which nudging is zero. The quantity
+:math:`\tilde{k}` is the :math:`\mathbb{W}_\theta` level index starting from 0,
+while :math:`\mathbb{W}_3` points are offset by half a level. Using this common
+coordinate ensures that both sets of levels see a consistent, vertically-aligned
+taper profile.
diff --git a/documentation/source/user_guide/index.rst b/documentation/source/user_guide/index.rst
index 91834ca41e..97cd4f3564 100644
--- a/documentation/source/user_guide/index.rst
+++ b/documentation/source/user_guide/index.rst
@@ -13,3 +13,4 @@ User Guide
applications/index
checkpointing/index
+ nudging/index
diff --git a/documentation/source/user_guide/nudging/index.rst b/documentation/source/user_guide/nudging/index.rst
new file mode 100644
index 0000000000..8cfc02106a
--- /dev/null
+++ b/documentation/source/user_guide/nudging/index.rst
@@ -0,0 +1,185 @@
+.. -----------------------------------------------------------------------------
+ (c) Crown copyright Met Office. All rights reserved.
+ The file LICENCE, distributed with this code, contains details of the terms
+ under which the code may be used.
+ -----------------------------------------------------------------------------
+
+.. _nudging_user_index:
+
+Nudging
+=======
+
+Nudging is a form of external forcing that relaxes model fields towards a
+specified reference state, typically taken from analysis or reanalysis data.
+It can be used, for example, to keep a forecast close to observed large-scale
+conditions, or to spin up a model state consistent with an external data
+source.
+
+A full mathematical description of the nudging scheme is given in the
+:ref:`Science Guide `. This page describes how to
+configure and use nudging.
+
+Enabling nudging
+----------------
+
+Nudging is enabled per-field via the ``namelist:external_forcing`` options
+``theta_forcing`` and ``wind_forcing``. Setting either (or both) of these to
+``'nudging'`` activates the nudging scheme for potential temperature or the
+horizontal wind components respectively:
+
+.. code-block:: ini
+
+ [namelist:external_forcing]
+ theta_forcing='nudging'
+ wind_forcing='nudging'
+
+The ``namelist:nudging`` namelist must then be configured as described
+below.
+
+Reference data files
+---------------------
+
+The external reference data used for nudging is read from a file specified
+in the ``namelist:files`` namelist:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 25 75
+
+ * - Option
+ - Description
+ * - ``nudging_directory``
+ - Path to the directory containing the nudging reference data file.
+ * - ``nudging_filename``
+ - Name of the file (within ``nudging_directory``) containing the
+ reference temperature, wind and surface pressure fields.
+
+Configuring the nudging scheme
+-------------------------------
+
+The behaviour of the nudging scheme is controlled by the
+``namelist:nudging`` namelist:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 25 75
+
+ * - Option
+ - Description
+ * - ``nudging_method``
+ - Selects the nudging algorithm: ``'newtonian'`` for pointwise
+ relaxation towards the reference state, or ``'convolution'`` to
+ relax only the large scales of the field, using a convolution in
+ physical space that emulates a low-pass top-hat filter in spectral
+ space (see the :ref:`Science Guide `).
+ * - ``nudging_relax_time``
+ - Timescale, in seconds, over which the field (or its large-scale
+ component) is relaxed towards the reference state. A value of zero
+ sets the field to the reference state in a single time step.
+ * - ``nudging_spinup_start`` / ``nudging_spinup_end``
+ - Times, in seconds since the start of the run, over which the
+ strength of nudging is ramped up linearly from zero to full
+ strength. This allows nudging to be introduced gradually rather
+ than as a step change.
+ * - ``num_ref_data_levels``
+ - Number of vertical levels on which the reference data for temperature
+ and wind components are provided.
+ * - ``nudging_level_bottom`` / ``nudging_level_top``
+ - Model levels defining the vertical extent over which nudging is
+ applied at full strength.
+ * - ``nudging_level_taper_bottom`` / ``nudging_level_taper_top``
+ - Model levels below/above which nudging is zero. Nudging is tapered
+ linearly between ``nudging_level_taper_bottom`` and
+ ``nudging_level_bottom``, and between ``nudging_level_top`` and
+ ``nudging_level_taper_top`` (see the :ref:`Science Guide
+ `).
+ * - ``spectral_kmin`` / ``spectral_kmax``
+ - (``nudging_method = 'convolution'`` only) Minimum and maximum
+ wavenumbers retained by the top-hat spectral filter.
+ * - ``spectral_stencil_extent``
+ - (``nudging_method = 'convolution'`` only) Extent of the stencil
+ used to construct the physical-space convolution.
+ * - ``spectral_envelope_width``
+ - (``nudging_method = 'convolution'`` only) Width, in radians, of the
+ Gaussian envelope applied to the convolution filter (see the
+ :ref:`Science Guide `).
+
+Choosing the stencil extent and envelope width
+-----------------------------------------------
+
+The ``spectral_stencil_extent`` and ``spectral_envelope_width`` options must
+be chosen together, and some care is needed:
+
+* The Legendre-polynomial filter that underlies the convolution does not
+ have compact support, so truncating it to a finite stencil is itself an
+ approximation. The Gaussian envelope is what makes this truncation
+ acceptable, by bringing the filter smoothly down to a small value by the
+ edge of the stencil.
+* If ``spectral_envelope_width`` is too large relative to
+ ``spectral_stencil_extent``, the filter has not decayed sufficiently by
+ the edge of the stencil. The abrupt truncation this causes can amplify,
+ rather than damp, some wavenumbers, leading to noise or numerical
+ instability rather than the intended scale-selective nudging.
+* Conversely, if ``spectral_envelope_width`` is too small, the envelope
+ suppresses part of the range of wavenumbers between
+ ``spectral_kmin`` and ``spectral_kmax`` that was intended to be retained,
+ making the filter less selective than requested.
+* Increasing ``spectral_stencil_extent`` allows a larger
+ ``spectral_envelope_width`` to be used safely, but at the cost of a wider
+ halo exchange and increased computational expense.
+
+Because of this trade-off, it is recommended to test any new combination of
+``spectral_kmin``, ``spectral_kmax``, ``spectral_stencil_extent`` and
+``spectral_envelope_width`` for a given mesh resolution, checking that the
+resulting weights decay smoothly across the stencil, before using it in
+a forecast or research configuration.
+
+Choosing the vertical extent of nudging
+-----------------------------------------
+
+The four vertical-level options must satisfy
+``nudging_level_taper_bottom`` :math:`\le` ``nudging_level_bottom``
+:math:`\le` ``nudging_level_top`` :math:`\le` ``nudging_level_taper_top``;
+this ordering is enforced automatically when the namelist is checked.
+Nudging is at full strength between ``nudging_level_bottom`` and
+``nudging_level_top``, tapers linearly to zero outside this range, and is
+exactly zero at and beyond ``nudging_level_taper_bottom`` /
+``nudging_level_taper_top``. Setting a taper level equal to the
+corresponding full-strength level removes the ramp on that side, giving an
+abrupt step instead of a gradual transition.
+
+Note that these levels are counted differently for the two sets of model
+levels that nudging acts on: levels holding potential temperature are counted
+from 0 at the surface, while levels holding density or the horizontal wind
+components are counted from 1/2 in the lowest level, since they are
+vertically staggered by half a level relative to the potential temperature
+levels (see the :ref:`Science Guide `). The
+same integer level values are used for both, so the two sets of levels see
+a consistent, vertically-aligned taper profile.
+
+Coarse-mesh nudging
+--------------------
+
+For efficiency, nudging may be computed on a mesh coarser than
+the one used by the dynamical core, with the resulting increment mapped
+back onto the dynamics mesh (see the :ref:`Science Guide
+`).
+
+Coarse-mesh nudging is controlled from the ``namelist:multires_coupling``
+namelist:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 25 75
+
+ * - Option
+ - Description
+ * - ``coarse_nudging``
+ - If ``.true.``, nudging increments are computed on a coarser mesh and
+ then mapped back onto the dynamics mesh. If ``.false.`` (the
+ default), nudging is computed directly on the dynamics mesh and no
+ coarse mesh needs to be specified.
+ * - ``nudging_mesh_name``
+ - Tag-name of the coarser mesh to use when ``coarse_nudging`` is
+ ``.true.``. This mesh must also be listed in
+ ``multires_coupling_mesh_tags``.
From b92176335e14e8213fc43bc6cb93eab0be05af58 Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Tue, 11 Aug 2026 12:50:23 +0100
Subject: [PATCH 24/35] big rewrite: reorganise options, vertical weights are
1D array, use tropopause
---
.../lfric_atm/metadata/field_def_diags.xml | 5 +
.../lfric_atm/metadata/lfric_dictionary.xml | 5 -
.../science_guide/nudging/convolution.rst | 3 +
.../science_guide/nudging/formulation.rst | 3 +
.../source/science_guide/nudging/index.rst | 3 +
.../science_guide/nudging/multiresolution.rst | 3 +
.../source/science_guide/nudging/overview.rst | 6 +-
.../nudging/vertical_treatment.rst | 33 +++
.../source/user_guide/nudging/index.rst | 47 +++-
.../rose-app-nudging-newtonian-coarse.conf | 6 +-
.../opt/rose-app-nudging-newtonian.conf | 6 +-
.../opt/rose-app-nudging-spectral-coarse.conf | 7 +-
.../opt/rose-app-nudging-spectral.conf | 7 +-
rose-stem/app/lfric_atm/rose-app.conf | 7 +-
.../lfric-gungho/HEAD/rose-meta.conf | 96 ++++++-
.../gungho/rose-meta/lfric-gungho/versions.py | 25 +-
.../physics/external_forcing_alg_mod.X90 | 72 ++++-
...udging_alg_mod.x90 => nudging_alg_mod.X90} | 206 +++++++++++---
.../physics_constants_mod.X90 | 260 +++++++-----------
.../source/driver/gungho_driver_mod.F90 | 14 -
.../convolution_2d_kernel_mod.F90 | 103 +++++--
.../newtonian_nudging_kernel_mod.F90 | 127 +++++++++
... nudging_ecmwf_vert_interp_kernel_mod.F90} | 18 +-
.../nudging_weights_kernel_mod.F90 | 133 ---------
science/gungho/source/psy/psykal_lite_mod.F90 | 195 +++++++++++++
.../convolution_2d_kernel_mod_test.pf | 36 ++-
.../newtonian_nudging_kernel_mod_test.pf | 92 +++++++
...ging_ecmwf_vert_interp_kernel_mod_test.pf} | 10 +-
.../nudging_weights_kernel_mod_test.pf | 75 -----
29 files changed, 1064 insertions(+), 539 deletions(-)
rename science/gungho/source/algorithm/physics/{nudging_alg_mod.x90 => nudging_alg_mod.X90} (57%)
create mode 100644 science/gungho/source/kernel/external_forcing/newtonian_nudging_kernel_mod.F90
rename science/gungho/source/kernel/external_forcing/{nudging_vert_interp_kernel_mod.F90 => nudging_ecmwf_vert_interp_kernel_mod.F90} (96%)
delete mode 100644 science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90
create mode 100644 science/gungho/unit-test/kernel/external_forcing/newtonian_nudging_kernel_mod_test.pf
rename science/gungho/unit-test/kernel/external_forcing/{nudging_vert_interp_kernel_mod_test.pf => nudging_ecmwf_vert_interp_kernel_mod_test.pf} (94%)
delete mode 100644 science/gungho/unit-test/kernel/external_forcing/nudging_weights_kernel_mod_test.pf
diff --git a/applications/lfric_atm/metadata/field_def_diags.xml b/applications/lfric_atm/metadata/field_def_diags.xml
index 9b01795782..76ba47758b 100644
--- a/applications/lfric_atm/metadata/field_def_diags.xml
+++ b/applications/lfric_atm/metadata/field_def_diags.xml
@@ -45,6 +45,11 @@
+
+
+
+
+
diff --git a/applications/lfric_atm/metadata/lfric_dictionary.xml b/applications/lfric_atm/metadata/lfric_dictionary.xml
index 272f51dde6..e64eab42a3 100644
--- a/applications/lfric_atm/metadata/lfric_dictionary.xml
+++ b/applications/lfric_atm/metadata/lfric_dictionary.xml
@@ -430,10 +430,5 @@
-
-
-
-
-
diff --git a/documentation/source/science_guide/nudging/convolution.rst b/documentation/source/science_guide/nudging/convolution.rst
index c35110195c..2a3dad92ba 100644
--- a/documentation/source/science_guide/nudging/convolution.rst
+++ b/documentation/source/science_guide/nudging/convolution.rst
@@ -2,6 +2,9 @@
(c) Crown copyright Met Office. All rights reserved.
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
+ Met Office GitHub Copilot Enterprise.
-----------------------------------------------------------------------------
.. _nudging_science_convolution:
diff --git a/documentation/source/science_guide/nudging/formulation.rst b/documentation/source/science_guide/nudging/formulation.rst
index eb199c79dd..db3800d591 100644
--- a/documentation/source/science_guide/nudging/formulation.rst
+++ b/documentation/source/science_guide/nudging/formulation.rst
@@ -2,6 +2,9 @@
(c) Crown copyright Met Office. All rights reserved.
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
+ Met Office GitHub Copilot Enterprise.
-----------------------------------------------------------------------------
.. _nudging_science_formulation:
diff --git a/documentation/source/science_guide/nudging/index.rst b/documentation/source/science_guide/nudging/index.rst
index 276128c66c..c27366b4f5 100644
--- a/documentation/source/science_guide/nudging/index.rst
+++ b/documentation/source/science_guide/nudging/index.rst
@@ -2,6 +2,9 @@
(c) Crown copyright Met Office. All rights reserved.
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
+ Met Office GitHub Copilot Enterprise.
-----------------------------------------------------------------------------
.. _nudging_science_index:
diff --git a/documentation/source/science_guide/nudging/multiresolution.rst b/documentation/source/science_guide/nudging/multiresolution.rst
index da86c81314..4739c79161 100644
--- a/documentation/source/science_guide/nudging/multiresolution.rst
+++ b/documentation/source/science_guide/nudging/multiresolution.rst
@@ -2,6 +2,9 @@
(c) Crown copyright Met Office. All rights reserved.
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
+ Met Office GitHub Copilot Enterprise.
-----------------------------------------------------------------------------
.. _nudging_science_multiresolution:
diff --git a/documentation/source/science_guide/nudging/overview.rst b/documentation/source/science_guide/nudging/overview.rst
index ccfa77d97f..87ceced994 100644
--- a/documentation/source/science_guide/nudging/overview.rst
+++ b/documentation/source/science_guide/nudging/overview.rst
@@ -2,6 +2,9 @@
(c) Crown copyright Met Office. All rights reserved.
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
+ Met Office GitHub Copilot Enterprise.
-----------------------------------------------------------------------------
.. _nudging_science_overview:
@@ -28,7 +31,8 @@ tendencies. Two methods are available for calculating this increment:
In both cases the increment is scaled by a set of weights which:
* ramp the nudging increment smoothly up and down with height, targeting
- nudging at a chosen range of vertical levels (see
+ nudging at a chosen range of vertical levels, optionally capped by the
+ model's diagnosed tropopause level (see
:ref:`nudging_science_vertical_treatment`);
* ramp the nudging on gradually at the start of a run, over a configurable
spin-up period;
diff --git a/documentation/source/science_guide/nudging/vertical_treatment.rst b/documentation/source/science_guide/nudging/vertical_treatment.rst
index da182c4c89..14c15ceb60 100644
--- a/documentation/source/science_guide/nudging/vertical_treatment.rst
+++ b/documentation/source/science_guide/nudging/vertical_treatment.rst
@@ -2,6 +2,9 @@
(c) Crown copyright Met Office. All rights reserved.
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
+ Met Office GitHub Copilot Enterprise.
-----------------------------------------------------------------------------
.. _nudging_science_vertical_treatment:
@@ -113,3 +116,33 @@ levels below and above which nudging is zero. The quantity
while :math:`\mathbb{W}_3` points are offset by half a level. Using this common
coordinate ensures that both sets of levels see a consistent, vertically-aligned
taper profile.
+
+.. _nudging_science_tropopause_cap:
+
+Tropopause capping
+-------------------
+
+Nudging towards external reference data is generally undesirable well above
+the tropopause: the stratosphere is only loosely constrained (if at all) by
+the coarse vertical and temporal sampling of typical reference datasets, and
+imposing large-scale forcing there can disrupt the model's own
+stratospheric transport and composition. Rather than relying solely on a
+fixed upper taper level, chosen in advance to sit safely below the
+tropopause for an expected range of conditions, the upper extent of the
+tapering profile described above may instead be capped dynamically by the
+model's own, evolving tropopause height.
+
+When this option is used, the upper full-strength and taper levels,
+:math:`k_{top}` and :math:`k_{taper,top}`, in :eq:`eq:nudging_vertical_taper`
+are replaced at each application of the scheme by the lesser of their
+configured values and the current tropopause level, diagnosed
+independently for each atmospheric column from the model's evolving
+temperature structure. This allows the vertical extent of nudging to track
+a rising or falling tropopause, rather than remaining fixed, and ensures
+nudging is switched off in the stratosphere even where the diagnosed
+tropopause sits below the configured upper taper level. A fixed minimum
+level may also be enforced as a floor for the diagnosed tropopause level,
+guarding against spuriously low diagnoses; setting this floor high enough
+disables the dynamic capping altogether, recovering the fixed taper profile
+described above.
+
diff --git a/documentation/source/user_guide/nudging/index.rst b/documentation/source/user_guide/nudging/index.rst
index 8cfc02106a..aea844160b 100644
--- a/documentation/source/user_guide/nudging/index.rst
+++ b/documentation/source/user_guide/nudging/index.rst
@@ -2,6 +2,9 @@
(c) Crown copyright Met Office. All rights reserved.
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
+ Met Office GitHub Copilot Enterprise.
-----------------------------------------------------------------------------
.. _nudging_user_index:
@@ -72,15 +75,20 @@ The behaviour of the nudging scheme is controlled by the
relax only the large scales of the field, using a convolution in
physical space that emulates a low-pass top-hat filter in spectral
space (see the :ref:`Science Guide `).
- * - ``nudging_relax_time``
- - Timescale, in seconds, over which the field (or its large-scale
- component) is relaxed towards the reference state. A value of zero
- sets the field to the reference state in a single time step.
+ * - ``nudging_relax_time_theta`` / ``nudging_relax_time_u`` /
+ ``nudging_relax_time_v``
+ - Timescale, in hours, over which potential temperature, zonal wind
+ and meridional wind respectively are relaxed towards the reference
+ state. Setting a timescale to zero disables nudging of that field
+ entirely.
* - ``nudging_spinup_start`` / ``nudging_spinup_end``
- - Times, in seconds since the start of the run, over which the
+ - Times, in hours since the start of the run, over which the
strength of nudging is ramped up linearly from zero to full
strength. This allows nudging to be introduced gradually rather
than as a step change.
+ * - ``nudging_stop_time``
+ - Time, in hours since the start of the run, at which nudging is
+ switched off.
* - ``num_ref_data_levels``
- Number of vertical levels on which the reference data for temperature
and wind components are provided.
@@ -93,6 +101,13 @@ The behaviour of the nudging scheme is controlled by the
``nudging_level_bottom``, and between ``nudging_level_top`` and
``nudging_level_taper_top`` (see the :ref:`Science Guide
`).
+ * - ``nudging_min_tropopause_level``
+ - Model level enforced as a floor for the tropopause level used to
+ cap the vertical extent of nudging (see the :ref:`Science Guide
+ `). Set to ``0`` to always cap
+ nudging using the model-diagnosed tropopause level. Set to
+ ``nudging_level_taper_top`` (or above) to always apply nudging up
+ to that level, regardless of the diagnosed tropopause.
* - ``spectral_kmin`` / ``spectral_kmax``
- (``nudging_method = 'convolution'`` only) Minimum and maximum
wavenumbers retained by the top-hat spectral filter.
@@ -157,6 +172,28 @@ levels (see the :ref:`Science Guide `). The
same integer level values are used for both, so the two sets of levels see
a consistent, vertically-aligned taper profile.
+Capping nudging at the tropopause
+------------------------------------
+
+The upper end of the vertical range configured above (``nudging_level_top``
+and ``nudging_level_taper_top``) can additionally be capped by the model's
+own diagnosed tropopause level, so that nudging is switched off in the
+stratosphere even where the tropopause is currently lower than these
+configured levels (see the :ref:`Science Guide
+`). This behaviour is controlled by
+``nudging_min_tropopause_level``, which sets a floor below which the
+diagnosed tropopause level is never allowed to cap nudging:
+
+* Setting ``nudging_min_tropopause_level`` to ``0`` means nudging always
+ follows the diagnosed tropopause level, wherever it is.
+* Setting ``nudging_min_tropopause_level`` to ``nudging_level_taper_top``
+ (or higher) disables the dynamic capping, so nudging always extends up
+ to the fixed levels configured above, regardless of the diagnosed
+ tropopause.
+* Intermediate values allow the diagnosed tropopause to lower the effective
+ top of the nudged region, but prevent it from doing so below the
+ specified floor.
+
Coarse-mesh nudging
--------------------
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian-coarse.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian-coarse.conf
index dabbbfe011..fac9c67439 100644
--- a/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian-coarse.conf
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian-coarse.conf
@@ -25,9 +25,9 @@ physics_mesh_name='dynamics'
[namelist:nudging]
nudging_method='newtonian'
-nudging_relax_time=3600.0
-nudging_spinup_start=3600.0
-nudging_spinup_end=7200.0
+nudging_relax_time=1.0
+nudging_spinup_start=1.0
+nudging_spinup_end=2.0
[namelist:section_choice]
external_forcing=.true.
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf
index d70f1ad4d2..c34a258192 100644
--- a/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf
@@ -22,9 +22,9 @@ physics_mesh_name='dynamics'
[namelist:nudging]
nudging_method='newtonian'
-nudging_relax_time=3600.0
-nudging_spinup_start=3600.0
-nudging_spinup_end=7200.0
+nudging_relax_time=1.0
+nudging_spinup_start=1.0
+nudging_spinup_end=2.0
[namelist:section_choice]
external_forcing=.true.
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf
index 8e12a10bb6..b708e9b1cb 100644
--- a/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf
@@ -24,9 +24,10 @@ orography_mesh_name='dynamics'
physics_mesh_name='dynamics'
[namelist:nudging]
-nudging_relax_time=3600.0
-nudging_spinup_start=3600.0
-nudging_spinup_end=7200.0
+nudging_relax_time=1.0
+nudging_spinup_start=1.0
+nudging_spinup_end=2.0
+spectral_stencil_extent=4
[namelist:section_choice]
external_forcing=.true.
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf
index 26bd67f584..be653daa3c 100644
--- a/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf
@@ -21,9 +21,10 @@ orography_mesh_name='dynamics'
physics_mesh_name='dynamics'
[namelist:nudging]
-nudging_relax_time=3600.0
-nudging_spinup_start=3600.0
-nudging_spinup_end=7200.0
+nudging_relax_time=1.0
+nudging_spinup_start=1.0
+nudging_spinup_end=2.0
+spectral_stencil_extent=6
[namelist:section_choice]
external_forcing=.true.
diff --git a/rose-stem/app/lfric_atm/rose-app.conf b/rose-stem/app/lfric_atm/rose-app.conf
index 96659d23df..1bcb9f8a6d 100644
--- a/rose-stem/app/lfric_atm/rose-app.conf
+++ b/rose-stem/app/lfric_atm/rose-app.conf
@@ -1456,14 +1456,9 @@ recovery_order='linear'
nudge_data_levels=137
nudging_level_bottom=5
nudging_level_top=52
-nudging_method='convolution'
-nudging_spinup_start=43200.0
-nudging_spinup_end=86400.0
+nudging_source='era'
nudging_width_bottom=1
nudging_width_top=0
-spectral_kmax=10
-spectral_kmin=0
-spectral_stencil_extent=4
[namelist:orbit]
!!arg_periapsis=1.796767421
diff --git a/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf b/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
index 4b5bb4f83c..cf6bcb4f5f 100644
--- a/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
+++ b/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
@@ -1053,6 +1053,7 @@ trigger=namelist:external_forcing=hs_random: this == "'held_suarez'" ;
=namelist:external_forcing=pc2_force_response: this != "'none'";
=namelist:nudging: this == "'nudging'";
=namelist:multires_coupling=coarse_nudging: this == "'nudging'";
+ =namelist:multires_coupling=nudging_mesh_name: this == "'nudging'";
=namelist:files=nudging_directory: this == "'nudging'";
=namelist:files=nudging_filename: this == "'nudging'";
value-titles=None, Held-Suarez, Earth-Like, Tidally Locked Earth,
@@ -1136,6 +1137,7 @@ trigger=namelist:external_forcing=filtering_order: this == "'filtering'";
=namelist:external_forcing=diffusion_coefficient: this == "'diffusion'";
=namelist:nudging: this == "'nudging'";
=namelist:multires_coupling=coarse_nudging: this == "'nudging'";
+ =namelist:multires_coupling=nudging_mesh_name: this == "'nudging'";
=namelist:files=nudging_directory: this == "'nudging'";
=namelist:files=nudging_filename: this == "'nudging'",
=namelist:wind_forcing: this == "'profile'";
@@ -4024,27 +4026,94 @@ trigger=namelist:nudging=spectral_stencil_extent: this == "'convolution'";
value-titles=Convolution, Newtonian
values='convolution', 'newtonian'
-[namelist:nudging=nudging_relax_time]
+[namelist:nudging=nudging_min_tropopause_level]
compulsory=true
-description=Time scale for nudging (seconds)
+description=Minimum level to enforce for tropopause, above which nudging is not
+ =applied. Controls whether to use tropopause as vertical cap of
+ =nudging.
+fail-if=this > namelist:extrusion=number_of_layers;
+help=Controls whether nudging is applied in the stratosphere, allowing the
+ =nudging to be applied to different vertical levels in different columns.
+ =Set to 0 to always cap nudging using the model-determined tropopause level.
+ =Set to nudging_level_taper_top (or above) to always apply nudging up to
+ =that level, regardless of the model-determined tropopause level.
+ =Applies to both W3 and Wtheta fields. Wtheta fields are counted from 0 at
+ =the surface, while W3 fields are counted from 1/2 in the lowest level.
+!kind=default
+ns=namelist/Science/Nudging
+sort-key=Panel-A05e
+type=integer
+
+[namelist:nudging=nudging_relax_time_theta]
+compulsory=true
+description=Time scale for nudging (hours) for potential temperature
fail-if=this < 0.0 ;
help=The nudging scheme applies an increment to nudge (parts of the
=spectrum of) a field towards a specified reference state. This parameter
- =gives the time scale (in seconds) after which the spectrum of the field
+ =gives the time scale (in hours) after which the spectrum of the field
=would match that of the reference field. A value of zero sets the (spectrum
=of the) field to that of the reference state in a single time step.
+ =This option controls potential temperature nudging, and can be set to zero
+ =to disable nudging of potential temperature.
!kind=default
ns=namelist/Science/Nudging
-sort-key=Panel-A03
+sort-key=Panel-A03a
type=real
+[namelist:nudging=nudging_relax_time_u]
+compulsory=true
+description=Time scale for nudging (hours) for zonal wind
+fail-if=this < 0.0 ;
+help=The nudging scheme applies an increment to nudge (parts of the
+ =spectrum of) a field towards a specified reference state. This parameter
+ =gives the time scale (in hours) after which the spectrum of the field
+ =would match that of the reference field. A value of zero sets the (spectrum
+ =of the) field to that of the reference state in a single time step.
+ =This option controls zonal wind nudging, and can be set to zero
+ =to disable nudging of zonal wind.
+!kind=default
+ns=namelist/Science/Nudging
+sort-key=Panel-A03b
+type=real
+
+[namelist:nudging=nudging_relax_time_v]
+compulsory=true
+description=Time scale for nudging (hours) for meridional wind
+fail-if=this < 0.0 ;
+help=The nudging scheme applies an increment to nudge (parts of the
+ =spectrum of) a field towards a specified reference state. This parameter
+ =gives the time scale (in hours) after which the spectrum of the field
+ =would match that of the reference field. A value of zero sets the (spectrum
+ =of the) field to that of the reference state in a single time step.
+ =This option controls meridional wind nudging, and can be set to zero
+ =to disable nudging of meridional wind.
+!kind=default
+ns=namelist/Science/Nudging
+sort-key=Panel-A03c
+type=real
+
+[namelist:nudging=nudging_source]
+compulsory=true
+description=The source of the reference states for nudging
+!enumeration=true
+help=This determines the processing necessary for the external data. For example
+ =the model could be nudged to ECMWF data on pressure levels (e.g. ERA
+ =re-analysis data or AIFS output), or to UM or LFRic re-analysis data on
+ =model levels.
+ =Only ECMWF data has currently been implemented.
+!kind=default
+ns=namelist/Science/Nudging
+sort-key=Panel-A01c
+value-titles=ECMWF
+values='ecmwf'
+
[namelist:nudging=nudging_spinup_end]
compulsory=true
-description=Time at which to end nudging spinup (seconds)
+description=Time at which to end nudging spinup (hours)
fail-if=this < 0.0 ;
help=This option allows the nudging to be ramped up linearly over some
=spin-up period. This parameter specifies the length of time after the start
- =of the model simulation to end the nudging spinup, in seconds.
+ =of the model simulation to end the nudging spinup, in hours.
!kind=default
ns=namelist/Science/Nudging
sort-key=Panel-A04b
@@ -4052,16 +4121,27 @@ type=real
[namelist:nudging=nudging_spinup_start]
compulsory=true
-description=Time at which to start nudging spinup (seconds)
+description=Time at which to start nudging spinup (hours)
fail-if=this < 0.0 ;
help=This option allows the nudging to be ramped up linearly over some
=spin-up period. This parameter specifies the length of time after the start
- =of the model simulation to begin the nudging spinup, in seconds.
+ =of the model simulation to begin the nudging spinup, in hours.
!kind=default
ns=namelist/Science/Nudging
sort-key=Panel-A04a
type=real
+[namelist:nudging=nudging_stop_time]
+compulsory=true
+description=Time at which to stop nudging (hours)
+fail-if=this < 0.0 ;
+help=This parameter specifies the length of time after the start
+ =of the model simulation to stop the nudging, in hours.
+!kind=default
+ns=namelist/Science/Nudging
+sort-key=Panel-A04c
+type=real
+
[namelist:nudging=num_ref_data_levels]
compulsory=true
description=Number of levels for the Nudging reference data
diff --git a/science/gungho/rose-meta/lfric-gungho/versions.py b/science/gungho/rose-meta/lfric-gungho/versions.py
index 83e389d43c..0bd6b94fd4 100644
--- a/science/gungho/rose-meta/lfric-gungho/versions.py
+++ b/science/gungho/rose-meta/lfric-gungho/versions.py
@@ -43,19 +43,34 @@ def upgrade(self, config, meta_config=None):
config, ["namelist:nudging", "nudging_method"], "'convolution'"
)
self.add_setting(
- config, ["namelist:nudging", "nudging_relax_time"], "21600.0"
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
)
self.add_setting(
- config, ["namelist:nudging", "nudging_spinup_start"], "43200.0"
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
)
self.add_setting(
- config, ["namelist:nudging", "nudging_spinup_end"], "86400.0"
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
)
self.add_setting(
- config, ["namelist:nudging", "nudging_level_taper_bottom"], "4"
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
)
self.add_setting(
- config, ["namelist:nudging", "nudging_level_taper_top"], "54"
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "144.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
)
self.add_setting(
config, ["namelist:nudging", "num_ref_data_levels"], "137"
diff --git a/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90 b/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
index c4bae2aaee..8116921c5f 100644
--- a/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
+++ b/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
@@ -28,7 +28,9 @@ module external_forcing_alg_mod
use field_parent_mod, only: write_interface
use nudging_alg_mod, only: nudging_alg, &
- set_nudging_reference_fields
+ set_nudging_reference_fields, &
+ set_nudging_trop_level, &
+ compute_nudging_spinup_factor
use map_fd_to_prognostics_alg_mod, only: set_wind
use held_suarez_fv_kernel_mod, only: held_suarez_fv_kernel_type
@@ -43,7 +45,7 @@ module external_forcing_alg_mod
#endif
! Derived Types
use config_mod, only: config_type
- use constants_mod, only: i_def, r_def, l_def, IMDI, RMDI
+ use constants_mod, only: i_def, r_def, l_def, IMDI, str_def
use field_mod, only: field_type
use field_collection_mod, only: field_collection_type
use integer_field_mod, only: integer_field_type
@@ -157,9 +159,12 @@ contains
type(mesh_type), pointer :: mesh
procedure(write_interface), pointer :: write_behaviour
+ type(integer_field_type) :: trop_level
+
type(field_type) :: u_smoothed, dx, visc_m
type(field_type) :: du_in_w3, dv_in_w3, dw_in_wth
integer(kind=i_def) :: order, nlayers
+ real(kind=r_def) :: nudging_time_weight
real(kind=r_def) :: diffusion_coefficient_scaled, dx_sum, dx_size
integer(tik) :: id
@@ -171,7 +176,8 @@ contains
integer(kind=i_def) :: filtering_order, diffusion_order
real(kind=r_def) :: diffusion_coefficient
real(kind=r_def) :: kappa
- real(kind=r_def) :: nudging_spinup_start
+ real(kind=r_def) :: nudging_spinup_factor
+ real(kind=r_def) :: nudging_relax_time
logical(kind=l_def) :: write_diag, use_xios_io
integer(kind=i_def) :: cloud, scheme
@@ -193,10 +199,8 @@ contains
kappa = config%planet%kappa()
cloud = IMDI
scheme = IMDI
- nudging_spinup_start = RMDI
if (section_choice_is_loaded()) cloud = config%section_choice%cloud()
if (cloud_is_loaded()) scheme = config%cloud%scheme()
- if (nudging_is_loaded()) nudging_spinup_start = config%nudging%nudging_spinup_start()
mesh => theta%get_mesh()
height_wth => get_height_fv(config, mesh, Wtheta)
@@ -205,6 +209,10 @@ contains
if ( theta_forcing == theta_forcing_nudging .or. &
wind_forcing == wind_forcing_nudging ) then
call set_nudging_reference_fields(config, derived_fields, exner)
+ nudging_spinup_factor = compute_nudging_spinup_factor(config, time_now)
+ if (nudging_spinup_factor > 0.0_r_def) then
+ call set_nudging_trop_level(config, trop_level, theta, exner_in_wth)
+ end if
end if
if ( theta_forcing /= theta_forcing_none .or. theta_relaxation .or. &
@@ -270,10 +278,21 @@ contains
call temp_tend_profile_alg( config, dtheta_forcing, exner_in_wth, time_now )
case(theta_forcing_nudging)
+ ! Determine nudging time-weighting for theta
+ nudging_relax_time = config%nudging%nudging_relax_time_theta()
+ if (nudging_relax_time > 0.0_r_def) then
+ nudging_time_weight = dt * nudging_spinup_factor / nudging_relax_time
+ else
+ nudging_time_weight = 0.0_r_def
+ end if
- if (time_now >= nudging_spinup_start) then
+ ! Perform nudging
+ if (nudging_time_weight > 0.0_r_def) then
call derived_fields%get_field('theta_nudging_ref', theta_ref)
- call nudging_alg(config, dtheta_forcing, theta, theta_ref)
+ call nudging_alg(config, dtheta_forcing, theta, theta_ref, &
+ nudging_time_weight, trop_level)
+ else
+ call invoke(setval_c(dtheta_forcing, 0.0_r_def))
end if
case default
@@ -409,12 +428,10 @@ contains
call du_forcing%initialise(u%get_function_space())
- if (time_now >= nudging_spinup_start) then
+ if (nudging_spinup_factor > 0.0_r_def) then
! Extract fields for spectral nudging
call derived_fields%get_field('u_in_w3', u_in_w3)
call derived_fields%get_field('v_in_w3', v_in_w3)
- call derived_fields%get_field('u_in_w3_nudging_ref', u_in_w3_ref)
- call derived_fields%get_field('v_in_w3_nudging_ref', v_in_w3_ref)
! Initialise components for wind increment
call du_in_w3%initialise(u_in_w3%get_function_space())
@@ -422,11 +439,40 @@ contains
call dw_in_wth%initialise(theta%get_function_space())
! Apply nudging to calculate increments for each component
- call nudging_alg(config, du_in_w3, u_in_w3, u_in_w3_ref)
- call nudging_alg(config, dv_in_w3, v_in_w3, v_in_w3_ref)
- call invoke(setval_c(dw_in_wth, 0.0_r_def))
+ ! u ----------------------------------------------------------------
+ nudging_relax_time = config%nudging%nudging_relax_time_u()
+ if (nudging_relax_time > 0.0_r_def) then
+ nudging_time_weight = dt * nudging_spinup_factor / nudging_relax_time
+ else
+ nudging_time_weight = 0.0_r_def
+ end if
+
+ if (nudging_time_weight > 0.0_r_def) then
+ call derived_fields%get_field('u_in_w3_nudging_ref', u_in_w3_ref)
+ call nudging_alg(config, du_in_w3, u_in_w3, u_in_w3_ref, &
+ nudging_time_weight, trop_level)
+ else
+ call invoke(setval_c(du_in_w3, 0.0_r_def))
+ end if
+
+ ! v ----------------------------------------------------------------
+ nudging_relax_time = config%nudging%nudging_relax_time_v()
+ if (nudging_relax_time > 0.0_r_def) then
+ nudging_time_weight = dt * nudging_spinup_factor / nudging_relax_time
+ else
+ nudging_time_weight = 0.0_r_def
+ end if
+
+ if (nudging_time_weight > 0.0_r_def) then
+ call derived_fields%get_field('v_in_w3_nudging_ref', v_in_w3_ref)
+ call nudging_alg(config, dv_in_w3, v_in_w3, v_in_w3_ref, &
+ nudging_time_weight, trop_level)
+ else
+ call invoke(setval_c(dv_in_w3, 0.0_r_def))
+ end if
! Combine increments to obtain full wind increment
+ call invoke(setval_c(dw_in_wth, 0.0_r_def))
call set_wind(config, du_forcing, du_in_w3, dv_in_w3, dw_in_wth)
else
diff --git a/science/gungho/source/algorithm/physics/nudging_alg_mod.x90 b/science/gungho/source/algorithm/physics/nudging_alg_mod.X90
similarity index 57%
rename from science/gungho/source/algorithm/physics/nudging_alg_mod.x90
rename to science/gungho/source/algorithm/physics/nudging_alg_mod.X90
index d029117560..913c4781c1 100644
--- a/science/gungho/source/algorithm/physics/nudging_alg_mod.x90
+++ b/science/gungho/source/algorithm/physics/nudging_alg_mod.X90
@@ -9,9 +9,13 @@ module nudging_alg_mod
! Infrastructure
use config_mod, only: config_type
use constants_mod, only: i_def, l_def, r_def, str_def
+ use extrusion_mod, only: TWOD
use field_mod, only: field_type
use field_collection_mod, only: field_collection_type
+ use fs_continuity_mod, only: W3
use function_space_mod, only: function_space_type
+ use function_space_collection_mod, only: function_space_collection
+ use integer_field_mod, only: integer_field_type
use log_mod, only: log_event, LOG_LEVEL_ERROR
use mesh_mod, only: mesh_type
use mesh_collection_mod, only: mesh_collection
@@ -26,7 +30,11 @@ module nudging_alg_mod
! Kernels
use convolution_2d_kernel_mod, only: convolution_2d_kernel_type
- use nudging_vert_interp_kernel_mod, only: nudging_vert_interp_kernel_type
+ use newtonian_nudging_kernel_mod, only: newtonian_nudging_kernel_type
+ use nudging_ecmwf_vert_interp_kernel_mod, &
+ only: nudging_ecmwf_vert_interp_kernel_type
+ use psykal_lite_mod, only: invoke_newtonian_nudging_kernel_type, &
+ invoke_convolution_2d_kernel_type
! Other algorithms
use intermesh_mappings_alg_mod, only: map_scalar_intermesh, &
@@ -40,6 +48,8 @@ module nudging_alg_mod
private
public :: nudging_alg
+ public :: compute_nudging_spinup_factor
+ public :: set_nudging_trop_level
public :: set_nudging_reference_fields
contains
@@ -50,29 +60,34 @@ contains
!> @param[in,out] increment Increment field to be computed
!> @param[in] field_in Input field to be nudged
!> @param[in] field_ref Reference field to nudge towards
- subroutine nudging_alg(config, increment, field_in, field_ref)
+ subroutine nudging_alg(config, increment, field_in, field_ref, &
+ time_weight, tropopause_level)
use multires_coupling_config_mod, only: recovery_order_linear
implicit none
- type(config_type), intent(in) :: config
- type(field_type), intent(inout) :: increment
- type(field_type), intent(in) :: field_in, field_ref
+ type(config_type), intent(in) :: config
+ type(field_type), intent(inout) :: increment
+ type(field_type), intent(in) :: field_in, field_ref
+ real(kind=r_def), intent(in) :: time_weight
+ type(integer_field_type), intent(in) :: tropopause_level
type(mesh_type), pointer :: mesh, coarse_mesh
type(field_type), pointer :: convolution_weights
- type(field_type), pointer :: nudging_weights
+ real(kind=r_def), pointer :: vertical_weights(:)
type(function_space_type), pointer :: fs
type(field_type) :: coarse_field_in, coarse_increment, coarse_field_diff
integer(kind=i_def) :: fs_id, coarse_mesh_id
+ integer(kind=i_def) :: nvert(1)
integer(kind=tik) :: id
! Config variables
logical(kind=l_def) :: coarse_nudging, use_multires_coupling
integer(kind=i_def) :: nudging_method, spectral_stencil_extent
integer(kind=i_def) :: theta_forcing, wind_forcing
+ integer(kind=i_def) :: min_level, max_level
character(len=str_def) :: nudging_mesh_name
real(kind=r_def) :: p_zero, kappa
@@ -90,6 +105,8 @@ contains
nudging_mesh_name = config%multires_coupling%nudging_mesh_name()
p_zero = config%planet%p_zero()
kappa = config%planet%kappa()
+ min_level = config%nudging%nudging_level_taper_bottom()
+ max_level = config%nudging%nudging_level_taper_top()
! Getting nudging mesh name
coarse_nudging = .false.
@@ -130,7 +147,8 @@ contains
! Point to weights used for nudging
fs_id = field_ref%which_function_space()
- nudging_weights => get_nudging_weights(config, coarse_mesh, fs_id)
+ vertical_weights => get_nudging_weights(config, coarse_mesh, fs_id)
+ nvert(:) = SIZE(vertical_weights)
! ------------------------------------------------------------------------ !
! Coarsen
@@ -150,8 +168,12 @@ contains
case (nudging_method_newtonian)
! Simply nudge towards reference field, with no spectral impact
call invoke( &
- X_minus_Y(coarse_increment, field_ref, coarse_field_in), &
- inc_X_times_Y(coarse_increment, nudging_weights) &
+ setval_c(coarse_increment, 0.0_r_def) &
+ )
+ call invoke_newtonian_nudging_kernel_type( &
+ coarse_increment, coarse_field_in, field_ref, &
+ vertical_weights, tropopause_level, &
+ min_level, max_level, time_weight, nvert &
)
! ------------------------------------------------------------------------ !
@@ -164,13 +186,12 @@ contains
call invoke( &
X_minus_Y(coarse_field_diff, field_ref, coarse_field_in), &
! Compute convolution
- setval_c(coarse_increment, 0.0_r_def), &
- convolution_2d_kernel_type( &
- coarse_increment, coarse_field_diff, &
- spectral_stencil_extent, convolution_weights &
- ), &
- ! Scale convolution increment by weights
- inc_X_times_Y(coarse_increment, nudging_weights) &
+ setval_c(coarse_increment, 0.0_r_def) &
+ )
+ call invoke_convolution_2d_kernel_type( &
+ coarse_increment, coarse_field_diff, spectral_stencil_extent, &
+ vertical_weights, convolution_weights, &
+ tropopause_level, min_level, max_level, time_weight, nvert &
)
end select
@@ -191,6 +212,94 @@ contains
end subroutine nudging_alg
+ !> @brief Computes a spinup factor for nudging based on the current time
+ !> @param[in] config Application namelist configuration object
+ !> @param[in] time Current model time in seconds
+ !> @return Spinup factor for nudging, between 0 and 1
+ function compute_nudging_spinup_factor(config, time) result(spinup_factor)
+
+ implicit none
+
+ type(config_type), intent(in) :: config
+ real(kind=r_def), intent(in) :: time
+
+ ! Local variables
+ real(kind=r_def) :: nudging_spinup_start
+ real(kind=r_def) :: nudging_spinup_end
+ real(kind=r_def) :: nudging_stop_time
+ real(kind=r_def) :: time_hours
+ real(kind=r_def) :: spinup_factor
+
+ nudging_spinup_start = config%nudging%nudging_spinup_start()
+ nudging_spinup_end = config%nudging%nudging_spinup_end()
+ nudging_stop_time = config%nudging%nudging_stop_time()
+
+ ! Convert time to hours
+ time_hours = time / 3600.0_r_def
+
+ if (time_hours < nudging_spinup_start) then
+ spinup_factor = 0.0_r_def
+ else if (time_hours < nudging_spinup_end) then
+ spinup_factor = ( &
+ (time_hours - nudging_spinup_start) &
+ / (nudging_spinup_end - nudging_spinup_start) &
+ )
+ else if (time_hours < nudging_stop_time) then
+ spinup_factor = 1.0_r_def
+ else
+ spinup_factor = 0.0_r_def
+ end if
+
+ end function compute_nudging_spinup_factor
+
+ !> @brief Sets the tropopause level for nudging based on current model state
+ !> @param[in] config Application namelist configuration object
+ !> @param[in,out] trop_level Level of tropopause
+ !> @param[in] theta Potential temperature
+ !> @param[in] exner Exner pressure field (in same space as theta)
+ subroutine set_nudging_trop_level(config, trop_level, theta, exner)
+
+#ifdef UM_PHYSICS
+ use locate_tropopause_alg_mod, only: locate_tropopause_alg
+#endif
+
+ implicit none
+
+ type(config_type), intent(in) :: config
+ type(integer_field_type), intent(inout) :: trop_level
+ type(field_type), intent(in) :: theta, exner
+
+ ! Local variables
+ integer(kind=i_def) :: nudging_min_tropopause_level
+ integer(kind=i_def) :: nudging_level_taper_top
+ character(str_def) :: nudging_mesh_name
+
+ type(mesh_type), pointer :: nudging_mesh
+ type(mesh_type), pointer :: nudging_twod_mesh
+ type(function_space_type), pointer :: w3_2d_fs
+
+ nudging_min_tropopause_level = config%nudging%nudging_min_tropopause_level()
+ nudging_level_taper_top = config%nudging%nudging_level_taper_top()
+
+ nudging_mesh_name = trim(config%multires_coupling%nudging_mesh_name())
+ nudging_mesh => mesh_collection%get_mesh(nudging_mesh_name)
+ nudging_twod_mesh => mesh_collection%get_mesh(nudging_mesh, TWOD)
+ w3_2d_fs => function_space_collection%get_fs(nudging_twod_mesh, 0, 0, W3)
+
+ call trop_level%initialise(w3_2d_fs)
+
+ if (nudging_min_tropopause_level < nudging_level_taper_top) then
+#ifdef UM_PHYSICS
+ call locate_tropopause_alg(config, trop_level, theta, exner, trop_diag=.false.)
+#else
+ call invoke( int_setval_c(trop_level, nudging_min_tropopause_level) )
+#endif
+ else
+ call invoke( int_setval_c(trop_level, nudging_min_tropopause_level) )
+ end if
+
+ end subroutine set_nudging_trop_level
+
!> @brief Sets the reference fields for nudging from external data
!> @param[in] config Application namelist configuration object
!> @param[in,out] derived_fields Collection of derived fields, which holds
@@ -199,6 +308,8 @@ contains
!> @param[in] exner Current Exner pressure field
subroutine set_nudging_reference_fields(config, derived_fields, exner)
+ use nudging_config_mod, only: nudging_source_ecmwf
+
implicit none
! Arguments
@@ -224,6 +335,7 @@ contains
integer(kind=i_def) :: num_ref_data_levels
real(kind=r_def) :: p_zero, kappa
logical(kind=l_def) :: coarse_nudging
+ integer(kind=i_def) :: nudging_source
! Get config variables
theta_forcing = config%external_forcing%theta_forcing()
@@ -232,6 +344,7 @@ contains
p_zero = config%planet%p_zero()
kappa = config%planet%kappa()
coarse_nudging = config%multires_coupling%coarse_nudging()
+ nudging_source = config%nudging%nudging_source()
! Get target reference fields from derived field collection
if (theta_forcing == theta_forcing_nudging) then
@@ -255,37 +368,44 @@ contains
)
end if
- ! Get external fields
- call derived_fields%get_field('temperature_nudging_ext_ref', T_nudging_ext_ref)
- call derived_fields%get_field('u_nudging_ext_ref', u_nudging_ext_ref)
- call derived_fields%get_field('v_nudging_ext_ref', v_nudging_ext_ref)
- call derived_fields%get_field('exner_in_wth', exner_in_wth)
- call derived_fields%get_field( &
- 'surface_pressure_nudging_ext_ref', surfp_nudging_ext_ref &
- )
+ if (nudging_source == nudging_source_ecmwf) then
+ ! Get external fields
+ call derived_fields%get_field('temperature_nudging_ext_ref', T_nudging_ext_ref)
+ call derived_fields%get_field('u_nudging_ext_ref', u_nudging_ext_ref)
+ call derived_fields%get_field('v_nudging_ext_ref', v_nudging_ext_ref)
+ call derived_fields%get_field('exner_in_wth', exner_in_wth)
+ call derived_fields%get_field( &
+ 'surface_pressure_nudging_ext_ref', surfp_nudging_ext_ref &
+ )
- ! Map Exner fields to coarse mesh, if required
- if (coarse_nudging) then
- call exner_in_wth_coarse%initialise(theta_ref%get_function_space())
- call exner_in_w3_coarse%initialise(u_in_w3_ref%get_function_space())
- call map_scalar_intermesh(exner_in_wth_coarse, exner_in_wth)
- call map_scalar_intermesh(exner_in_w3_coarse, exner)
- exner_in_wth => exner_in_wth_coarse
- exner_in_w3 => exner_in_w3_coarse
+ ! Map Exner fields to coarse mesh, if required
+ if (coarse_nudging) then
+ call exner_in_wth_coarse%initialise(theta_ref%get_function_space())
+ call exner_in_w3_coarse%initialise(u_in_w3_ref%get_function_space())
+ call map_scalar_intermesh(exner_in_wth_coarse, exner_in_wth)
+ call map_scalar_intermesh(exner_in_w3_coarse, exner)
+ exner_in_wth => exner_in_wth_coarse
+ exner_in_w3 => exner_in_w3_coarse
+ else
+ exner_in_w3 => exner
+ end if
+
+ ! Vertical remapping of external fields to model levels
+ call invoke( &
+ nudging_ecmwf_vert_interp_kernel_type( &
+ theta_ref, u_in_w3_ref, v_in_w3_ref, &
+ T_nudging_ext_ref, u_nudging_ext_ref, v_nudging_ext_ref, &
+ exner_in_wth, exner_in_w3, surfp_nudging_ext_ref, &
+ p_zero, kappa, num_ref_data_levels &
+ ) &
+ )
else
- exner_in_w3 => exner
+ call log_event( &
+ "set_nudging_reference_fields: nudging_source is only currently " // &
+ "implemented for nudging_source_ecmwf", LOG_LEVEL_ERROR &
+ )
end if
- ! Vertical remapping of external fields to model levels
- call invoke( &
- nudging_vert_interp_kernel_type( &
- theta_ref, u_in_w3_ref, v_in_w3_ref, &
- T_nudging_ext_ref, u_nudging_ext_ref, v_nudging_ext_ref, &
- exner_in_wth, exner_in_w3, surfp_nudging_ext_ref, &
- p_zero, kappa, num_ref_data_levels &
- ) &
- )
-
end subroutine set_nudging_reference_fields
diff --git a/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90 b/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
index 7004d54d67..40ccd3fc76 100644
--- a/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
+++ b/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
@@ -43,7 +43,6 @@ module physics_constants_mod
! functions returning pointers to them
real(kind=r_def) :: dt_stored = 0.0_r_def
- logical(kind=l_def) :: to_update_nudging_weights = .true.
! ========================================================================== !
! Inventories for use in the rest of the model
@@ -55,9 +54,11 @@ module physics_constants_mod
type(inventory_by_mesh_type) :: rdz_w3_inventory
type(inventory_by_mesh_type) :: max_diff_wtheta_inventory
- type(inventory_by_mesh_type), target :: nudging_weights_w3_inventory
- type(inventory_by_mesh_type), target :: nudging_weights_wt_inventory
- type(inventory_by_local_mesh_type) :: convolution_weights_inventory
+ type(inventory_by_local_mesh_type) :: convolution_weights_inventory
+
+ ! 1D vertical nudging weights
+ real(kind=r_def), target, allocatable :: nudging_weights_w3(:)
+ real(kind=r_def), target, allocatable :: nudging_weights_wt(:)
#ifdef UM_PHYSICS
! Spherical harmonics, used by stochastic physics
@@ -76,7 +77,6 @@ module physics_constants_mod
public :: get_max_diff
public :: get_convolution_weights
public :: get_nudging_weights
- public :: update_nudging_weights
#ifdef UM_PHYSICS
public :: get_Pnm_star
@@ -496,76 +496,111 @@ contains
end function get_convolution_weights
- !> @brief Returns the weights for applying spectral nudging
+ !> @brief Returns the vertical weights for applying spectral nudging
!> @param[in] config Application namelist configuration object
!> @param[in] mesh The mesh to get the object for
!> @param[in] fs_id The function space ID to get the object for
- !> @return The nudging_weights field
- function get_nudging_weights(config, mesh, fs_id) result(nudging_weights)
-
- use nudging_weights_kernel_mod, only: nudging_weights_kernel_type
+ !> @return The 1D vertical nudging_weights array
+ function get_nudging_weights(config, mesh, fs_id) result(weights)
implicit none
- type(config_type), intent(in) :: config
- type(mesh_type), intent(in) :: mesh
- integer(kind=i_def), intent(in) :: fs_id
- integer(kind=i_def) :: mesh_id
- type(function_space_type), pointer :: fs
- type(field_type), pointer :: nudging_weights
- type(inventory_by_mesh_type), pointer :: nudging_weights_inventory
- logical(kind=l_def) :: constant_exists
- character(len=str_def) :: inventory_name
- integer(kind=i_def) :: nudging_level_top
- integer(kind=i_def) :: nudging_level_bottom
- integer(kind=i_def) :: nudging_taper_top
- integer(kind=i_def) :: nudging_taper_bottom
- integer(kind=tik) :: id
-
- if (fs_id == W3) then
- nudging_weights_inventory => nudging_weights_w3_inventory
- inventory_name = "nudging_weights_w3"
- else if (fs_id == Wtheta) then
- nudging_weights_inventory => nudging_weights_wt_inventory
- inventory_name = "nudging_weights_wt"
- else
- call log_event( &
- 'Spectral nudging weights only implemented for W3 and Wtheta', &
- LOG_LEVEL_ERROR &
- )
- end if
-
- ! Initialise inventory if it hasn't been done so already
- if (.not. nudging_weights_inventory%is_initialised()) then
- call nudging_weights_inventory%initialise(name=inventory_name)
- end if
+ type(config_type), intent(in) :: config
+ type(mesh_type), intent(in) :: mesh
+ integer(kind=i_def), intent(in) :: fs_id
+ integer(kind=i_def) :: mesh_id
+ real(kind=r_def), pointer :: weights(:)
+ integer(kind=i_def) :: nudging_level_top
+ integer(kind=i_def) :: nudging_level_bottom
+ integer(kind=i_def) :: nudging_taper_top
+ integer(kind=i_def) :: nudging_taper_bottom
+ integer(kind=i_def) :: k, nlayers, nl
+ real(kind=r_def) :: k_real, w3_offset
+ real(kind=r_def) :: taper_bottom, nudging_bottom
+ real(kind=r_def) :: nudging_top, taper_top
+ logical(kind=l_def) :: compute_weights
+ integer(kind=tik) :: id
mesh_id = mesh%get_id()
- constant_exists = nudging_weights_inventory%paired_object_exists(mesh_id)
nudging_level_bottom = config%nudging%nudging_level_bottom()
nudging_level_top = config%nudging%nudging_level_top()
nudging_taper_bottom = config%nudging%nudging_level_taper_bottom()
nudging_taper_top = config%nudging%nudging_level_taper_top()
- ! Create object if it doesn't already exist
- if (.not. constant_exists) then
- fs => function_space_collection%get_fs(mesh, 0, 0, fs_id)
-
- if ( LPROF ) call start_timing( id, 'runtime_constants.physics' )
+ ! Work out if object exists
+ compute_weights = .false.
+ select case (fs_id)
+ case (W3)
+ if (.not. allocated(nudging_weights_w3)) compute_weights = .true.
+ case (Wtheta)
+ if (.not. allocated(nudging_weights_wt)) compute_weights = .true.
+ case default
+ call log_event( &
+ 'Spectral nudging weights only implemented for W3 and Wtheta', &
+ LOG_LEVEL_ERROR &
+ )
+ end select
- call nudging_weights_inventory%add_field(nudging_weights, fs, mesh)
+ if (compute_weights) then
- call invoke( &
- nudging_weights_kernel_type( &
- nudging_weights, nudging_level_bottom, nudging_level_top, &
- nudging_taper_bottom, nudging_taper_top &
- ) &
- )
+ if ( LPROF ) call start_timing( id, 'runtime_constants.physics' )
+ nlayers = mesh%get_nlayers()
+ select case (fs_id)
+ case (W3)
+ nl = nlayers
+ w3_offset = 0.5_r_def
+ allocate(nudging_weights_w3(nl))
+ weights => nudging_weights_w3
+ case (Wtheta)
+ nl = nlayers + 1
+ w3_offset = 0.0_r_def
+ allocate(nudging_weights_wt(nl))
+ weights => nudging_weights_wt
+ end select
+
+ do k = 0, nl-1
+ k_real = real(k, r_def) + w3_offset
+
+ ! Below start of taper, weights = 0
+ if (k_real < taper_bottom + EPS) then
+ weights(1 + k) = 0.0_r_def
+
+ ! Ramp up from 0 to 1 between taper_bottom and nudging_bottom
+ else if (k_real < nudging_bottom + EPS) then
+ weights(1 + k) = (k_real - taper_bottom) / &
+ max(1.0_r_def, nudging_bottom - taper_bottom)
+
+ ! Full nudging between nudging_bottom and nudging_top
+ else if (k_real < nudging_top + EPS) then
+ weights(1 + k) = 1.0_r_def
+
+ ! Ramp down from 1 to 0 between nudging_top and taper_top
+ else if (k_real < taper_top + EPS) then
+ weights(1 + k) = 1.0_r_def - (k_real - nudging_top) / &
+ max(1.0_r_def, taper_top - nudging_top)
+
+ ! Above taper_top, weights = 0
+ else
+ weights(1 + k) = 0.0_r_def
+
+ end if
+ end do
if ( LPROF ) call stop_timing( id, 'runtime_constants.physics' )
end if
- call nudging_weights_inventory%get_field(mesh, nudging_weights)
+ ! Point to existing object
+ select case (fs_id)
+ case (W3)
+ weights => nudging_weights_w3
+ case (Wtheta)
+ weights => nudging_weights_wt
+ case default
+ call log_event( &
+ 'Spectral nudging weights only implemented for W3 and Wtheta', &
+ LOG_LEVEL_ERROR &
+ )
+ end select
end function get_nudging_weights
@@ -637,8 +672,8 @@ contains
call rdz_fd1_inventory%clear()
call rdz_w3_inventory%clear()
call max_diff_wtheta_inventory%clear()
- call nudging_weights_w3_inventory%clear()
- call nudging_weights_wt_inventory%clear()
+ if (allocated(nudging_weights_w3)) deallocate(nudging_weights_w3)
+ if (allocated(nudging_weights_wt)) deallocate(nudging_weights_wt)
call convolution_weights_inventory%clear()
#ifdef UM_PHYSICS
call Pnm_star_inventory%clear()
@@ -646,113 +681,4 @@ contains
end subroutine final_physics_constants
- ! ========================================================================== !
- ! UPDATE SPECTRAL NUDGING WEIGHTS
- ! ========================================================================== !
- !> @brief Update the spectral nudging weights based on the current time
- !> @param[in] model_clock The model clock object
- !> @param[in] config Application namelist configuration object
- !> @param[in] mesh The mesh to compute the weights on
- subroutine update_nudging_weights(model_clock, config, mesh)
-
- use nudging_weights_kernel_mod, only: nudging_weights_kernel_type
-
- implicit none
-
- class(model_clock_type), intent(in) :: model_clock
- type(config_type), intent(in) :: config
- type(mesh_type), intent(in) :: mesh
- type(field_type), pointer :: nudging_weights
- real(kind=r_def) :: nudging_spinup_ratio
- real(kind=r_def) :: dt, time
-
- ! Configuration variables
- integer(kind=i_def) :: nudging_level_bottom, nudging_level_top
- integer(kind=i_def) :: nudging_taper_bottom, nudging_taper_top
- real(kind=r_def) :: nudging_spinup_start, nudging_spinup_end
- real(kind=r_def) :: nudging_relax_time
-
- ! Get nudging configuration variables
- nudging_level_bottom = config%nudging%nudging_level_bottom()
- nudging_level_top = config%nudging%nudging_level_top()
- nudging_taper_bottom = config%nudging%nudging_level_taper_bottom()
- nudging_taper_top = config%nudging%nudging_level_taper_top()
- nudging_spinup_start = config%nudging%nudging_spinup_start()
- nudging_spinup_end = config%nudging%nudging_spinup_end()
- nudging_relax_time = config%nudging%nudging_relax_time()
-
- ! NB: this assumes that dt is constant
- dt = REAL(model_clock%get_seconds_per_step(), r_def)
- time = dt*REAL(model_clock%get_step() - 1, r_def)
-
- ! Set factor for nudging weight during spin-up period
- if (time <= nudging_spinup_start) then
- ! Before spin-up period
- nudging_spinup_ratio = 0.0_r_def
-
- ! Set weights to zero
- nudging_weights => get_nudging_weights(config, mesh, W3)
- call invoke( setval_c(nudging_weights, 0.0_r_def) )
- nudging_weights => get_nudging_weights(config, mesh, Wtheta)
- call invoke( setval_c(nudging_weights, 0.0_r_def) )
-
- ! Do not need to call usual kernel so set flag to false
- to_update_nudging_weights = .false.
-
- else if (time > nudging_spinup_start .and. time < nudging_spinup_end) then
- ! During spin-up period
- if (ABS(nudging_spinup_end - nudging_spinup_start) < EPS) then
- nudging_spinup_ratio = 1.0_r_def
- else
- nudging_spinup_ratio = ( &
- (time - nudging_spinup_start) &
- / (nudging_spinup_end - nudging_spinup_start) &
- )
- end if
- ! Ramping up nudging weights, so enforce that they are updated
- to_update_nudging_weights = .true.
-
- else
- ! After spin-up period
- nudging_spinup_ratio = 1.0_r_def
- end if
-
- ! Now adjust weighting based on relaxation time
- if (nudging_relax_time > EPS) then
- nudging_spinup_ratio = nudging_spinup_ratio * ( &
- MIN(1.0_r_def, dt / nudging_relax_time) &
- )
- end if
-
- if (to_update_nudging_weights) then
- ! Update W3 nudging weights
- nudging_weights => get_nudging_weights(config, mesh, W3)
-
- call invoke( &
- nudging_weights_kernel_type( &
- nudging_weights, nudging_level_bottom, nudging_level_top, &
- nudging_taper_bottom, nudging_taper_top &
- ), &
- inc_a_times_X(nudging_spinup_ratio, nudging_weights) &
- )
-
- ! Update Wtheta nudging weights
- nudging_weights => get_nudging_weights(config, mesh, Wtheta)
-
- call invoke( &
- nudging_weights_kernel_type( &
- nudging_weights, nudging_level_bottom, nudging_level_top, &
- nudging_taper_bottom, nudging_taper_top &
- ), &
- inc_a_times_X(nudging_spinup_ratio, nudging_weights) &
- )
-
- ! If after spin-up period, we don't need to update the weights any more
- if (time >= nudging_spinup_end) then
- to_update_nudging_weights = .false.
- end if
- end if
-
- end subroutine update_nudging_weights
-
end module physics_constants_mod
diff --git a/science/gungho/source/driver/gungho_driver_mod.F90 b/science/gungho/source/driver/gungho_driver_mod.F90
index 88f9f46e96..993862d999 100644
--- a/science/gungho/source/driver/gungho_driver_mod.F90
+++ b/science/gungho/source/driver/gungho_driver_mod.F90
@@ -358,15 +358,12 @@ end subroutine initialise
!>
subroutine step( modeldb )
- use physics_constants_mod, only: update_nudging_weights
-
implicit none
type(modeldb_type), intent(inout) :: modeldb
type(mesh_type), pointer :: mesh
type(mesh_type), pointer :: twod_mesh
- type(mesh_type), pointer :: nudging_mesh
integer(kind=i_def) :: ts_start, rc
integer(tik) :: tid_first, tid_rest
@@ -391,7 +388,6 @@ subroutine step( modeldb )
integer(i_def) :: theta_forcing
integer(i_def) :: wind_forcing
- character(len=str_def) :: nudging_mesh_name
read(timestep_start,*,iostat=rc) ts_start
if ( LPROF ) then
@@ -476,21 +472,11 @@ subroutine step( modeldb )
if ( theta_forcing == theta_forcing_nudging &
.or. wind_forcing == wind_forcing_nudging) then
- ! Getting nudging mesh name
- nudging_mesh_name = modeldb%config%base_mesh%prime_mesh_name()
- if ( modeldb%config%formulation%use_multires_coupling() ) then
- if ( modeldb%config%multires_coupling%coarse_nudging() ) then
- nudging_mesh_name = modeldb%config%multires_coupling%nudging_mesh_name()
- end if
- end if
derived_fields => modeldb%fields%get_field_collection("derived_fields")
call update_variable_fields( &
model_axes%nudging_times_list, modeldb%clock, derived_fields &
)
-
- nudging_mesh => mesh_collection%get_mesh(trim(adjustl(nudging_mesh_name)))
- call update_nudging_weights(modeldb%clock, modeldb%config, nudging_mesh)
end if
end if
diff --git a/science/gungho/source/kernel/external_forcing/convolution_2d_kernel_mod.F90 b/science/gungho/source/kernel/external_forcing/convolution_2d_kernel_mod.F90
index 8f6338087b..5c04d6e640 100644
--- a/science/gungho/source/kernel/external_forcing/convolution_2d_kernel_mod.F90
+++ b/science/gungho/source/kernel/external_forcing/convolution_2d_kernel_mod.F90
@@ -11,10 +11,11 @@
module convolution_2d_kernel_mod
use argument_mod, only: arg_type, &
- GH_FIELD, &
- GH_REAL, GH_READ, GH_WRITE, &
+ GH_FIELD, GH_SCALAR_ARRAY, GH_SCALAR, &
+ GH_REAL, GH_INTEGER, GH_READ, GH_WRITE, &
CELL_COLUMN, STENCIL, REGION, &
ANY_DISCONTINUOUS_SPACE_1, &
+ ANY_DISCONTINUOUS_SPACE_2, &
ANY_DISCONTINUOUS_SPACE_9
use constants_mod, only: r_def, i_def
@@ -31,11 +32,17 @@ module convolution_2d_kernel_mod
!> Contains the metadata needed by the Psy layer
type, public, extends(kernel_type) :: convolution_2d_kernel_type
private
- type(arg_type) :: meta_args(3) = (/ &
- arg_type(GH_FIELD, GH_REAL, GH_WRITE, ANY_DISCONTINUOUS_SPACE_1), &
- arg_type(GH_FIELD, GH_REAL, GH_READ, ANY_DISCONTINUOUS_SPACE_1, &
+ type(arg_type) :: meta_args(8) = (/ &
+ arg_type(GH_FIELD, GH_REAL, GH_WRITE, ANY_DISCONTINUOUS_SPACE_1), &
+ arg_type(GH_FIELD, GH_REAL, GH_READ, ANY_DISCONTINUOUS_SPACE_1, &
STENCIL(REGION)), &
- arg_type(GH_FIELD, GH_REAL, GH_READ, ANY_DISCONTINUOUS_SPACE_9) &
+ arg_type(GH_SCALAR_ARRAY, &
+ GH_REAL, GH_READ, 1), &
+ arg_type(GH_FIELD, GH_REAL, GH_READ, ANY_DISCONTINUOUS_SPACE_9), &
+ arg_type(GH_FIELD, GH_INTEGER, GH_READ, ANY_DISCONTINUOUS_SPACE_2), &
+ arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
+ arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
+ arg_type(GH_SCALAR, GH_REAL, GH_READ) &
/)
integer :: operates_on = CELL_COLUMN
contains
@@ -49,28 +56,46 @@ module convolution_2d_kernel_mod
contains
-!> @brief Computes weights for performing convolution for spectral nudging.
-!> @param[in] nlayers Number of layers
-!> @param[in,out] filtered_field The filtered field to compute
-!> @param[in] field_in Field to be filtered
-!> @param[in] stencil_size Size of the stencil used in the convolution
-!> @param[in] stencil_map Map of the stencil used in the convolution
-!> @param[in] weights Weights for performing the convolution
-!> @param[in] ndf_3d Number of DoFs per cell in 3D field
-!> @param[in] undf_3d Number of DoFs in this partition in 3D field
-!> @param[in] map_3d DoF map for the 3D field
-!> @param[in] ndf_2d Num of DoFs per cell in weights field
-!> @param[in] undf_2d Num of DoFs in this partition in weights field
-!> @param[in] map_2d DoF map for the weights field
+!> @brief Performs convolution for spectral nudging.
+!> @param[in] nlayers Number of layers
+!> @param[in,out] filtered_field The filtered field to compute
+!> @param[in] field_in Field to be filtered
+!> @param[in] stencil_size Size of the stencil used in the convolution
+!> @param[in] stencil_map Map of the stencil used in the convolution
+!> @param[in] nvert Number of vertical weights
+!> @param[in] vert_weights 1D array of vertical weights for nudging
+!> @param[in] conv_weights 2D weights for performing the convolution
+!> @param[in] tropopause_level Tropopause level for each column
+!> @param[in] min_level Minimum vertical level for nudging
+!> @param[in] max_level Maximum vertical level for nudging
+!> @param[in] time_weight Time weight for nudging
+!> @param[in] ndf_3d Number of DoFs per cell in 3D field
+!> @param[in] undf_3d Number of DoFs in this partition in 3D field
+!> @param[in] map_3d DoF map for the 3D field
+!> @param[in] ndf_2d_cw Num of DoFs per cell in weights field
+!> @param[in] undf_2d_cw Num DoFs in this partition in weights field
+!> @param[in] map_2d_cw DoF map for the weights field
+!> @param[in] ndf_2d Num of DoFs per cell in 2D trop field
+!> @param[in] undf_2d Num DoFs in this partition in 2D trop field
+!> @param[in] map_2d DoF map for the 2D trop field
subroutine convolution_2d_code(nlayers, &
filtered_field, &
field_in, &
stencil_size, &
stencil_map, &
- weights, &
+ nvert, &
+ vert_weights, &
+ conv_weights, &
+ tropopause_level, &
+ min_level, &
+ max_level, &
+ time_weight, &
ndf_3d, &
undf_3d, &
map_3d, &
+ ndf_2d_cw, &
+ undf_2d_cw, &
+ map_2d_cw, &
ndf_2d, &
undf_2d, &
map_2d)
@@ -79,31 +104,49 @@ subroutine convolution_2d_code(nlayers, &
! Arguments
integer(kind=i_def), intent(in) :: nlayers
- integer(kind=i_def), intent(in) :: ndf_2d, ndf_3d
- integer(kind=i_def), intent(in) :: undf_2d, undf_3d
+ integer(kind=i_def), intent(in) :: ndf_2d, ndf_2d_cw, ndf_3d
+ integer(kind=i_def), intent(in) :: undf_2d, undf_2d_cw, undf_3d
+ integer(kind=i_def), intent(in) :: nvert(1)
integer(kind=i_def), intent(in) :: stencil_size
+ integer(kind=i_def), intent(in) :: min_level, max_level
integer(kind=i_def), intent(in) :: map_2d(ndf_2d)
+ integer(kind=i_def), intent(in) :: map_2d_cw(ndf_2d_cw)
integer(kind=i_def), intent(in) :: map_3d(ndf_3d)
integer(kind=i_def), intent(in) :: stencil_map(ndf_3d, stencil_size)
- real(kind=r_def), intent(in) :: weights(undf_2d)
+ real(kind=r_def), intent(in) :: vert_weights(nvert(1))
+ real(kind=r_def), intent(in) :: time_weight
+ real(kind=r_def), intent(in) :: conv_weights(undf_2d_cw)
+ integer(kind=i_def), intent(in) :: tropopause_level(undf_2d)
real(kind=r_def), intent(in) :: field_in(undf_3d)
real(kind=r_def), intent(inout) :: filtered_field(undf_3d)
! Local variables
- integer(kind=i_def) :: i, idx_w, idx_s, idx_f, nl
+ integer(kind=i_def) :: i, nl
+ integer(kind=i_def) :: idx_w, idx_sb, idx_st, idx_fb, idx_ft, idx_vb, idx_vt
+ integer(kind=i_def) :: max_level_loc
+ ! Set maximum level
nl = nlayers - 2 + ndf_3d ! nlayers for Wtheta, nlayers - 1 for W3
- idx_f = map_3d(1)
+ max_level_loc = MIN(max_level, nl, tropopause_level(map_2d(1)))
+ idx_fb = map_3d(1) + min_level
+ idx_ft = idx_fb + max_level_loc
+ idx_vb = min_level + 1
+ idx_vt = idx_vb + max_level_loc
do i = 1, stencil_size
- idx_w = map_2d(1) + i - 1 ! Multidata index
- idx_s = stencil_map(1, i) ! Stencil index
+ idx_w = map_2d_cw(1) + i - 1 ! Multidata index
+ idx_sb = stencil_map(1, i) + min_level ! Stencil index bottom
+ idx_st = idx_sb + max_level_loc ! Stencil index top
- filtered_field(idx_f:idx_f+nl) = ( &
- filtered_field(idx_f:idx_f+nl) &
- + weights(idx_w) * field_in(idx_s:idx_s+nl) &
+ filtered_field(idx_fb:idx_ft) = ( &
+ filtered_field(idx_fb:idx_ft) &
+ + field_in(idx_sb:idx_st) * conv_weights(idx_w) &
)
end do
+ ! Apply vertical weights
+ filtered_field(idx_fb:idx_ft) = ( &
+ filtered_field(idx_fb:idx_ft) * time_weight * vert_weights(idx_vb:idx_vt) &
+ )
end subroutine convolution_2d_code
diff --git a/science/gungho/source/kernel/external_forcing/newtonian_nudging_kernel_mod.F90 b/science/gungho/source/kernel/external_forcing/newtonian_nudging_kernel_mod.F90
new file mode 100644
index 0000000000..b288934d71
--- /dev/null
+++ b/science/gungho/source/kernel/external_forcing/newtonian_nudging_kernel_mod.F90
@@ -0,0 +1,127 @@
+!-----------------------------------------------------------------------------
+! (C) Crown copyright Met Office. All rights reserved.
+! The file LICENCE, distributed with this code, contains details of the terms
+! under which the code may be used.
+!-----------------------------------------------------------------------------
+!> @brief Performs Newtonian nudging.
+!> @details This kernel computes increment for performing Newtonian nudging of
+!! a field towards a reference field.
+!! Only implemented for the lowest-order elements
+module newtonian_nudging_kernel_mod
+
+ use argument_mod, only: arg_type, &
+ GH_FIELD, GH_SCALAR, GH_SCALAR_ARRAY, &
+ GH_REAL, GH_INTEGER, &
+ GH_READ, GH_WRITE, &
+ ANY_DISCONTINUOUS_SPACE_1, &
+ ANY_DISCONTINUOUS_SPACE_2, &
+ CELL_COLUMN
+ use constants_mod, only: r_def, i_def
+ use kernel_mod, only: kernel_type
+
+ implicit none
+
+ private
+
+ !-----------------------------------------------------------------------------
+ ! Public types
+ !-----------------------------------------------------------------------------
+ !> The type declaration for the kernel.
+ !> Contains the metadata needed by the Psy layer
+ type, public, extends(kernel_type) :: newtonian_nudging_kernel_type
+ private
+ type(arg_type) :: meta_args(8) = (/ &
+ arg_type(GH_FIELD, GH_REAL, GH_WRITE, ANY_DISCONTINUOUS_SPACE_1), &
+ arg_type(GH_FIELD, GH_REAL, GH_READ, ANY_DISCONTINUOUS_SPACE_1), &
+ arg_type(GH_FIELD, GH_REAL, GH_READ, ANY_DISCONTINUOUS_SPACE_1), &
+ arg_type(GH_SCALAR_ARRAY, &
+ GH_REAL, GH_READ, 1), &
+ arg_type(GH_FIELD, GH_INTEGER, GH_READ, ANY_DISCONTINUOUS_SPACE_2), &
+ arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
+ arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
+ arg_type(GH_SCALAR, GH_REAL, GH_READ) &
+ /)
+ integer :: operates_on = CELL_COLUMN
+ contains
+ procedure, nopass :: newtonian_nudging_code
+ end type
+
+ !-----------------------------------------------------------------------------
+ ! Contained functions/subroutines
+ !-----------------------------------------------------------------------------
+ public :: newtonian_nudging_code
+
+contains
+
+!> @brief Performs Newtonian nudging
+!> @param[in] nlayers Number of layers
+!> @param[in,out] increment The increment to compute
+!> @param[in] field_in Field to be nudged
+!> @param[in] field_ref Reference field to nudge towards
+!> @param[in] nvert Number of vertical levels
+!> @param[in] vert_weights Vertical weights for nudging
+!> @param[in] tropopause_level Tropopause level for each column
+!> @param[in] min_level Minimum vertical level for nudging
+!> @param[in] max_level Maximum vertical level for nudging
+!> @param[in] time_weight Time weight for nudging
+!> @param[in] ndf_3d Num of DoFs per cell in 3D field
+!> @param[in] undf_3d Num of DoFs in this partition in 3D field
+!> @param[in] map_3d DoF map for the 3D field
+!> @param[in] ndf_2d Num of DoFs per cell in 2D field
+!> @param[in] undf_2d Num of DoFs in this partition in 2D field
+!> @param[in] map_2d DoF map for the 2D field
+subroutine newtonian_nudging_code(nlayers, &
+ increment, &
+ field_in, &
+ field_ref, &
+ nvert, &
+ vert_weights, &
+ tropopause_level, &
+ min_level, &
+ max_level, &
+ time_weight, &
+ ndf_3d, &
+ undf_3d, &
+ map_3d, &
+ ndf_2d, &
+ undf_2d, &
+ map_2d)
+
+ implicit none
+
+ ! Arguments
+ integer(kind=i_def), intent(in) :: nlayers
+ integer(kind=i_def), intent(in) :: ndf_2d, ndf_3d
+ integer(kind=i_def), intent(in) :: undf_2d, undf_3d
+ integer(kind=i_def), intent(in) :: nvert(1)
+ integer(kind=i_def), intent(in) :: min_level, max_level
+ integer(kind=i_def), intent(in) :: map_2d(ndf_2d)
+ integer(kind=i_def), intent(in) :: map_3d(ndf_3d)
+ real(kind=r_def), intent(in) :: vert_weights(nvert(1))
+ real(kind=r_def), intent(in) :: time_weight
+ integer(kind=i_def), intent(in) :: tropopause_level(undf_2d)
+ real(kind=r_def), intent(in) :: field_ref(undf_3d)
+ real(kind=r_def), intent(in) :: field_in(undf_3d)
+ real(kind=r_def), intent(inout) :: increment(undf_3d)
+
+ ! Local variables
+ integer(kind=i_def) :: nl
+ integer(kind=i_def) :: idx_fb, idx_ft, idx_vb, idx_vt
+ integer(kind=i_def) :: max_level_loc
+
+ ! Set maximum level
+ nl = nlayers - 2 + ndf_3d ! nlayers for Wtheta, nlayers - 1 for W3
+ max_level_loc = MIN(max_level, nl, tropopause_level(map_2d(1)))
+ idx_fb = map_3d(1) + min_level
+ idx_ft = idx_fb + max_level_loc
+ idx_vb = min_level + 1
+ idx_vt = idx_vb + max_level_loc
+
+ increment(idx_fb:idx_ft) = ( &
+ (field_ref(idx_fb:idx_ft) - field_in(idx_fb:idx_ft)) &
+ * time_weight * vert_weights(idx_vb:idx_vt) &
+ )
+
+end subroutine newtonian_nudging_code
+
+end module newtonian_nudging_kernel_mod
diff --git a/science/gungho/source/kernel/external_forcing/nudging_vert_interp_kernel_mod.F90 b/science/gungho/source/kernel/external_forcing/nudging_ecmwf_vert_interp_kernel_mod.F90
similarity index 96%
rename from science/gungho/source/kernel/external_forcing/nudging_vert_interp_kernel_mod.F90
rename to science/gungho/source/kernel/external_forcing/nudging_ecmwf_vert_interp_kernel_mod.F90
index fd98c0fa79..ca95c0628e 100644
--- a/science/gungho/source/kernel/external_forcing/nudging_vert_interp_kernel_mod.F90
+++ b/science/gungho/source/kernel/external_forcing/nudging_ecmwf_vert_interp_kernel_mod.F90
@@ -4,7 +4,7 @@
! under which the code may be used.
!-------------------------------------------------------------------------------
!> @brief Performs vertical remapping of nudging reference fields
-module nudging_vert_interp_kernel_mod
+module nudging_ecmwf_vert_interp_kernel_mod
use argument_mod, only: arg_type, &
GH_FIELD, GH_SCALAR, &
@@ -25,7 +25,7 @@ module nudging_vert_interp_kernel_mod
!-------------------------------------------------------------------------------
! Public types
!-------------------------------------------------------------------------------
- type, public, extends(kernel_type) :: nudging_vert_interp_kernel_type
+ type, public, extends(kernel_type) :: nudging_ecmwf_vert_interp_kernel_type
private
type(arg_type) :: meta_args(12) = (/ &
arg_type(GH_FIELD, GH_REAL, GH_WRITE, Wtheta), & ! Interpolated theta field
@@ -43,13 +43,13 @@ module nudging_vert_interp_kernel_mod
/)
integer :: operates_on = CELL_COLUMN
contains
- procedure, nopass :: nudging_vert_interp_code
-end type nudging_vert_interp_kernel_type
+ procedure, nopass :: nudging_ecmwf_vert_interp_code
+end type nudging_ecmwf_vert_interp_kernel_type
!-------------------------------------------------------------------------------
! Contained functions/subroutines
!-------------------------------------------------------------------------------
-public :: nudging_vert_interp_code
+public :: nudging_ecmwf_vert_interp_code
contains
!-----------------------------------------------------------------------------
@@ -80,7 +80,7 @@ module nudging_vert_interp_kernel_mod
!> @param[in] ndf_ec_2d num DoFs per cell for surface pressure
!> @param[in] undf_ec_2d num DoFs in this partition for surf pressure
!> @param[in] map_ec_2d dofmap for surf pressure field
- subroutine nudging_vert_interp_code( &
+ subroutine nudging_ecmwf_vert_interp_code( &
nlayers, &
theta_mod_levs, u_mod_levs, v_mod_levs, &
T_nudge_levs, u_nudge_levs, v_nudge_levs, &
@@ -92,7 +92,7 @@ subroutine nudging_vert_interp_code( &
ndf_ec2d, undf_ec2d, map_ec2d &
)
- use ecmwf_level_support_mod, only: ak_ec_137, bk_ec_137, &
+ use ecmwf_level_support_mod, only: ak_ec_137, bk_ec_137, &
ak_ec_137_50, bk_ec_137_50
implicit none
@@ -259,6 +259,6 @@ subroutine nudging_vert_interp_code( &
! Set bottom value of theta to the value of the next level up
theta_mod_levs(wt_idx) = theta_mod_levs(wt_idx+1)
- end subroutine nudging_vert_interp_code
+ end subroutine nudging_ecmwf_vert_interp_code
-end module nudging_vert_interp_kernel_mod
+end module nudging_ecmwf_vert_interp_kernel_mod
diff --git a/science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90 b/science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90
deleted file mode 100644
index b848844fb9..0000000000
--- a/science/gungho/source/kernel/external_forcing/nudging_weights_kernel_mod.F90
+++ /dev/null
@@ -1,133 +0,0 @@
-!-----------------------------------------------------------------------------
-! (C) Crown copyright Met Office. All rights reserved.
-! The file LICENCE, distributed with this code, contains details of the terms
-! under which the code may be used.
-!-----------------------------------------------------------------------------
-!> @brief Computes weights for scaling the spectral nudging increment.
-!> @details This kernel computes the height-dependent weights used in spectral
-!! nudging, to allow the nudging to be targeted at certain vertical
-!! heights in the atmosphere.
-!! Only implemented for the lowest-order elements
-module nudging_weights_kernel_mod
-
- use argument_mod, only: arg_type, &
- GH_FIELD, GH_SCALAR, &
- GH_REAL, GH_INTEGER, &
- GH_READ, GH_WRITE, &
- ANY_DISCONTINUOUS_SPACE_1, &
- CELL_COLUMN
- use constants_mod, only: r_def, i_def, EPS
- use kernel_mod, only: kernel_type
-
- implicit none
-
- private
-
- !-----------------------------------------------------------------------------
- ! Public types
- !-----------------------------------------------------------------------------
- !> The type declaration for the kernel.
- !> Contains the metadata needed by the Psy layer
- type, public, extends(kernel_type) :: nudging_weights_kernel_type
- private
- type(arg_type) :: meta_args(5) = (/ &
- arg_type(GH_FIELD, GH_REAL, GH_WRITE, ANY_DISCONTINUOUS_SPACE_1), &
- arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
- arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
- arg_type(GH_SCALAR, GH_INTEGER, GH_READ), &
- arg_type(GH_SCALAR, GH_INTEGER, GH_READ) &
- /)
- integer :: operates_on = CELL_COLUMN
- contains
- procedure, nopass :: nudging_weights_code
- end type
-
- !-----------------------------------------------------------------------------
- ! Contained functions/subroutines
- !-----------------------------------------------------------------------------
- public :: nudging_weights_code
-
-contains
-
-!> @brief Computes weights for scaling the spectral nudging increment.
-!> @param[in] nlayers Number of layers
-!> @param[in,out] weights The weights to be computed
-!> @param[in] bottom_nudge_level Level at and above which nudging is full
-!! strength
-!> @param[in] top_nudge_level Level at and below which nudging is full
-!! strength
-!> @param[in] taper_bottom_level Level at and below which nudging is 0
-!> @param[in] taper_top_level Level at and above which nudging is 0
-!> @param[in] ndf Num DoFs per cell
-!> @param[in] undf Num DoFs in this partition
-!> @param[in] map DoFmap for this column
-subroutine nudging_weights_code(nlayers, &
- weights, &
- bottom_nudge_level, &
- top_nudge_level, &
- taper_bottom_level, &
- taper_top_level, &
- ndf, &
- undf, &
- map)
-
- implicit none
-
- ! Arguments
- integer(kind=i_def), intent(in) :: nlayers
- integer(kind=i_def), intent(in) :: ndf
- integer(kind=i_def), intent(in) :: undf
- integer(kind=i_def), intent(in) :: map(ndf)
- integer(kind=i_def), intent(in) :: bottom_nudge_level
- integer(kind=i_def), intent(in) :: top_nudge_level
- integer(kind=i_def), intent(in) :: taper_bottom_level
- integer(kind=i_def), intent(in) :: taper_top_level
- real(kind=r_def), intent(inout) :: weights(undf)
-
-
- ! Local variables
- integer(kind=i_def) :: k, idx, nl
- real(kind=r_def) :: k_real, w3_offset
- real(kind=r_def) :: taper_bottom, nudging_bottom, nudging_top, taper_top
-
- nl = nlayers + ndf - 2 ! nlayers for W3, nlayers+1 for Wtheta
- w3_offset = 0.5_r_def * real(2 - ndf, r_def) ! 0.5 for W3, 0.0 for Wtheta
- idx = map(1)
-
- ! Convert levels to reals for comparison with k_real
- taper_bottom = real(taper_bottom_level, r_def)
- nudging_bottom = real(bottom_nudge_level, r_def)
- nudging_top = real(top_nudge_level, r_def)
- taper_top = real(taper_top_level, r_def)
-
- do k = 0, nl
- k_real = real(k, r_def) + w3_offset
-
- ! Below start of taper, weights = 0
- if (k_real < taper_bottom + EPS) then
- weights(idx + k) = 0.0_r_def
-
- ! Ramp up from 0 to 1 between taper_bottom and nudging_bottom
- else if (k_real < nudging_bottom + EPS) then
- weights(idx + k) = (k_real - taper_bottom) / &
- max(1.0_r_def, nudging_bottom - taper_bottom)
-
- ! Full nudging between nudging_bottom and nudging_top
- else if (k_real < nudging_top + EPS) then
- weights(idx + k) = 1.0_r_def
-
- ! Ramp down from 1 to 0 between nudging_top and taper_top
- else if (k_real < taper_top + EPS) then
- weights(idx + k) = 1.0_r_def - (k_real - nudging_top) / &
- max(1.0_r_def, taper_top - nudging_top)
-
- ! Above taper_top, weights = 0
- else
- weights(idx + k) = 0.0_r_def
-
- end if
- end do
-
-end subroutine nudging_weights_code
-
-end module nudging_weights_kernel_mod
diff --git a/science/gungho/source/psy/psykal_lite_mod.F90 b/science/gungho/source/psy/psykal_lite_mod.F90
index 80509dc1d2..8e86210d5b 100644
--- a/science/gungho/source/psy/psykal_lite_mod.F90
+++ b/science/gungho/source/psy/psykal_lite_mod.F90
@@ -795,4 +795,199 @@ subroutine invoke_prolong_multidata_linear_kernel_type(target_field, source_fiel
!
end subroutine invoke_prolong_multidata_linear_kernel_type
+! ============================================================================ !
+! NUDGING ROUTINES
+! ============================================================================ !
+! These kernels use a 1D scalar array, which is not yet fully supported by
+! Psyclone. This code can be removed once PSyclone issue #1312 is closed
+
+ subroutine invoke_newtonian_nudging_kernel_type(coarse_increment, coarse_field_in, field_ref, vertical_weights, &
+&tropopause_level, min_level, max_level, time_weight, dims_vertical_weights)
+
+ use newtonian_nudging_kernel_mod, only: newtonian_nudging_code
+ use mesh_mod, only : mesh_type
+
+ implicit none
+
+ type(field_type), intent(in) :: coarse_increment
+ type(field_type), intent(in) :: coarse_field_in
+ type(field_type), intent(in) :: field_ref
+ type(integer_field_type), intent(in) :: tropopause_level
+ integer(kind=i_def), intent(in) :: min_level
+ integer(kind=i_def), intent(in) :: max_level
+ real(kind=r_def), intent(in) :: time_weight
+ integer(kind=i_def), dimension(1), intent(in) :: dims_vertical_weights
+ real(kind=r_def), dimension(dims_vertical_weights(1)), intent(in) :: vertical_weights
+ integer(kind=i_def) :: cell
+ type(mesh_type), pointer :: mesh => null()
+ integer(kind=i_def) :: max_halo_depth_mesh
+ real(kind=r_def), pointer, dimension(:) :: coarse_increment_data => null()
+ real(kind=r_def), pointer, dimension(:) :: coarse_field_in_data => null()
+ real(kind=r_def), pointer, dimension(:) :: field_ref_data => null()
+ integer(kind=i_def), pointer, dimension(:) :: tropopause_level_data => null()
+ integer(kind=i_def) :: nlayers_coarse_increment
+ integer(kind=i_def) :: ndf_a1_ce_it
+ integer(kind=i_def) :: undf_a1_ce_it
+ integer(kind=i_def) :: ndf_a2_te_ll
+ integer(kind=i_def) :: undf_a2_te_ll
+ integer(kind=i_def), pointer :: map_a1_ce_it(:,:) => null()
+ integer(kind=i_def), pointer :: map_a2_te_ll(:,:) => null()
+ type(field_proxy_type) :: coarse_increment_proxy
+ type(field_proxy_type) :: coarse_field_in_proxy
+ type(field_proxy_type) :: field_ref_proxy
+ type(integer_field_proxy_type) :: tropopause_level_proxy
+ integer(kind=i_def) :: loop0_start
+ integer(kind=i_def) :: loop0_stop
+
+ ! Initialise field and/or operator proxies
+ coarse_increment_proxy = coarse_increment%get_proxy()
+ coarse_increment_data => coarse_increment_proxy%data
+ coarse_field_in_proxy = coarse_field_in%get_proxy()
+ coarse_field_in_data => coarse_field_in_proxy%data
+ field_ref_proxy = field_ref%get_proxy()
+ field_ref_data => field_ref_proxy%data
+ tropopause_level_proxy = tropopause_level%get_proxy()
+ tropopause_level_data => tropopause_level_proxy%data
+
+ ! Initialise number of layers
+ nlayers_coarse_increment = coarse_increment_proxy%vspace%get_nlayers()
+
+ ! Create a mesh object
+ mesh => coarse_increment_proxy%vspace%get_mesh()
+ max_halo_depth_mesh = mesh%get_halo_depth()
+
+ ! Look-up dofmaps for each function space
+ map_a1_ce_it => coarse_increment_proxy%vspace%get_whole_dofmap()
+ map_a2_te_ll => tropopause_level_proxy%vspace%get_whole_dofmap()
+
+ ! Initialise number of DoFs for a1_ce_it
+ ndf_a1_ce_it = coarse_increment_proxy%vspace%get_ndf()
+ undf_a1_ce_it = coarse_increment_proxy%vspace%get_undf()
+
+ ! Initialise number of DoFs for a2_te_ll
+ ndf_a2_te_ll = tropopause_level_proxy%vspace%get_ndf()
+ undf_a2_te_ll = tropopause_level_proxy%vspace%get_undf()
+
+ ! Set-up all of the loop bounds
+ loop0_start = 1
+ loop0_stop = mesh%get_last_edge_cell()
+
+ ! Call kernels and communication routines
+ do cell = loop0_start, loop0_stop, 1
+ call newtonian_nudging_code(nlayers_coarse_increment, coarse_increment_data, coarse_field_in_data, field_ref_data, &
+&dims_vertical_weights, vertical_weights, tropopause_level_data, min_level, max_level, time_weight, ndf_a1_ce_it, undf_a1_ce_it, &
+&map_a1_ce_it(:,cell), ndf_a2_te_ll, undf_a2_te_ll, map_a2_te_ll(:,cell))
+ enddo
+
+ ! Set halos dirty/clean for fields modified in the above loop(s)
+ call coarse_increment_proxy%set_dirty()
+
+ end subroutine invoke_newtonian_nudging_kernel_type
+
+ subroutine invoke_convolution_2d_kernel_type(coarse_increment, coarse_field_diff, &
+&spectral_stencil_extent, vertical_weights, convolution_weights, &
+&tropopause_level, min_level, max_level, time_weight, dims_vertical_weights)
+
+ use convolution_2d_kernel_mod, only: convolution_2d_code
+ use mesh_mod, only : mesh_type
+ use stencil_dofmap_mod, only : STENCIL_REGION, stencil_dofmap_type
+
+ implicit none
+
+ type(field_type), intent(in) :: coarse_increment
+ type(field_type), intent(in) :: coarse_field_diff
+ type(field_type), intent(in) :: convolution_weights
+ type(integer_field_type), intent(in) :: tropopause_level
+ integer(kind=i_def), intent(in) :: min_level
+ integer(kind=i_def), intent(in) :: max_level
+ real(kind=r_def), intent(in) :: time_weight
+ integer(kind=i_def), intent(in) :: spectral_stencil_extent
+ integer(kind=i_def), dimension(1), intent(in) :: dims_vertical_weights
+ real(kind=r_def), dimension(dims_vertical_weights(1)), intent(in) :: vertical_weights
+ integer(kind=i_def) :: cell
+ type(mesh_type), pointer :: mesh => null()
+ integer(kind=i_def) :: max_halo_depth_mesh
+ real(kind=r_def), pointer, dimension(:) :: coarse_increment_data => null()
+ real(kind=r_def), pointer, dimension(:) :: coarse_field_diff_data => null()
+ real(kind=r_def), pointer, dimension(:) :: convolution_weights_data => null()
+ integer(kind=i_def), pointer, dimension(:) :: tropopause_level_data => null()
+ integer(kind=i_def) :: nlayers_coarse_increment
+ type(stencil_dofmap_type), pointer :: coarse_field_diff_stencil_map => null()
+ integer(kind=i_def), pointer, dimension(:,:,:) :: coarse_field_diff_stencil_dofmap => null()
+ integer(kind=i_def) :: ndf_a1_ce_it
+ integer(kind=i_def) :: undf_a1_ce_it
+ integer(kind=i_def) :: ndf_a9_cn_ws
+ integer(kind=i_def) :: undf_a9_cn_ws
+ integer(kind=i_def) :: ndf_a2_te_ll
+ integer(kind=i_def) :: undf_a2_te_ll
+ integer(kind=i_def), pointer :: map_a1_ce_it(:,:) => null()
+ integer(kind=i_def), pointer :: map_a2_te_ll(:,:) => null()
+ integer(kind=i_def), pointer :: map_a9_cn_ws(:,:) => null()
+ type(field_proxy_type) :: coarse_increment_proxy
+ type(field_proxy_type) :: coarse_field_diff_proxy
+ type(field_proxy_type) :: convolution_weights_proxy
+ type(integer_field_proxy_type) :: tropopause_level_proxy
+ integer(kind=i_def), pointer, dimension(:) :: coarse_field_diff_stencil_size => null()
+ integer(kind=i_def) :: loop0_start
+ integer(kind=i_def) :: loop0_stop
+
+ ! Initialise field and/or operator proxies
+ coarse_increment_proxy = coarse_increment%get_proxy()
+ coarse_increment_data => coarse_increment_proxy%data
+ coarse_field_diff_proxy = coarse_field_diff%get_proxy()
+ coarse_field_diff_data => coarse_field_diff_proxy%data
+ convolution_weights_proxy = convolution_weights%get_proxy()
+ convolution_weights_data => convolution_weights_proxy%data
+ tropopause_level_proxy = tropopause_level%get_proxy()
+ tropopause_level_data => tropopause_level_proxy%data
+
+ ! Initialise number of layers
+ nlayers_coarse_increment = coarse_increment_proxy%vspace%get_nlayers()
+
+ ! Create a mesh object
+ mesh => coarse_increment_proxy%vspace%get_mesh()
+ max_halo_depth_mesh = mesh%get_halo_depth()
+
+ ! Initialise stencil dofmaps
+ coarse_field_diff_stencil_map => coarse_field_diff_proxy%vspace%get_stencil_dofmap(STENCIL_REGION, spectral_stencil_extent)
+ coarse_field_diff_stencil_dofmap => coarse_field_diff_stencil_map%get_whole_dofmap()
+ coarse_field_diff_stencil_size => coarse_field_diff_stencil_map%get_stencil_sizes()
+
+ ! Look-up dofmaps for each function space
+ map_a1_ce_it => coarse_increment_proxy%vspace%get_whole_dofmap()
+ map_a9_cn_ws => convolution_weights_proxy%vspace%get_whole_dofmap()
+ map_a2_te_ll => tropopause_level_proxy%vspace%get_whole_dofmap()
+
+ ! Initialise number of DoFs for a1_ce_it
+ ndf_a1_ce_it = coarse_increment_proxy%vspace%get_ndf()
+ undf_a1_ce_it = coarse_increment_proxy%vspace%get_undf()
+
+ ! Initialise number of DoFs for a9_cn_ws
+ ndf_a9_cn_ws = convolution_weights_proxy%vspace%get_ndf()
+ undf_a9_cn_ws = convolution_weights_proxy%vspace%get_undf()
+
+ ! Initialise number of DoFs for a2_te_ll
+ ndf_a2_te_ll = tropopause_level_proxy%vspace%get_ndf()
+ undf_a2_te_ll = tropopause_level_proxy%vspace%get_undf()
+
+ ! Set-up all of the loop bounds
+ loop0_start = 1
+ loop0_stop = mesh%get_last_edge_cell()
+
+ ! Call kernels and communication routines
+ if (coarse_field_diff_proxy%is_dirty(depth=spectral_stencil_extent)) then
+ call coarse_field_diff_proxy%halo_exchange(depth=spectral_stencil_extent)
+ end if
+ do cell = loop0_start, loop0_stop, 1
+ call convolution_2d_code(nlayers_coarse_increment, coarse_increment_data, coarse_field_diff_data, &
+&coarse_field_diff_stencil_size(cell), coarse_field_diff_stencil_dofmap(:,:,cell), dims_vertical_weights, vertical_weights, &
+&convolution_weights_data, tropopause_level_data, min_level, max_level, time_weight, ndf_a1_ce_it, undf_a1_ce_it, &
+&map_a1_ce_it(:,cell), ndf_a9_cn_ws, undf_a9_cn_ws, map_a9_cn_ws(:,cell), ndf_a2_te_ll, undf_a2_te_ll, map_a2_te_ll(:,cell))
+ enddo
+
+ ! Set halos dirty/clean for fields modified in the above loop(s)
+ call coarse_increment_proxy%set_dirty()
+
+ end subroutine invoke_convolution_2d_kernel_type
+
end module psykal_lite_mod
diff --git a/science/gungho/unit-test/kernel/external_forcing/convolution_2d_kernel_mod_test.pf b/science/gungho/unit-test/kernel/external_forcing/convolution_2d_kernel_mod_test.pf
index 69a3fd49e0..19d12fb604 100644
--- a/science/gungho/unit-test/kernel/external_forcing/convolution_2d_kernel_mod_test.pf
+++ b/science/gungho/unit-test/kernel/external_forcing/convolution_2d_kernel_mod_test.pf
@@ -26,30 +26,45 @@ contains
integer(kind=i_def), parameter :: nlayers = 1
integer(kind=i_def), parameter :: stencil_size = 25
+ integer(kind=i_def), parameter :: ndf_2d_cw = 1
+ integer(kind=i_def), parameter :: undf_2d_cw = stencil_size
integer(kind=i_def), parameter :: ndf_2d = 1
- integer(kind=i_def), parameter :: undf_2d = stencil_size
+ integer(kind=i_def), parameter :: undf_2d = 1
integer(kind=i_def), parameter :: ndf_3d = 1 ! This simplifies the test!
integer(kind=i_def), parameter :: undf_3d = stencil_size
+ integer(kind=i_def), parameter :: nvert(1) = 1
integer(kind=i_def), parameter :: kmax = 40
integer(kind=i_def), parameter :: kmin = 4
real(kind=r_def), parameter :: dx = 20.0_r_def
real(kind=r_def), parameter :: dy = 10.0_r_def
+ ! With nlayers = 1 and ndf_3d = 1, only a single level (level 0) exists, so
+ ! min_level/max_level/tropopause_level are all capped down to that single
+ ! level regardless of their values here
+ integer(kind=i_def), parameter :: min_level = 0
+ integer(kind=i_def), parameter :: max_level = 2
+ real(kind=r_def), parameter :: time_weight = 3.0_r_def
real(kind=r_def), parameter :: tol = 10.0_r_def*epsilon(1.0_r_def)
integer(kind=i_def) :: map_3d(ndf_3d)
+ integer(kind=i_def) :: map_2d_cw(ndf_2d_cw)
integer(kind=i_def) :: map_2d(ndf_2d)
integer(kind=i_def) :: stencil_map(ndf_3d, stencil_size)
+ integer(kind=i_def) :: tropopause_level(undf_2d)
real(kind=r_def) :: answer
real(kind=r_def) :: filtered_field(undf_3d)
real(kind=r_def) :: field_in(undf_3d)
- real(kind=r_def) :: weights(undf_2d)
+ real(kind=r_def) :: conv_weights(undf_2d_cw)
+ real(kind=r_def) :: vert_weights(nvert(1))
integer(kind=i_def) :: i, j, ij
real(kind=r_def) :: dist, chi1, chi2
map_3d(1) = 13
+ map_2d_cw(1) = 1
map_2d(1) = 1
+ tropopause_level(1) = 4
+ vert_weights(1) = 2.0_r_def
! Populate maps / weights
! Set up regular grid of coordinates
@@ -63,9 +78,9 @@ contains
! Weights --------------------------------------------------------------
dist = sqrt((chi1 - 3.0_r_def*dx)**2 + (chi2 - 3.0_r_def*dy)**2)
if (dist < 1.0e-3_r_def) then
- weights(ij) = 2.0_r_def*REAL(kmax - kmin, r_def)
+ conv_weights(ij) = 2.0_r_def*REAL(kmax - kmin, r_def)
else
- weights(ij) = ( &
+ conv_weights(ij) = ( &
SIN(2.0_r_def*PI*kmax*dist) / (PI*dist) &
- SIN(2.0_r_def*PI*kmin*dist) / (PI*dist) &
)
@@ -76,18 +91,23 @@ contains
end do
end do
- ! Field is linear and convolution is symmetric, so answer should be central value
- answer = field_in(13)
+ ! Field is linear and convolution is symmetric, so the convolution alone
+ ! should reproduce the central value; the kernel then scales this by the
+ ! time weight and vertical weight
+ answer = field_in(13) * time_weight * vert_weights(1)
! Normalise weights
- weights(:) = weights(:) / SUM(weights(:))
+ conv_weights(:) = conv_weights(:) / SUM(conv_weights(:))
! Initialise output field to zero
filtered_field(:) = 0.0_r_def
call convolution_2d_code( &
nlayers, filtered_field, field_in, stencil_size, stencil_map, &
- weights, ndf_3d, undf_3d, map_3d, ndf_2d, undf_2d, map_2d &
+ nvert, vert_weights, conv_weights, tropopause_level, &
+ min_level, max_level, time_weight, &
+ ndf_3d, undf_3d, map_3d, ndf_2d_cw, undf_2d_cw, map_2d_cw, &
+ ndf_2d, undf_2d, map_2d &
)
! Only central value (13 out of 25) is set
diff --git a/science/gungho/unit-test/kernel/external_forcing/newtonian_nudging_kernel_mod_test.pf b/science/gungho/unit-test/kernel/external_forcing/newtonian_nudging_kernel_mod_test.pf
new file mode 100644
index 0000000000..cba2578aa2
--- /dev/null
+++ b/science/gungho/unit-test/kernel/external_forcing/newtonian_nudging_kernel_mod_test.pf
@@ -0,0 +1,92 @@
+!-----------------------------------------------------------------------------
+! (C) Crown copyright Met Office. All rights reserved.
+! 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
+! GitHub Copilot (Claude Sonnet 5).
+!-------------------------------------------------------------------------------
+module newtonian_nudging_kernel_mod_test
+
+ use constants_mod, only : i_def, r_def
+ use funit
+
+ implicit none
+
+ private
+ public :: test_all
+
+contains
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ @Test
+ subroutine test_all( )
+
+ use newtonian_nudging_kernel_mod, only: newtonian_nudging_code
+
+ implicit none
+
+ integer(kind=i_def), parameter :: nlayers = 8
+ integer(kind=i_def), parameter :: ndf_3d = 1
+ integer(kind=i_def), parameter :: undf_3d = 10
+ integer(kind=i_def), parameter :: ndf_2d = 1
+ integer(kind=i_def), parameter :: undf_2d = 1
+ integer(kind=i_def), parameter :: nvert(1) = 10
+
+ ! Bottom and top levels for full-strength nudging: the tropopause level
+ ! is more restrictive than max_level (and both are within the number of
+ ! model levels), so it is the tropopause level that should determine the
+ ! top of the nudged range
+ integer(kind=i_def), parameter :: min_level = 2
+ integer(kind=i_def), parameter :: max_level = 6
+ integer(kind=i_def), parameter :: trop_level = 5
+ integer(kind=i_def), parameter :: max_level_loc = trop_level
+
+ real(kind=r_def), parameter :: time_weight = 2.0_r_def
+ real(kind=r_def), parameter :: tol = 10.0_r_def*epsilon(1.0_r_def)
+
+ integer(kind=i_def) :: map_3d(ndf_3d)
+ integer(kind=i_def) :: map_2d(ndf_2d)
+ integer(kind=i_def) :: tropopause_level(undf_2d)
+
+ real(kind=r_def) :: increment(undf_3d)
+ real(kind=r_def) :: field_in(undf_3d)
+ real(kind=r_def) :: field_ref(undf_3d)
+ real(kind=r_def) :: vert_weights(nvert(1))
+ real(kind=r_def) :: answer(undf_3d)
+
+ integer(kind=i_def) :: k, idx_fb, idx_ft
+
+ map_3d(1) = 1
+ map_2d(1) = 1
+ tropopause_level(1) = trop_level
+
+ do k = 1, undf_3d
+ field_in(k) = 10.0_r_def + real(k, r_def)
+ field_ref(k) = 50.0_r_def + 2.0_r_def*real(k, r_def)
+ vert_weights(k) = 0.1_r_def*real(k, r_def)
+ end do
+
+ ! Only levels min_level to min_level+max_level_loc (i.e. array indices
+ ! 3 to 8, since map_3d(1) = 1) should be nudged; everywhere else the
+ ! increment should remain at its initial value of 0
+ idx_fb = map_3d(1) + min_level
+ idx_ft = idx_fb + max_level_loc
+ answer(:) = 0.0_r_def
+ do k = idx_fb, idx_ft
+ answer(k) = (field_ref(k) - field_in(k)) * time_weight * vert_weights(k)
+ end do
+
+ increment(:) = 0.0_r_def
+
+ call newtonian_nudging_code( &
+ nlayers, increment, field_in, field_ref, nvert, vert_weights, &
+ tropopause_level, min_level, max_level, time_weight, &
+ ndf_3d, undf_3d, map_3d, ndf_2d, undf_2d, map_2d &
+ )
+
+ @assertEqual(answer, increment, tol)
+
+ end subroutine test_all
+
+end module newtonian_nudging_kernel_mod_test
diff --git a/science/gungho/unit-test/kernel/external_forcing/nudging_vert_interp_kernel_mod_test.pf b/science/gungho/unit-test/kernel/external_forcing/nudging_ecmwf_vert_interp_kernel_mod_test.pf
similarity index 94%
rename from science/gungho/unit-test/kernel/external_forcing/nudging_vert_interp_kernel_mod_test.pf
rename to science/gungho/unit-test/kernel/external_forcing/nudging_ecmwf_vert_interp_kernel_mod_test.pf
index 4bae07d526..f8f3796066 100644
--- a/science/gungho/unit-test/kernel/external_forcing/nudging_vert_interp_kernel_mod_test.pf
+++ b/science/gungho/unit-test/kernel/external_forcing/nudging_ecmwf_vert_interp_kernel_mod_test.pf
@@ -6,7 +6,7 @@
! Some of the content of this file has been produced with the assistance of
! GitHub Copilot (Claude Sonnet 5).
!-------------------------------------------------------------------------------
-module nudging_vert_interp_kernel_mod_test
+module nudging_ecmwf_vert_interp_kernel_mod_test
use constants_mod, only : i_def, r_def
use funit
@@ -22,8 +22,8 @@ contains
@Test
subroutine test_all( )
- use nudging_vert_interp_kernel_mod, only: nudging_vert_interp_code
- use ecmwf_level_support_mod, only: ak_ec_137, bk_ec_137
+ use nudging_ecmwf_vert_interp_kernel_mod, only: nudging_ecmwf_vert_interp_code
+ use ecmwf_level_support_mod, only: ak_ec_137, bk_ec_137
implicit none
@@ -125,7 +125,7 @@ contains
332.65624218452757_r_def, 336.2736730367809_r_def, &
341.0504790035589_r_def, 348.100533442245_r_def /)
- call nudging_vert_interp_code( &
+ call nudging_ecmwf_vert_interp_code( &
nlayers, &
theta_mod_levs, u_mod_levs, v_mod_levs, &
T_nudge_levs, u_nudge_levs, v_nudge_levs, &
@@ -143,4 +143,4 @@ contains
end subroutine test_all
-end module nudging_vert_interp_kernel_mod_test
+end module nudging_ecmwf_vert_interp_kernel_mod_test
diff --git a/science/gungho/unit-test/kernel/external_forcing/nudging_weights_kernel_mod_test.pf b/science/gungho/unit-test/kernel/external_forcing/nudging_weights_kernel_mod_test.pf
deleted file mode 100644
index 4bcedb7b6f..0000000000
--- a/science/gungho/unit-test/kernel/external_forcing/nudging_weights_kernel_mod_test.pf
+++ /dev/null
@@ -1,75 +0,0 @@
-!-----------------------------------------------------------------------------
-! (C) Crown copyright Met Office. All rights reserved.
-! The file LICENCE, distributed with this code, contains details of the terms
-! under which the code may be used.
-!-------------------------------------------------------------------------------
-module nudging_weights_kernel_mod_test
-
- use constants_mod, only : i_def, r_def, l_def
- use funit
-
-
- implicit none
-
- private
- public :: test_all
-
-contains
-
- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- @Test
- subroutine test_all( )
-
- use nudging_weights_kernel_mod, only: nudging_weights_code
-
- implicit none
-
- integer(kind=i_def), parameter :: nlayers = 20
- integer(kind=i_def), parameter :: ndf_w3 = 1
- integer(kind=i_def), parameter :: undf_w3 = nlayers
- integer(kind=i_def), parameter :: ndf_wt = 2
- integer(kind=i_def), parameter :: undf_wt = nlayers + 1
- real(kind=r_def), parameter :: tol = 10.0_r_def*epsilon(1.0_r_def)
-
- integer(kind=i_def) :: map_w3(ndf_w3), map_wt(ndf_wt)
- real(kind=r_def) :: answer_w3(undf_w3), answer_wt(undf_wt)
- real(kind=r_def) :: weights_w3(undf_w3), weights_wt(undf_wt)
- integer(kind=i_def) :: bottom_level, top_level, bottom_taper, top_taper
-
- map_w3(1) = 1
- map_wt(1) = 1
- map_wt(2) = 2
- bottom_taper = 2
- bottom_level = 3
- top_level = 16
- top_taper = 18
- answer_w3 = (/ 0.0_r_def, 0.0_r_def, 0.5_r_def, 1.0_r_def, 1.0_r_def, &
- 1.0_r_def, 1.0_r_def, 1.0_r_def, 1.0_r_def, 1.0_r_def, &
- 1.0_r_def, 1.0_r_def, 1.0_r_def, 1.0_r_def, 1.0_r_def, &
- 1.0_r_def, 0.75_r_def, 0.25_r_def, 0.0_r_def, 0.0_r_def /)
- answer_wt = (/ 0.0_r_def, 0.0_r_def, 0.0_r_def, 1.0_r_def, 1.0_r_def, &
- 1.0_r_def, 1.0_r_def, 1.0_r_def, 1.0_r_def, 1.0_r_def, &
- 1.0_r_def, 1.0_r_def, 1.0_r_def, 1.0_r_def, 1.0_r_def, &
- 1.0_r_def, 1.0_r_def, 0.5_r_def, 0.0_r_def, 0.0_r_def, &
- 0.0_r_def /)
-
- weights_w3(:) = 100.0_r_def
- call nudging_weights_code( &
- nlayers, weights_w3, bottom_level, top_level, bottom_taper, top_taper, &
- ndf_w3, undf_w3, map_w3 &
- )
- weights_wt(:) = 100.0_r_def
- call nudging_weights_code( &
- nlayers, weights_wt, bottom_level, top_level, bottom_taper, top_taper, &
- ndf_wt, undf_wt, map_wt &
- )
-
- write(*,*) 'weights_w3 = ', weights_w3
- write(*,*) 'weights_wt = ', weights_wt
-
- @assertEqual(answer_w3, weights_w3, tol)
- @assertEqual(answer_wt, weights_wt, tol)
-
- end subroutine test_all
-
-end module nudging_weights_kernel_mod_test
From bd61626363003ea0858143ed9b2445ff0c17a59b Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Tue, 11 Aug 2026 15:45:19 +0100
Subject: [PATCH 25/35] fix tropopause api
---
science/gungho/source/algorithm/physics/nudging_alg_mod.X90 | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/science/gungho/source/algorithm/physics/nudging_alg_mod.X90 b/science/gungho/source/algorithm/physics/nudging_alg_mod.X90
index 913c4781c1..227a2ffe0a 100644
--- a/science/gungho/source/algorithm/physics/nudging_alg_mod.X90
+++ b/science/gungho/source/algorithm/physics/nudging_alg_mod.X90
@@ -290,7 +290,10 @@ contains
if (nudging_min_tropopause_level < nudging_level_taper_top) then
#ifdef UM_PHYSICS
- call locate_tropopause_alg(config, trop_level, theta, exner, trop_diag=.false.)
+ call locate_tropopause_alg( &
+ config, trop_level, theta, exner, &
+ nudging_twod_mesh, trop_diag=.false. &
+ )
#else
call invoke( int_setval_c(trop_level, nudging_min_tropopause_level) )
#endif
From e27c91f3b6bc44a8339037925e14b80d93f4c325 Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Tue, 11 Aug 2026 17:05:24 +0100
Subject: [PATCH 26/35] remove old option
---
.../app/lfric_atm/opt/rose-app-nudging-newtonian-coarse.conf | 4 +++-
rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf | 4 +++-
.../app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf | 4 +++-
rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf | 4 +++-
4 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian-coarse.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian-coarse.conf
index fac9c67439..80ba65b365 100644
--- a/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian-coarse.conf
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian-coarse.conf
@@ -25,7 +25,9 @@ physics_mesh_name='dynamics'
[namelist:nudging]
nudging_method='newtonian'
-nudging_relax_time=1.0
+nudging_relax_time_theta=1.0
+nudging_relax_time_u=1.0
+nudging_relax_time_v=1.0
nudging_spinup_start=1.0
nudging_spinup_end=2.0
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf
index c34a258192..b9c9b650e1 100644
--- a/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf
@@ -22,7 +22,9 @@ physics_mesh_name='dynamics'
[namelist:nudging]
nudging_method='newtonian'
-nudging_relax_time=1.0
+nudging_relax_time_theta=1.0
+nudging_relax_time_u=1.0
+nudging_relax_time_v=1.0
nudging_spinup_start=1.0
nudging_spinup_end=2.0
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf
index b708e9b1cb..8d6b33afca 100644
--- a/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf
@@ -24,7 +24,9 @@ orography_mesh_name='dynamics'
physics_mesh_name='dynamics'
[namelist:nudging]
-nudging_relax_time=1.0
+nudging_relax_time_theta=1.0
+nudging_relax_time_u=1.0
+nudging_relax_time_v=1.0
nudging_spinup_start=1.0
nudging_spinup_end=2.0
spectral_stencil_extent=4
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf
index be653daa3c..f2fc702c29 100644
--- a/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf
@@ -21,7 +21,9 @@ orography_mesh_name='dynamics'
physics_mesh_name='dynamics'
[namelist:nudging]
-nudging_relax_time=1.0
+nudging_relax_time_theta=1.0
+nudging_relax_time_u=1.0
+nudging_relax_time_v=1.0
nudging_spinup_start=1.0
nudging_spinup_end=2.0
spectral_stencil_extent=6
From c4c2c3bcd372408197daefce077113ef85814e70 Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Wed, 12 Aug 2026 09:46:00 +0100
Subject: [PATCH 27/35] use appropriate theta/exner for locating tropopause
---
.../algorithm/physics/nudging_alg_mod.X90 | 29 ++++++++++++++++---
1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/science/gungho/source/algorithm/physics/nudging_alg_mod.X90 b/science/gungho/source/algorithm/physics/nudging_alg_mod.X90
index 227a2ffe0a..67d4a23642 100644
--- a/science/gungho/source/algorithm/physics/nudging_alg_mod.X90
+++ b/science/gungho/source/algorithm/physics/nudging_alg_mod.X90
@@ -12,7 +12,7 @@ module nudging_alg_mod
use extrusion_mod, only: TWOD
use field_mod, only: field_type
use field_collection_mod, only: field_collection_type
- use fs_continuity_mod, only: W3
+ use fs_continuity_mod, only: W3, Wtheta
use function_space_mod, only: function_space_type
use function_space_collection_mod, only: function_space_collection
use integer_field_mod, only: integer_field_type
@@ -267,31 +267,52 @@ contains
type(config_type), intent(in) :: config
type(integer_field_type), intent(inout) :: trop_level
- type(field_type), intent(in) :: theta, exner
+ type(field_type), target, intent(in) :: theta, exner
! Local variables
integer(kind=i_def) :: nudging_min_tropopause_level
integer(kind=i_def) :: nudging_level_taper_top
character(str_def) :: nudging_mesh_name
+ logical(kind=l_def) :: coarse_nudging
type(mesh_type), pointer :: nudging_mesh
type(mesh_type), pointer :: nudging_twod_mesh
- type(function_space_type), pointer :: w3_2d_fs
+ type(function_space_type), pointer :: w3_2d_fs, wt_fs
+ type(field_type), pointer :: exner_trop, theta_trop
+ type(field_type), target :: exner_coarse, theta_coarse
nudging_min_tropopause_level = config%nudging%nudging_min_tropopause_level()
nudging_level_taper_top = config%nudging%nudging_level_taper_top()
+ coarse_nudging = config%multires_coupling%coarse_nudging()
nudging_mesh_name = trim(config%multires_coupling%nudging_mesh_name())
nudging_mesh => mesh_collection%get_mesh(nudging_mesh_name)
nudging_twod_mesh => mesh_collection%get_mesh(nudging_mesh, TWOD)
w3_2d_fs => function_space_collection%get_fs(nudging_twod_mesh, 0, 0, W3)
+ wt_fs => function_space_collection%get_fs(nudging_mesh, 0, 0, Wtheta)
call trop_level%initialise(w3_2d_fs)
if (nudging_min_tropopause_level < nudging_level_taper_top) then
#ifdef UM_PHYSICS
+
+ if (coarse_nudging) then
+ ! TODO: already have coarse exner from setting reference fields, and
+ ! will need coarse theta for nudging. So could save a small bit of work
+ ! by pulling these mappings out to somewhere else (but it would be messy)
+ call exner_coarse%initialise(wt_fs)
+ call theta_coarse%initialise(wt_fs)
+ call map_scalar_intermesh(exner_coarse, exner)
+ call map_scalar_intermesh(theta_coarse, theta)
+ exner_trop => exner_coarse
+ theta_trop => theta_coarse
+ else
+ exner_trop => exner
+ theta_trop => theta
+ end if
+
call locate_tropopause_alg( &
- config, trop_level, theta, exner, &
+ config, trop_level, theta_trop, exner_trop, &
nudging_twod_mesh, trop_diag=.false. &
)
#else
From b5a2b9c16ae309b4dea0e350b391983d6058d661 Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Wed, 12 Aug 2026 10:23:39 +0100
Subject: [PATCH 28/35] rose config-dump and add ifdef to nudging_alg to avoid
unused variable errors
---
.../app/lfric_atm/opt/rose-app-nudging-newtonian-coarse.conf | 2 +-
rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf | 2 +-
.../app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf | 2 +-
rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf | 2 +-
science/gungho/source/algorithm/physics/nudging_alg_mod.X90 | 3 ++-
5 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian-coarse.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian-coarse.conf
index 80ba65b365..844163d738 100644
--- a/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian-coarse.conf
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian-coarse.conf
@@ -28,8 +28,8 @@ nudging_method='newtonian'
nudging_relax_time_theta=1.0
nudging_relax_time_u=1.0
nudging_relax_time_v=1.0
-nudging_spinup_start=1.0
nudging_spinup_end=2.0
+nudging_spinup_start=1.0
[namelist:section_choice]
external_forcing=.true.
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf
index b9c9b650e1..7b5ec60e18 100644
--- a/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf
@@ -25,8 +25,8 @@ nudging_method='newtonian'
nudging_relax_time_theta=1.0
nudging_relax_time_u=1.0
nudging_relax_time_v=1.0
-nudging_spinup_start=1.0
nudging_spinup_end=2.0
+nudging_spinup_start=1.0
[namelist:section_choice]
external_forcing=.true.
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf
index 8d6b33afca..1f4f5c7faa 100644
--- a/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf
@@ -27,8 +27,8 @@ physics_mesh_name='dynamics'
nudging_relax_time_theta=1.0
nudging_relax_time_u=1.0
nudging_relax_time_v=1.0
-nudging_spinup_start=1.0
nudging_spinup_end=2.0
+nudging_spinup_start=1.0
spectral_stencil_extent=4
[namelist:section_choice]
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf
index f2fc702c29..602029ce69 100644
--- a/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf
@@ -24,8 +24,8 @@ physics_mesh_name='dynamics'
nudging_relax_time_theta=1.0
nudging_relax_time_u=1.0
nudging_relax_time_v=1.0
-nudging_spinup_start=1.0
nudging_spinup_end=2.0
+nudging_spinup_start=1.0
spectral_stencil_extent=6
[namelist:section_choice]
diff --git a/science/gungho/source/algorithm/physics/nudging_alg_mod.X90 b/science/gungho/source/algorithm/physics/nudging_alg_mod.X90
index 67d4a23642..51204b411a 100644
--- a/science/gungho/source/algorithm/physics/nudging_alg_mod.X90
+++ b/science/gungho/source/algorithm/physics/nudging_alg_mod.X90
@@ -278,8 +278,10 @@ contains
type(mesh_type), pointer :: nudging_mesh
type(mesh_type), pointer :: nudging_twod_mesh
type(function_space_type), pointer :: w3_2d_fs, wt_fs
+#ifdef UM_PHYSICS
type(field_type), pointer :: exner_trop, theta_trop
type(field_type), target :: exner_coarse, theta_coarse
+#endif
nudging_min_tropopause_level = config%nudging%nudging_min_tropopause_level()
nudging_level_taper_top = config%nudging%nudging_level_taper_top()
@@ -295,7 +297,6 @@ contains
if (nudging_min_tropopause_level < nudging_level_taper_top) then
#ifdef UM_PHYSICS
-
if (coarse_nudging) then
! TODO: already have coarse exner from setting reference fields, and
! will need coarse theta for nudging. So could save a small bit of work
From d5913f19b90d3495fa30be012b8e05441b761838 Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Wed, 12 Aug 2026 10:47:22 +0100
Subject: [PATCH 29/35] fix silly error with nudging levels
---
.../algorithm/runtime_constants/physics_constants_mod.X90 | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90 b/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
index 40ccd3fc76..8eb5bb14c6 100644
--- a/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
+++ b/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
@@ -559,6 +559,12 @@ contains
weights => nudging_weights_wt
end select
+ ! Convert nudging levels to reals
+ taper_bottom = real(nudging_taper_bottom, r_def)
+ nudging_bottom = real(nudging_level_bottom, r_def)
+ nudging_top = real(nudging_level_top, r_def)
+ taper_top = real(nudging_taper_top, r_def)
+
do k = 0, nl-1
k_real = real(k, r_def) + w3_offset
From 70583da011e885e792f42bbe3b2f42c617a513c2 Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Thu, 13 Aug 2026 14:49:56 +0100
Subject: [PATCH 30/35] fix temporal and vertical weights
---
.../lfric-gungho/HEAD/rose-meta.conf | 3 +-
.../physics/external_forcing_alg_mod.X90 | 6 ++--
.../algorithm/physics/nudging_alg_mod.X90 | 1 +
.../physics_constants_mod.X90 | 14 +++++---
.../convolution_2d_kernel_mod.F90 | 34 +++++++++++++------
.../newtonian_nudging_kernel_mod.F90 | 28 +++++++++++----
.../newtonian_nudging_kernel_mod_test.pf | 22 ++++++------
7 files changed, 70 insertions(+), 38 deletions(-)
diff --git a/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf b/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
index cf6bcb4f5f..6d86858061 100644
--- a/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
+++ b/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf
@@ -3971,8 +3971,7 @@ description=Level above which to ramp up nudging from 0 to full strength
fail-if=this < 0;
=this > namelist:nudging=nudging_level_bottom;
help=Above this level, nudging is tapered up to nudging_level_bottom.
- =At this level and below, nudging is 0, unless this is equal to
- =nudging_level_bottom.
+ =At this level and below, nudging is 0.
=Applies to both W3 and Wtheta fields. Wtheta fields are counted from 0 at
=the surface, while W3 fields are counted from 1/2 in the lowest level.
!kind=default
diff --git a/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90 b/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
index 8116921c5f..37cede0458 100644
--- a/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
+++ b/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
@@ -279,7 +279,7 @@ contains
case(theta_forcing_nudging)
! Determine nudging time-weighting for theta
- nudging_relax_time = config%nudging%nudging_relax_time_theta()
+ nudging_relax_time = 3600.0_r_def * config%nudging%nudging_relax_time_theta()
if (nudging_relax_time > 0.0_r_def) then
nudging_time_weight = dt * nudging_spinup_factor / nudging_relax_time
else
@@ -440,7 +440,7 @@ contains
! Apply nudging to calculate increments for each component
! u ----------------------------------------------------------------
- nudging_relax_time = config%nudging%nudging_relax_time_u()
+ nudging_relax_time = 3600.0_r_def * config%nudging%nudging_relax_time_u()
if (nudging_relax_time > 0.0_r_def) then
nudging_time_weight = dt * nudging_spinup_factor / nudging_relax_time
else
@@ -456,7 +456,7 @@ contains
end if
! v ----------------------------------------------------------------
- nudging_relax_time = config%nudging%nudging_relax_time_v()
+ nudging_relax_time = 3600.0_r_def * config%nudging%nudging_relax_time_v()
if (nudging_relax_time > 0.0_r_def) then
nudging_time_weight = dt * nudging_spinup_factor / nudging_relax_time
else
diff --git a/science/gungho/source/algorithm/physics/nudging_alg_mod.X90 b/science/gungho/source/algorithm/physics/nudging_alg_mod.X90
index 51204b411a..287391bdfd 100644
--- a/science/gungho/source/algorithm/physics/nudging_alg_mod.X90
+++ b/science/gungho/source/algorithm/physics/nudging_alg_mod.X90
@@ -316,6 +316,7 @@ contains
config, trop_level, theta_trop, exner_trop, &
nudging_twod_mesh, trop_diag=.false. &
)
+ call invoke( int_inc_max_aX(nudging_min_tropopause_level, trop_level) )
#else
call invoke( int_setval_c(trop_level, nudging_min_tropopause_level) )
#endif
diff --git a/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90 b/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
index 8eb5bb14c6..5e2c7dc89e 100644
--- a/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
+++ b/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
@@ -521,6 +521,10 @@ contains
logical(kind=l_def) :: compute_weights
integer(kind=tik) :: id
+ ! To avoid floating point issues when comparing integers as reals, set
+ ! a generous tolerance
+ real(kind=r_def), parameter :: EPS_INT = 0.1_r_def
+
mesh_id = mesh%get_id()
nudging_level_bottom = config%nudging%nudging_level_bottom()
nudging_level_top = config%nudging%nudging_level_top()
@@ -569,20 +573,22 @@ contains
k_real = real(k, r_def) + w3_offset
! Below start of taper, weights = 0
- if (k_real < taper_bottom + EPS) then
+ ! If taper_bottom == nudging_bottom, then ramp is skipped and weights=1
+ if (k_real < taper_bottom + EPS_INT &
+ .and. k_real < nudging_bottom + EPS_INT) then
weights(1 + k) = 0.0_r_def
! Ramp up from 0 to 1 between taper_bottom and nudging_bottom
- else if (k_real < nudging_bottom + EPS) then
+ else if (k_real < nudging_bottom + EPS_INT) then
weights(1 + k) = (k_real - taper_bottom) / &
max(1.0_r_def, nudging_bottom - taper_bottom)
! Full nudging between nudging_bottom and nudging_top
- else if (k_real < nudging_top + EPS) then
+ else if (k_real < nudging_top + EPS_INT) then
weights(1 + k) = 1.0_r_def
! Ramp down from 1 to 0 between nudging_top and taper_top
- else if (k_real < taper_top + EPS) then
+ else if (k_real < taper_top + EPS_INT) then
weights(1 + k) = 1.0_r_def - (k_real - nudging_top) / &
max(1.0_r_def, taper_top - nudging_top)
diff --git a/science/gungho/source/kernel/external_forcing/convolution_2d_kernel_mod.F90 b/science/gungho/source/kernel/external_forcing/convolution_2d_kernel_mod.F90
index 5c04d6e640..f59dfff55f 100644
--- a/science/gungho/source/kernel/external_forcing/convolution_2d_kernel_mod.F90
+++ b/science/gungho/source/kernel/external_forcing/convolution_2d_kernel_mod.F90
@@ -123,20 +123,34 @@ subroutine convolution_2d_code(nlayers, &
! Local variables
integer(kind=i_def) :: i, nl
integer(kind=i_def) :: idx_w, idx_sb, idx_st, idx_fb, idx_ft, idx_vb, idx_vt
- integer(kind=i_def) :: max_level_loc
+ integer(kind=i_def) :: min_level_loc, max_level_loc
- ! Set maximum level
nl = nlayers - 2 + ndf_3d ! nlayers for Wtheta, nlayers - 1 for W3
- max_level_loc = MIN(max_level, nl, tropopause_level(map_2d(1)))
- idx_fb = map_3d(1) + min_level
- idx_ft = idx_fb + max_level_loc
- idx_vb = min_level + 1
- idx_vt = idx_vb + max_level_loc
+
+ ! Wtheta: Level 0 corresponds to the surface
+ ! At min_level, Wtheta weights will be 0 unless taper_bottom == level_bottom
+ ! At max_level, Wtheta weights will be 0 unless taper_top == level_top
+ ! The tropopause level is included in the calculation if it is below max_level
+ ! W3: (k_wt < k_w3 < k_wt + 1)
+ ! Below min_level, W3 weights are set to 0, so we can ignore these levels
+ ! Above max_level, W3 weights are set to 0, so we can ignore these levels
+ ! Tropopause level is at a Wtheta level, so include W3 levels below this
+
+ ! The following indices take the above into account:
+ min_level_loc = MAX(min_level, 0)
+ ! max_level, not above tropopause. Subtract 1 for W3
+ max_level_loc = &
+ MAX(MIN(max_level, tropopause_level(map_2d(1)) - 2 + ndf_3d, nl), 0)
+
+ idx_fb = map_3d(1) + min_level_loc
+ idx_ft = map_3d(1) + max_level_loc
+ idx_vb = 1 + min_level_loc
+ idx_vt = 1 + max_level_loc
do i = 1, stencil_size
- idx_w = map_2d_cw(1) + i - 1 ! Multidata index
- idx_sb = stencil_map(1, i) + min_level ! Stencil index bottom
- idx_st = idx_sb + max_level_loc ! Stencil index top
+ idx_w = map_2d_cw(1) + i - 1 ! Multidata index
+ idx_sb = stencil_map(1, i) + min_level_loc ! Stencil index bottom
+ idx_st = stencil_map(1, i) + max_level_loc ! Stencil index top
filtered_field(idx_fb:idx_ft) = ( &
filtered_field(idx_fb:idx_ft) &
diff --git a/science/gungho/source/kernel/external_forcing/newtonian_nudging_kernel_mod.F90 b/science/gungho/source/kernel/external_forcing/newtonian_nudging_kernel_mod.F90
index b288934d71..98c522100f 100644
--- a/science/gungho/source/kernel/external_forcing/newtonian_nudging_kernel_mod.F90
+++ b/science/gungho/source/kernel/external_forcing/newtonian_nudging_kernel_mod.F90
@@ -107,15 +107,29 @@ subroutine newtonian_nudging_code(nlayers, &
! Local variables
integer(kind=i_def) :: nl
integer(kind=i_def) :: idx_fb, idx_ft, idx_vb, idx_vt
- integer(kind=i_def) :: max_level_loc
+ integer(kind=i_def) :: min_level_loc, max_level_loc
- ! Set maximum level
nl = nlayers - 2 + ndf_3d ! nlayers for Wtheta, nlayers - 1 for W3
- max_level_loc = MIN(max_level, nl, tropopause_level(map_2d(1)))
- idx_fb = map_3d(1) + min_level
- idx_ft = idx_fb + max_level_loc
- idx_vb = min_level + 1
- idx_vt = idx_vb + max_level_loc
+
+ ! Wtheta: Level 0 corresponds to the surface
+ ! At min_level, Wtheta weights will be 0 unless taper_bottom == level_bottom
+ ! At max_level, Wtheta weights will be 0 unless taper_top == level_top
+ ! The tropopause level is included in the calculation if it is below max_level
+ ! W3: (k_wt < k_w3 < k_wt + 1)
+ ! Below min_level, W3 weights are set to 0, so we can ignore these levels
+ ! Above max_level, W3 weights are set to 0, so we can ignore these levels
+ ! Tropopause level is at a Wtheta level, so include W3 levels below this
+
+ ! The following indices take the above into account:
+ min_level_loc = MAX(min_level, 0)
+ ! max_level, not above tropopause. Subtract 1 for W3
+ max_level_loc = &
+ MAX(MIN(max_level, tropopause_level(map_2d(1)) - 2 + ndf_3d, nl), 0)
+
+ idx_fb = map_3d(1) + min_level_loc
+ idx_ft = map_3d(1) + max_level_loc
+ idx_vb = 1 + min_level_loc
+ idx_vt = 1 + max_level_loc
increment(idx_fb:idx_ft) = ( &
(field_ref(idx_fb:idx_ft) - field_in(idx_fb:idx_ft)) &
diff --git a/science/gungho/unit-test/kernel/external_forcing/newtonian_nudging_kernel_mod_test.pf b/science/gungho/unit-test/kernel/external_forcing/newtonian_nudging_kernel_mod_test.pf
index cba2578aa2..66930d76b2 100644
--- a/science/gungho/unit-test/kernel/external_forcing/newtonian_nudging_kernel_mod_test.pf
+++ b/science/gungho/unit-test/kernel/external_forcing/newtonian_nudging_kernel_mod_test.pf
@@ -26,12 +26,12 @@ contains
implicit none
- integer(kind=i_def), parameter :: nlayers = 8
+ integer(kind=i_def), parameter :: nlayers = 10
integer(kind=i_def), parameter :: ndf_3d = 1
integer(kind=i_def), parameter :: undf_3d = 10
integer(kind=i_def), parameter :: ndf_2d = 1
integer(kind=i_def), parameter :: undf_2d = 1
- integer(kind=i_def), parameter :: nvert(1) = 10
+ integer(kind=i_def), parameter :: nvert(1) = nlayers
! Bottom and top levels for full-strength nudging: the tropopause level
! is more restrictive than max_level (and both are within the number of
@@ -40,7 +40,6 @@ contains
integer(kind=i_def), parameter :: min_level = 2
integer(kind=i_def), parameter :: max_level = 6
integer(kind=i_def), parameter :: trop_level = 5
- integer(kind=i_def), parameter :: max_level_loc = trop_level
real(kind=r_def), parameter :: time_weight = 2.0_r_def
real(kind=r_def), parameter :: tol = 10.0_r_def*epsilon(1.0_r_def)
@@ -55,7 +54,7 @@ contains
real(kind=r_def) :: vert_weights(nvert(1))
real(kind=r_def) :: answer(undf_3d)
- integer(kind=i_def) :: k, idx_fb, idx_ft
+ integer(kind=i_def) :: k
map_3d(1) = 1
map_2d(1) = 1
@@ -64,17 +63,16 @@ contains
do k = 1, undf_3d
field_in(k) = 10.0_r_def + real(k, r_def)
field_ref(k) = 50.0_r_def + 2.0_r_def*real(k, r_def)
- vert_weights(k) = 0.1_r_def*real(k, r_def)
end do
- ! Only levels min_level to min_level+max_level_loc (i.e. array indices
- ! 3 to 8, since map_3d(1) = 1) should be nudged; everywhere else the
- ! increment should remain at its initial value of 0
- idx_fb = map_3d(1) + min_level
- idx_ft = idx_fb + max_level_loc
+ vert_weights(:) = (/ 0.0_r_def, 0.0_r_def, 0.5_r_def, 1.0_r_def, 1.0_r_def, &
+ 1.0_r_def, 0.5_r_def, 0.0_r_def, 0.0_r_def, 0.0_r_def /)
+
answer(:) = 0.0_r_def
- do k = idx_fb, idx_ft
- answer(k) = (field_ref(k) - field_in(k)) * time_weight * vert_weights(k)
+ do k = 1, nlayers
+ if (k >= min_level .and. k <= max_level .and. k <= trop_level) then
+ answer(k) = (field_ref(k) - field_in(k)) * time_weight * vert_weights(k)
+ end if
end do
increment(:) = 0.0_r_def
From 05797a35d023146535ccb464d2f9fb2c4879b32e Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Thu, 13 Aug 2026 15:03:17 +0100
Subject: [PATCH 31/35] add missing doxygen comment
---
.../external_forcing/nudging_ecmwf_vert_interp_kernel_mod.F90 | 1 +
1 file changed, 1 insertion(+)
diff --git a/science/gungho/source/kernel/external_forcing/nudging_ecmwf_vert_interp_kernel_mod.F90 b/science/gungho/source/kernel/external_forcing/nudging_ecmwf_vert_interp_kernel_mod.F90
index ca95c0628e..2dfc6bda67 100644
--- a/science/gungho/source/kernel/external_forcing/nudging_ecmwf_vert_interp_kernel_mod.F90
+++ b/science/gungho/source/kernel/external_forcing/nudging_ecmwf_vert_interp_kernel_mod.F90
@@ -68,6 +68,7 @@ module nudging_ecmwf_vert_interp_kernel_mod
!> @param[in] ln_surfp_nudge surface pressure from nudging data
!> @param[in] p_zero Reference pressure for Exner calculation
!> @param[in] kappa Rd/Cp, used in definition of Exner
+ !> @param[in] nudge_levels number of levels in reference data
!> @param[in] ndf_wth number of degrees of freedom per cell
!> @param[in] undf_wth num of DoFs in this partition for Wtheta
!> @param[in] map_wth index of DoFs for lowest cell for Wtheta
From 9f2b508af67421627bcae2ab8d1a9a012f7dff68 Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Wed, 19 Aug 2026 10:29:44 +0100
Subject: [PATCH 32/35] more science review changes
---
rose-stem/site/common/lfric_atm/tasks_lfric_atm.cylc | 8 ++++----
rose-stem/site/meto/groups/groups_lfric_atm.cylc | 8 ++++----
science/gungho/rose-meta/lfric-gungho/versions.py | 2 +-
.../source/algorithm/physics/external_forcing_alg_mod.X90 | 2 +-
.../algorithm/runtime_constants/physics_constants_mod.X90 | 4 ++++
.../kernel/external_forcing/convolution_2d_kernel_mod.F90 | 2 ++
.../external_forcing/newtonian_nudging_kernel_mod.F90 | 2 ++
7 files changed, 18 insertions(+), 10 deletions(-)
diff --git a/rose-stem/site/common/lfric_atm/tasks_lfric_atm.cylc b/rose-stem/site/common/lfric_atm/tasks_lfric_atm.cylc
index 6791d64ae0..afa16e5ca3 100644
--- a/rose-stem/site/common/lfric_atm/tasks_lfric_atm.cylc
+++ b/rose-stem/site/common/lfric_atm/tasks_lfric_atm.cylc
@@ -278,7 +278,7 @@
"plot_str": "plot_map.py $NODAL_DATA_DIR/lfric_diagnostics.nc $PLOT_DIR",
}) %}
-{% elif task_ns.conf_name == "nwp_gal9_nudging-spectral-C48_MG" %}
+{% elif task_ns.conf_name == "nwp_gal10_nudging-spectral-C48_MG" %}
{% do task_dict.update({
"opt_confs": ["nudging-spectral","um_dump"],
@@ -291,7 +291,7 @@
"plot_str": "plot_map.py $NODAL_DATA_DIR/lfric_diagnostics.nc $PLOT_DIR",
}) %}
-{% elif task_ns.conf_name == "nwp_gal9_nudging-spectral-coarse-C48_MG" %}
+{% elif task_ns.conf_name == "nwp_gal10_nudging-spectral-coarse-C48_MG" %}
{% do task_dict.update({
"opt_confs": ["nudging-spectral-coarse","um_dump"],
@@ -304,7 +304,7 @@
"plot_str": "plot_map.py $NODAL_DATA_DIR/lfric_diagnostics.nc $PLOT_DIR",
}) %}
-{% elif task_ns.conf_name == "nwp_gal9_nudging-newtonian-C48_MG" %}
+{% elif task_ns.conf_name == "nwp_gal10_nudging-newtonian-C48_MG" %}
{% do task_dict.update({
"opt_confs": ["nudging-newtonian","um_dump"],
@@ -317,7 +317,7 @@
"plot_str": "plot_map.py $NODAL_DATA_DIR/lfric_diagnostics.nc $PLOT_DIR",
}) %}
-{% elif task_ns.conf_name == "nwp_gal9_nudging-newtonian-coarse-C48_MG" %}
+{% elif task_ns.conf_name == "nwp_gal10_nudging-newtonian-coarse-C48_MG" %}
{% do task_dict.update({
"opt_confs": ["nudging-newtonian-coarse","um_dump"],
diff --git a/rose-stem/site/meto/groups/groups_lfric_atm.cylc b/rose-stem/site/meto/groups/groups_lfric_atm.cylc
index a66a7f1cfa..a7af35540a 100644
--- a/rose-stem/site/meto/groups/groups_lfric_atm.cylc
+++ b/rose-stem/site/meto/groups/groups_lfric_atm.cylc
@@ -64,10 +64,10 @@
"lfric_atm_canned_azspice_gnu_fast-debug-32bit",
],
"lfric_atm_azspice_nudging": [
- "lfric_atm_nwp_gal9_nudging-spectral-C48_MG_azspice_gnu_fast-debug-32bit",
- "lfric_atm_nwp_gal9_nudging-spectral-coarse-C48_MG_azspice_gnu_fast-debug-32bit",
- "lfric_atm_nwp_gal9_nudging-newtonian-C48_MG_azspice_gnu_fast-debug-32bit",
- "lfric_atm_nwp_gal9_nudging-newtonian-coarse-C48_MG_azspice_gnu_fast-debug-32bit",
+ "lfric_atm_nwp_gal10_nudging-spectral-C48_MG_azspice_gnu_fast-debug-32bit",
+ "lfric_atm_nwp_gal10_nudging-spectral-coarse-C48_MG_azspice_gnu_fast-debug-32bit",
+ "lfric_atm_nwp_gal10_nudging-newtonian-C48_MG_azspice_gnu_fast-debug-32bit",
+ "lfric_atm_nwp_gal10_nudging-newtonian-coarse-C48_MG_azspice_gnu_fast-debug-32bit",
],
"lfric_atm_azspice_developer": [
"lfric_atm_nwp_azspice_developer",
diff --git a/science/gungho/rose-meta/lfric-gungho/versions.py b/science/gungho/rose-meta/lfric-gungho/versions.py
index 0bd6b94fd4..704681ece7 100644
--- a/science/gungho/rose-meta/lfric-gungho/versions.py
+++ b/science/gungho/rose-meta/lfric-gungho/versions.py
@@ -58,7 +58,7 @@ def upgrade(self, config, meta_config=None):
config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
)
self.add_setting(
- config, ["namelist:nudging", "nudging_stop_time"], "144.0"
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
)
self.add_setting(
config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
diff --git a/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90 b/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
index 37cede0458..1d79d45132 100644
--- a/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
+++ b/science/gungho/source/algorithm/physics/external_forcing_alg_mod.X90
@@ -208,9 +208,9 @@ contains
! If nudging is to be applied, obtain reference fields
if ( theta_forcing == theta_forcing_nudging .or. &
wind_forcing == wind_forcing_nudging ) then
- call set_nudging_reference_fields(config, derived_fields, exner)
nudging_spinup_factor = compute_nudging_spinup_factor(config, time_now)
if (nudging_spinup_factor > 0.0_r_def) then
+ call set_nudging_reference_fields(config, derived_fields, exner)
call set_nudging_trop_level(config, trop_level, theta, exner_in_wth)
end if
end if
diff --git a/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90 b/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
index 5e2c7dc89e..cd00e80e1b 100644
--- a/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
+++ b/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
@@ -569,6 +569,10 @@ contains
nudging_top = real(nudging_level_top, r_def)
taper_top = real(nudging_taper_top, r_def)
+ ! NOTE: the weights array is indexed from 1 to nl. For the Wtheta weights,
+ ! this means the weights are shifted by 1 from our usual interpretation
+ ! of model level number. This is because PSyclone can't handle arrays
+ ! with an initial index of 0.
do k = 0, nl-1
k_real = real(k, r_def) + w3_offset
diff --git a/science/gungho/source/kernel/external_forcing/convolution_2d_kernel_mod.F90 b/science/gungho/source/kernel/external_forcing/convolution_2d_kernel_mod.F90
index f59dfff55f..6451803999 100644
--- a/science/gungho/source/kernel/external_forcing/convolution_2d_kernel_mod.F90
+++ b/science/gungho/source/kernel/external_forcing/convolution_2d_kernel_mod.F90
@@ -144,6 +144,8 @@ subroutine convolution_2d_code(nlayers, &
idx_fb = map_3d(1) + min_level_loc
idx_ft = map_3d(1) + max_level_loc
+
+ ! Index of vertical weights. These start from 1 (whether W3 or Wtheta)
idx_vb = 1 + min_level_loc
idx_vt = 1 + max_level_loc
diff --git a/science/gungho/source/kernel/external_forcing/newtonian_nudging_kernel_mod.F90 b/science/gungho/source/kernel/external_forcing/newtonian_nudging_kernel_mod.F90
index 98c522100f..2f53a5d603 100644
--- a/science/gungho/source/kernel/external_forcing/newtonian_nudging_kernel_mod.F90
+++ b/science/gungho/source/kernel/external_forcing/newtonian_nudging_kernel_mod.F90
@@ -128,6 +128,8 @@ subroutine newtonian_nudging_code(nlayers, &
idx_fb = map_3d(1) + min_level_loc
idx_ft = map_3d(1) + max_level_loc
+
+ ! Index of vertical weights. These start from 1 (whether W3 or Wtheta)
idx_vb = 1 + min_level_loc
idx_vt = 1 + max_level_loc
From a1815c50375a05f201f74e922d21fc928efc03af Mon Sep 17 00:00:00 2001
From: Thomas Bendall <14180399+tommbendall@users.noreply.github.com>
Date: Thu, 1 Oct 2026 11:40:39 +0100
Subject: [PATCH 33/35] code review comments
---
.../algorithm/physics/nudging_alg_mod.X90 | 103 ++++++++----------
.../physics_constants_mod.X90 | 6 +-
2 files changed, 49 insertions(+), 60 deletions(-)
diff --git a/science/gungho/source/algorithm/physics/nudging_alg_mod.X90 b/science/gungho/source/algorithm/physics/nudging_alg_mod.X90
index 287391bdfd..46a0f8e079 100644
--- a/science/gungho/source/algorithm/physics/nudging_alg_mod.X90
+++ b/science/gungho/source/algorithm/physics/nudging_alg_mod.X90
@@ -133,11 +133,8 @@ contains
coarse_mesh_id = coarse_mesh%get_id()
if (coarse_mesh_id /= field_ref%get_mesh_id()) then
- call log_event( &
- "nudging_alg: reference field is not on the mesh " // &
- "specified for nudging", &
- LOG_LEVEL_ERROR &
- )
+ call log_event("nudging_alg: reference field is not on the mesh " // &
+ "specified for nudging", LOG_LEVEL_ERROR)
end if
! Initialise coarse fields
@@ -167,14 +164,13 @@ contains
! ------------------------------------------------------------------------ !
case (nudging_method_newtonian)
! Simply nudge towards reference field, with no spectral impact
- call invoke( &
- setval_c(coarse_increment, 0.0_r_def) &
- )
- call invoke_newtonian_nudging_kernel_type( &
- coarse_increment, coarse_field_in, field_ref, &
- vertical_weights, tropopause_level, &
- min_level, max_level, time_weight, nvert &
- )
+ call invoke( setval_c(coarse_increment, 0.0_r_def) )
+ call invoke_newtonian_nudging_kernel_type(coarse_increment, &
+ coarse_field_in, field_ref, &
+ vertical_weights, &
+ tropopause_level, &
+ min_level, max_level, &
+ time_weight, nvert)
! ------------------------------------------------------------------------ !
! Convolution
@@ -183,17 +179,17 @@ contains
! Filter difference using a convolution in physical space
convolution_weights => get_convolution_weights(config, coarse_mesh)
call coarse_field_diff%initialise(fs, halo_depth=spectral_stencil_extent)
- call invoke( &
- X_minus_Y(coarse_field_diff, field_ref, coarse_field_in), &
- ! Compute convolution
- setval_c(coarse_increment, 0.0_r_def) &
- )
- call invoke_convolution_2d_kernel_type( &
- coarse_increment, coarse_field_diff, spectral_stencil_extent, &
- vertical_weights, convolution_weights, &
- tropopause_level, min_level, max_level, time_weight, nvert &
- )
-
+ call invoke( X_minus_Y(coarse_field_diff, field_ref, coarse_field_in), &
+ ! Compute convolution
+ setval_c(coarse_increment, 0.0_r_def) )
+ call invoke_convolution_2d_kernel_type(coarse_increment, &
+ coarse_field_diff, &
+ spectral_stencil_extent, &
+ vertical_weights, &
+ convolution_weights, &
+ tropopause_level, &
+ min_level, max_level, &
+ time_weight, nvert)
end select
! ------------------------------------------------------------------------ !
@@ -201,9 +197,8 @@ contains
! ------------------------------------------------------------------------ !
if (coarse_nudging) then
- call prolong_scalar_simple( &
- increment, coarse_increment, recovery_order_linear &
- )
+ call prolong_scalar_simple(increment, coarse_increment, &
+ recovery_order_linear)
else
call invoke( setval_X(increment, coarse_increment) )
end if
@@ -240,10 +235,8 @@ contains
if (time_hours < nudging_spinup_start) then
spinup_factor = 0.0_r_def
else if (time_hours < nudging_spinup_end) then
- spinup_factor = ( &
- (time_hours - nudging_spinup_start) &
- / (nudging_spinup_end - nudging_spinup_start) &
- )
+ spinup_factor = ( (time_hours - nudging_spinup_start) &
+ / (nudging_spinup_end - nudging_spinup_start) )
else if (time_hours < nudging_stop_time) then
spinup_factor = 1.0_r_def
else
@@ -312,10 +305,8 @@ contains
theta_trop => theta
end if
- call locate_tropopause_alg( &
- config, trop_level, theta_trop, exner_trop, &
- nudging_twod_mesh, trop_diag=.false. &
- )
+ call locate_tropopause_alg(config, trop_level, theta_trop, exner_trop, &
+ nudging_twod_mesh, trop_diag=.false.)
call invoke( int_inc_max_aX(nudging_min_tropopause_level, trop_level) )
#else
call invoke( int_setval_c(trop_level, nudging_min_tropopause_level) )
@@ -387,22 +378,20 @@ contains
! ------------------------------------------------------------------------ !
if (.not. (theta_forcing == theta_forcing_nudging .or. &
wind_forcing == wind_forcing_nudging) ) then
- call log_event( &
- "set_nudging_reference_fields: nudging to external data. This " // &
- "is currently only implemented to nudge both theta and wind", &
- LOG_LEVEL_ERROR &
- )
+ call log_event("set_nudging_reference_fields: nudging to external " // &
+ "data. This is currently only implemented to nudge " // &
+ "both theta and wind", LOG_LEVEL_ERROR)
end if
if (nudging_source == nudging_source_ecmwf) then
! Get external fields
- call derived_fields%get_field('temperature_nudging_ext_ref', T_nudging_ext_ref)
+ call derived_fields%get_field('temperature_nudging_ext_ref', &
+ T_nudging_ext_ref)
call derived_fields%get_field('u_nudging_ext_ref', u_nudging_ext_ref)
call derived_fields%get_field('v_nudging_ext_ref', v_nudging_ext_ref)
call derived_fields%get_field('exner_in_wth', exner_in_wth)
- call derived_fields%get_field( &
- 'surface_pressure_nudging_ext_ref', surfp_nudging_ext_ref &
- )
+ call derived_fields%get_field('surface_pressure_nudging_ext_ref', &
+ surfp_nudging_ext_ref)
! Map Exner fields to coarse mesh, if required
if (coarse_nudging) then
@@ -417,19 +406,21 @@ contains
end if
! Vertical remapping of external fields to model levels
- call invoke( &
- nudging_ecmwf_vert_interp_kernel_type( &
- theta_ref, u_in_w3_ref, v_in_w3_ref, &
- T_nudging_ext_ref, u_nudging_ext_ref, v_nudging_ext_ref, &
- exner_in_wth, exner_in_w3, surfp_nudging_ext_ref, &
- p_zero, kappa, num_ref_data_levels &
- ) &
- )
+ call invoke( nudging_ecmwf_vert_interp_kernel_type(theta_ref, &
+ u_in_w3_ref, &
+ v_in_w3_ref, &
+ T_nudging_ext_ref, &
+ u_nudging_ext_ref, &
+ v_nudging_ext_ref, &
+ exner_in_wth, &
+ exner_in_w3, &
+ surfp_nudging_ext_ref,&
+ p_zero, kappa, &
+ num_ref_data_levels) )
else
- call log_event( &
- "set_nudging_reference_fields: nudging_source is only currently " // &
- "implemented for nudging_source_ecmwf", LOG_LEVEL_ERROR &
- )
+ call log_event("set_nudging_reference_fields: nudging_source is " // &
+ "only currently implemented for nudging_source_ecmwf", &
+ LOG_LEVEL_ERROR)
end if
end subroutine set_nudging_reference_fields
diff --git a/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90 b/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
index cd00e80e1b..5b8bf50cae 100644
--- a/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
+++ b/science/gungho/source/algorithm/runtime_constants/physics_constants_mod.X90
@@ -467,10 +467,8 @@ contains
ndata = (2*stencil_depth + 1)**2
if (geometry /= geometry_spherical) then
- call log_event( &
- 'Spectral nudging only implemented for spherical geometry', &
- LOG_LEVEL_ERROR &
- )
+ call log_event("Spectral nudging only implemented for spherical " // &
+ "geometry", LOG_LEVEL_ERROR)
end if
if ( LPROF ) call start_timing( id, 'runtime_constants.physics' )
From 2f21f166c83597081944eff297b031a29cb76286 Mon Sep 17 00:00:00 2001
From: James Bruten <109733895+james-bruten-mo@users.noreply.github.com>
Date: Thu, 22 Oct 2026 14:40:14 +0100
Subject: [PATCH 34/35] apply macros
---
.../rose-meta/lfric-adjoint_tests/versions.py | 79 +++++++++++++++++++
.../rose-meta/lfric-gravity_wave/versions.py | 79 +++++++++++++++++++
.../rose-meta/lfric-gungho_model/versions.py | 79 +++++++++++++++++++
.../rose-meta/jedi_common/versions.py | 79 +++++++++++++++++++
.../rose-meta/jedi_forecast/versions.py | 79 +++++++++++++++++++
.../jedi_forecast_pseudo/versions.py | 79 +++++++++++++++++++
.../rose-meta/jedi_id_tlm_tests/versions.py | 79 +++++++++++++++++++
.../rose-meta/jedi_lfric_tests/versions.py | 79 +++++++++++++++++++
.../jedi_tlm_forecast_tl/versions.py | 79 +++++++++++++++++++
.../rose-meta/jedi_tlm_tests/versions.py | 79 +++++++++++++++++++
.../jules/rose-meta/lfric-jules/versions.py | 79 +++++++++++++++++++
.../rose-meta/lfric-lfric2lfric/versions.py | 79 +++++++++++++++++++
.../rose-meta/lfric-lfric_atm/versions.py | 79 +++++++++++++++++++
.../rose-meta/lfric-linear_model/versions.py | 79 +++++++++++++++++++
.../lfric-name_transport/versions.py | 79 +++++++++++++++++++
.../ngarch/rose-meta/lfric-ngarch/versions.py | 79 +++++++++++++++++++
.../rose-meta/lfric-shallow_water/versions.py | 79 +++++++++++++++++++
.../rose-meta/lfric-transport/versions.py | 79 +++++++++++++++++++
.../rose-meta/coupling/versions.py | 79 +++++++++++++++++++
.../jedi_lfric_interface/versions.py | 79 +++++++++++++++++++
rose-stem/app/adjoint_tests/rose-app.conf | 26 ++++--
rose-stem/app/gravity_wave/rose-app.conf | 24 ++++--
rose-stem/app/gungho_model/rose-app.conf | 24 ++++--
rose-stem/app/jedi_forecast/rose-app.conf | 26 ++++--
.../app/jedi_forecast_pseudo/rose-app.conf | 24 ++++--
rose-stem/app/jedi_id_tlm_tests/rose-app.conf | 26 ++++--
rose-stem/app/jedi_lfric_tests/rose-app.conf | 26 ++++--
.../app/jedi_tlm_forecast_tl/rose-app.conf | 26 ++++--
rose-stem/app/jedi_tlm_tests/rose-app.conf | 26 ++++--
rose-stem/app/jules/rose-app.conf | 26 ++++--
rose-stem/app/lfric2lfric/rose-app.conf | 24 ++++--
.../opt/rose-app-nudging-newtonian.conf | 1 +
.../opt/rose-app-nudging-spectral-coarse.conf | 3 +
.../opt/rose-app-nudging-spectral.conf | 4 +
rose-stem/app/lfric_atm/rose-app.conf | 26 ++++--
rose-stem/app/linear_model/rose-app.conf | 26 ++++--
rose-stem/app/name_transport/rose-app.conf | 26 ++++--
rose-stem/app/ngarch/rose-app.conf | 24 ++++--
rose-stem/app/shallow_water/rose-app.conf | 24 ++++--
rose-stem/app/transport/rose-app.conf | 26 ++++--
.../rose-meta/lfric-adjoint/versions.py | 79 +++++++++++++++++++
.../gungho/rose-meta/lfric-gungho/versions.py | 13 +--
.../linear/rose-meta/lfric-linear/versions.py | 79 +++++++++++++++++++
43 files changed, 2071 insertions(+), 118 deletions(-)
diff --git a/applications/adjoint_tests/rose-meta/lfric-adjoint_tests/versions.py b/applications/adjoint_tests/rose-meta/lfric-adjoint_tests/versions.py
index e33d8e39bc..3e9051d6c4 100644
--- a/applications/adjoint_tests/rose-meta/lfric-adjoint_tests/versions.py
+++ b/applications/adjoint_tests/rose-meta/lfric-adjoint_tests/versions.py
@@ -349,3 +349,82 @@ def upgrade(self, config, meta_config=None):
)
return config, self.reports
+
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2_t400"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
+ )
+ # Remove retired settings
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
+
+ return config, self.reports
diff --git a/applications/gravity_wave/rose-meta/lfric-gravity_wave/versions.py b/applications/gravity_wave/rose-meta/lfric-gravity_wave/versions.py
index e33d8e39bc..3e9051d6c4 100644
--- a/applications/gravity_wave/rose-meta/lfric-gravity_wave/versions.py
+++ b/applications/gravity_wave/rose-meta/lfric-gravity_wave/versions.py
@@ -349,3 +349,82 @@ def upgrade(self, config, meta_config=None):
)
return config, self.reports
+
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2_t400"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
+ )
+ # Remove retired settings
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
+
+ return config, self.reports
diff --git a/applications/gungho_model/rose-meta/lfric-gungho_model/versions.py b/applications/gungho_model/rose-meta/lfric-gungho_model/versions.py
index e33d8e39bc..3e9051d6c4 100644
--- a/applications/gungho_model/rose-meta/lfric-gungho_model/versions.py
+++ b/applications/gungho_model/rose-meta/lfric-gungho_model/versions.py
@@ -349,3 +349,82 @@ def upgrade(self, config, meta_config=None):
)
return config, self.reports
+
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2_t400"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
+ )
+ # Remove retired settings
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
+
+ return config, self.reports
diff --git a/applications/jedi_lfric_tests/rose-meta/jedi_common/versions.py b/applications/jedi_lfric_tests/rose-meta/jedi_common/versions.py
index e33d8e39bc..3e9051d6c4 100644
--- a/applications/jedi_lfric_tests/rose-meta/jedi_common/versions.py
+++ b/applications/jedi_lfric_tests/rose-meta/jedi_common/versions.py
@@ -349,3 +349,82 @@ def upgrade(self, config, meta_config=None):
)
return config, self.reports
+
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2_t400"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
+ )
+ # Remove retired settings
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
+
+ return config, self.reports
diff --git a/applications/jedi_lfric_tests/rose-meta/jedi_forecast/versions.py b/applications/jedi_lfric_tests/rose-meta/jedi_forecast/versions.py
index cf2f41cb8d..0454b6ccb5 100644
--- a/applications/jedi_lfric_tests/rose-meta/jedi_forecast/versions.py
+++ b/applications/jedi_lfric_tests/rose-meta/jedi_forecast/versions.py
@@ -358,3 +358,82 @@ def upgrade(self, config, meta_config=None):
)
return config, self.reports
+
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2_t400"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
+ )
+ # Remove retired settings
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
+
+ return config, self.reports
diff --git a/applications/jedi_lfric_tests/rose-meta/jedi_forecast_pseudo/versions.py b/applications/jedi_lfric_tests/rose-meta/jedi_forecast_pseudo/versions.py
index e33d8e39bc..3e9051d6c4 100644
--- a/applications/jedi_lfric_tests/rose-meta/jedi_forecast_pseudo/versions.py
+++ b/applications/jedi_lfric_tests/rose-meta/jedi_forecast_pseudo/versions.py
@@ -349,3 +349,82 @@ def upgrade(self, config, meta_config=None):
)
return config, self.reports
+
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2_t400"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
+ )
+ # Remove retired settings
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
+
+ return config, self.reports
diff --git a/applications/jedi_lfric_tests/rose-meta/jedi_id_tlm_tests/versions.py b/applications/jedi_lfric_tests/rose-meta/jedi_id_tlm_tests/versions.py
index e33d8e39bc..3e9051d6c4 100644
--- a/applications/jedi_lfric_tests/rose-meta/jedi_id_tlm_tests/versions.py
+++ b/applications/jedi_lfric_tests/rose-meta/jedi_id_tlm_tests/versions.py
@@ -349,3 +349,82 @@ def upgrade(self, config, meta_config=None):
)
return config, self.reports
+
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2_t400"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
+ )
+ # Remove retired settings
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
+
+ return config, self.reports
diff --git a/applications/jedi_lfric_tests/rose-meta/jedi_lfric_tests/versions.py b/applications/jedi_lfric_tests/rose-meta/jedi_lfric_tests/versions.py
index e33d8e39bc..3e9051d6c4 100644
--- a/applications/jedi_lfric_tests/rose-meta/jedi_lfric_tests/versions.py
+++ b/applications/jedi_lfric_tests/rose-meta/jedi_lfric_tests/versions.py
@@ -349,3 +349,82 @@ def upgrade(self, config, meta_config=None):
)
return config, self.reports
+
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2_t400"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
+ )
+ # Remove retired settings
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
+
+ return config, self.reports
diff --git a/applications/jedi_lfric_tests/rose-meta/jedi_tlm_forecast_tl/versions.py b/applications/jedi_lfric_tests/rose-meta/jedi_tlm_forecast_tl/versions.py
index e33d8e39bc..3e9051d6c4 100644
--- a/applications/jedi_lfric_tests/rose-meta/jedi_tlm_forecast_tl/versions.py
+++ b/applications/jedi_lfric_tests/rose-meta/jedi_tlm_forecast_tl/versions.py
@@ -349,3 +349,82 @@ def upgrade(self, config, meta_config=None):
)
return config, self.reports
+
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2_t400"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
+ )
+ # Remove retired settings
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
+
+ return config, self.reports
diff --git a/applications/jedi_lfric_tests/rose-meta/jedi_tlm_tests/versions.py b/applications/jedi_lfric_tests/rose-meta/jedi_tlm_tests/versions.py
index e33d8e39bc..3e9051d6c4 100644
--- a/applications/jedi_lfric_tests/rose-meta/jedi_tlm_tests/versions.py
+++ b/applications/jedi_lfric_tests/rose-meta/jedi_tlm_tests/versions.py
@@ -349,3 +349,82 @@ def upgrade(self, config, meta_config=None):
)
return config, self.reports
+
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2_t400"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
+ )
+ # Remove retired settings
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
+
+ return config, self.reports
diff --git a/applications/jules/rose-meta/lfric-jules/versions.py b/applications/jules/rose-meta/lfric-jules/versions.py
index e33d8e39bc..3e9051d6c4 100644
--- a/applications/jules/rose-meta/lfric-jules/versions.py
+++ b/applications/jules/rose-meta/lfric-jules/versions.py
@@ -349,3 +349,82 @@ def upgrade(self, config, meta_config=None):
)
return config, self.reports
+
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2_t400"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
+ )
+ # Remove retired settings
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
+
+ return config, self.reports
diff --git a/applications/lfric2lfric/rose-meta/lfric-lfric2lfric/versions.py b/applications/lfric2lfric/rose-meta/lfric-lfric2lfric/versions.py
index 061999577b..e69be641ee 100644
--- a/applications/lfric2lfric/rose-meta/lfric-lfric2lfric/versions.py
+++ b/applications/lfric2lfric/rose-meta/lfric-lfric2lfric/versions.py
@@ -430,3 +430,82 @@ def upgrade(self, config, meta_config=None):
)
return config, self.reports
+
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2_t400"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
+ )
+ # Remove retired settings
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
+
+ return config, self.reports
diff --git a/applications/lfric_atm/rose-meta/lfric-lfric_atm/versions.py b/applications/lfric_atm/rose-meta/lfric-lfric_atm/versions.py
index e33d8e39bc..3e9051d6c4 100644
--- a/applications/lfric_atm/rose-meta/lfric-lfric_atm/versions.py
+++ b/applications/lfric_atm/rose-meta/lfric-lfric_atm/versions.py
@@ -349,3 +349,82 @@ def upgrade(self, config, meta_config=None):
)
return config, self.reports
+
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2_t400"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
+ )
+ # Remove retired settings
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
+
+ return config, self.reports
diff --git a/applications/linear_model/rose-meta/lfric-linear_model/versions.py b/applications/linear_model/rose-meta/lfric-linear_model/versions.py
index e33d8e39bc..3e9051d6c4 100644
--- a/applications/linear_model/rose-meta/lfric-linear_model/versions.py
+++ b/applications/linear_model/rose-meta/lfric-linear_model/versions.py
@@ -349,3 +349,82 @@ def upgrade(self, config, meta_config=None):
)
return config, self.reports
+
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2_t400"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
+ )
+ # Remove retired settings
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
+
+ return config, self.reports
diff --git a/applications/name_transport/rose-meta/lfric-name_transport/versions.py b/applications/name_transport/rose-meta/lfric-name_transport/versions.py
index e33d8e39bc..3e9051d6c4 100644
--- a/applications/name_transport/rose-meta/lfric-name_transport/versions.py
+++ b/applications/name_transport/rose-meta/lfric-name_transport/versions.py
@@ -349,3 +349,82 @@ def upgrade(self, config, meta_config=None):
)
return config, self.reports
+
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2_t400"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
+ )
+ # Remove retired settings
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
+
+ return config, self.reports
diff --git a/applications/ngarch/rose-meta/lfric-ngarch/versions.py b/applications/ngarch/rose-meta/lfric-ngarch/versions.py
index e33d8e39bc..3e9051d6c4 100644
--- a/applications/ngarch/rose-meta/lfric-ngarch/versions.py
+++ b/applications/ngarch/rose-meta/lfric-ngarch/versions.py
@@ -349,3 +349,82 @@ def upgrade(self, config, meta_config=None):
)
return config, self.reports
+
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2_t400"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
+ )
+ # Remove retired settings
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
+
+ return config, self.reports
diff --git a/applications/shallow_water/rose-meta/lfric-shallow_water/versions.py b/applications/shallow_water/rose-meta/lfric-shallow_water/versions.py
index e33d8e39bc..3e9051d6c4 100644
--- a/applications/shallow_water/rose-meta/lfric-shallow_water/versions.py
+++ b/applications/shallow_water/rose-meta/lfric-shallow_water/versions.py
@@ -349,3 +349,82 @@ def upgrade(self, config, meta_config=None):
)
return config, self.reports
+
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2_t400"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
+ )
+ # Remove retired settings
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
+
+ return config, self.reports
diff --git a/applications/transport/rose-meta/lfric-transport/versions.py b/applications/transport/rose-meta/lfric-transport/versions.py
index e33d8e39bc..3e9051d6c4 100644
--- a/applications/transport/rose-meta/lfric-transport/versions.py
+++ b/applications/transport/rose-meta/lfric-transport/versions.py
@@ -349,3 +349,82 @@ def upgrade(self, config, meta_config=None):
)
return config, self.reports
+
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2_t400"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
+ )
+ # Remove retired settings
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
+
+ return config, self.reports
diff --git a/interfaces/coupled_interface/rose-meta/coupling/versions.py b/interfaces/coupled_interface/rose-meta/coupling/versions.py
index e33d8e39bc..3e9051d6c4 100644
--- a/interfaces/coupled_interface/rose-meta/coupling/versions.py
+++ b/interfaces/coupled_interface/rose-meta/coupling/versions.py
@@ -349,3 +349,82 @@ def upgrade(self, config, meta_config=None):
)
return config, self.reports
+
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2_t400"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
+ )
+ # Remove retired settings
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
+
+ return config, self.reports
diff --git a/interfaces/jedi_lfric_interface/rose-meta/jedi_lfric_interface/versions.py b/interfaces/jedi_lfric_interface/rose-meta/jedi_lfric_interface/versions.py
index e33d8e39bc..3e9051d6c4 100644
--- a/interfaces/jedi_lfric_interface/rose-meta/jedi_lfric_interface/versions.py
+++ b/interfaces/jedi_lfric_interface/rose-meta/jedi_lfric_interface/versions.py
@@ -349,3 +349,82 @@ def upgrade(self, config, meta_config=None):
)
return config, self.reports
+
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2_t400"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
+ )
+ # Remove retired settings
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
+
+ return config, self.reports
diff --git a/rose-stem/app/adjoint_tests/rose-app.conf b/rose-stem/app/adjoint_tests/rose-app.conf
index 2f4cc32c07..98a5abe69c 100644
--- a/rose-stem/app/adjoint_tests/rose-app.conf
+++ b/rose-stem/app/adjoint_tests/rose-app.conf
@@ -1,4 +1,4 @@
-meta=lfric-adjoint_tests/vn3.2_t400
+meta=lfric-adjoint_tests/vn3.2_t670
[command]
default=rose env-cat iodef_temp.xml -o iodef.xml; $LAUNCH_SCRIPT/launch-exe
@@ -1514,18 +1514,30 @@ dynamics_mesh_name='dynamics'
multires_coupling_mesh_tags='dynamics'
multires_coupling_mode='test'
negative_correction='none'
-!!nudging_mesh_name=''
+!!nudging_mesh_name='dynamics'
physics_mesh_name='aerosol'
reconstruction='simple'
recovery_order='linear'
[!!namelist:nudging]
-nudge_data_levels=137
-nudging_level_bottom=5
+nudging_level_bottom=6
+nudging_level_taper_bottom=5
+nudging_level_taper_top=52
nudging_level_top=52
-nudging_source='era'
-nudging_width_bottom=1
-nudging_width_top=0
+nudging_method='convolution'
+nudging_min_tropopause_level=48
+nudging_relax_time_theta=6.0
+nudging_relax_time_u=6.0
+nudging_relax_time_v=6.0
+nudging_source='ecmwf'
+nudging_spinup_end=24.0
+nudging_spinup_start=12.0
+nudging_stop_time=10000.0
+num_ref_data_levels=137
+!!spectral_envelope_width=0.1
+!!spectral_kmax=20
+!!spectral_kmin=2
+!!spectral_stencil_extent=12
[!!namelist:orbit]
!!arg_periapsis=1.796767421
diff --git a/rose-stem/app/gravity_wave/rose-app.conf b/rose-stem/app/gravity_wave/rose-app.conf
index 8d67eee73c..acf6ac8b38 100644
--- a/rose-stem/app/gravity_wave/rose-app.conf
+++ b/rose-stem/app/gravity_wave/rose-app.conf
@@ -1,4 +1,4 @@
-meta=lfric-gravity_wave/vn3.2_t400
+meta=lfric-gravity_wave/vn3.2_t670
[command]
default=$CORE_ROOT_DIR/bin/tweak_iodef; \
@@ -1456,12 +1456,24 @@ coarse_rad_aerosol=.false.
!!nudging_mesh_name=''
[!!namelist:nudging]
-nudge_data_levels=137
-nudging_level_bottom=5
+nudging_level_bottom=6
+nudging_level_taper_bottom=5
+nudging_level_taper_top=52
nudging_level_top=52
-nudging_source='era'
-nudging_width_bottom=1
-nudging_width_top=0
+nudging_method='convolution'
+nudging_min_tropopause_level=48
+nudging_relax_time_theta=6.0
+nudging_relax_time_u=6.0
+nudging_relax_time_v=6.0
+nudging_source='ecmwf'
+nudging_spinup_end=24.0
+nudging_spinup_start=12.0
+nudging_stop_time=10000.0
+num_ref_data_levels=137
+!!spectral_envelope_width=0.1
+!!spectral_kmax=20
+!!spectral_kmin=2
+!!spectral_stencil_extent=12
[!!namelist:orbit]
!!arg_periapsis=1.796767421
diff --git a/rose-stem/app/gungho_model/rose-app.conf b/rose-stem/app/gungho_model/rose-app.conf
index ec9ee83ac1..aea433d724 100644
--- a/rose-stem/app/gungho_model/rose-app.conf
+++ b/rose-stem/app/gungho_model/rose-app.conf
@@ -1,4 +1,4 @@
-meta=lfric-gungho_model/vn3.2_t400
+meta=lfric-gungho_model/vn3.2_t670
[command]
default=$CORE_ROOT_DIR/bin/tweak_iodef ; \
@@ -1478,12 +1478,24 @@ coarse_rad_aerosol=.false.
orography_mesh_name='dynamics'
[!!namelist:nudging]
-nudge_data_levels=137
-nudging_level_bottom=5
+nudging_level_bottom=6
+nudging_level_taper_bottom=5
+nudging_level_taper_top=52
nudging_level_top=52
-nudging_source='era'
-nudging_width_bottom=1
-nudging_width_top=0
+nudging_method='convolution'
+nudging_min_tropopause_level=48
+nudging_relax_time_theta=6.0
+nudging_relax_time_u=6.0
+nudging_relax_time_v=6.0
+nudging_source='ecmwf'
+nudging_spinup_end=24.0
+nudging_spinup_start=12.0
+nudging_stop_time=10000.0
+num_ref_data_levels=137
+!!spectral_envelope_width=0.1
+!!spectral_kmax=20
+!!spectral_kmin=2
+!!spectral_stencil_extent=12
[!!namelist:orbit]
!!arg_periapsis=1.796767421
diff --git a/rose-stem/app/jedi_forecast/rose-app.conf b/rose-stem/app/jedi_forecast/rose-app.conf
index fb0ca33a38..146e396937 100644
--- a/rose-stem/app/jedi_forecast/rose-app.conf
+++ b/rose-stem/app/jedi_forecast/rose-app.conf
@@ -1,4 +1,4 @@
-meta=jedi_forecast/vn3.2_t400
+meta=jedi_forecast/vn3.2_t670
[command]
default=$CORE_ROOT_DIR/bin/tweak_iodef; \
@@ -1535,18 +1535,30 @@ dynamics_mesh_name='dynamics'
multires_coupling_mesh_tags='dynamics'
multires_coupling_mode='test'
negative_correction='none'
-!!nudging_mesh_name=''
+!!nudging_mesh_name='dynamics'
physics_mesh_name='aerosol'
reconstruction='simple'
recovery_order='linear'
[!!namelist:nudging]
-nudge_data_levels=137
-nudging_level_bottom=5
+nudging_level_bottom=6
+nudging_level_taper_bottom=5
+nudging_level_taper_top=52
nudging_level_top=52
-nudging_source='era'
-nudging_width_bottom=1
-nudging_width_top=0
+nudging_method='convolution'
+nudging_min_tropopause_level=48
+nudging_relax_time_theta=6.0
+nudging_relax_time_u=6.0
+nudging_relax_time_v=6.0
+nudging_source='ecmwf'
+nudging_spinup_end=24.0
+nudging_spinup_start=12.0
+nudging_stop_time=10000.0
+num_ref_data_levels=137
+!!spectral_envelope_width=0.1
+!!spectral_kmax=20
+!!spectral_kmin=2
+!!spectral_stencil_extent=12
[namelist:orbit]
!!arg_periapsis=1.796767421
diff --git a/rose-stem/app/jedi_forecast_pseudo/rose-app.conf b/rose-stem/app/jedi_forecast_pseudo/rose-app.conf
index bb7506ff07..e081e29a2c 100644
--- a/rose-stem/app/jedi_forecast_pseudo/rose-app.conf
+++ b/rose-stem/app/jedi_forecast_pseudo/rose-app.conf
@@ -1,4 +1,4 @@
-meta=jedi_forecast_pseudo/vn3.2_t400
+meta=jedi_forecast_pseudo/vn3.2_t670
[command]
default=$CORE_ROOT_DIR/bin/tweak_iodef; \
@@ -1529,12 +1529,24 @@ coarse_rad_aerosol=.false.
!!nudging_mesh_name=''
[!!namelist:nudging]
-nudge_data_levels=137
-nudging_level_bottom=5
+nudging_level_bottom=6
+nudging_level_taper_bottom=5
+nudging_level_taper_top=52
nudging_level_top=52
-nudging_source='era'
-nudging_width_bottom=1
-nudging_width_top=0
+nudging_method='convolution'
+nudging_min_tropopause_level=48
+nudging_relax_time_theta=6.0
+nudging_relax_time_u=6.0
+nudging_relax_time_v=6.0
+nudging_source='ecmwf'
+nudging_spinup_end=24.0
+nudging_spinup_start=12.0
+nudging_stop_time=10000.0
+num_ref_data_levels=137
+!!spectral_envelope_width=0.1
+!!spectral_kmax=20
+!!spectral_kmin=2
+!!spectral_stencil_extent=12
[!!namelist:orbit]
!!arg_periapsis=1.796767421
diff --git a/rose-stem/app/jedi_id_tlm_tests/rose-app.conf b/rose-stem/app/jedi_id_tlm_tests/rose-app.conf
index 76d906b050..ca099b04e6 100644
--- a/rose-stem/app/jedi_id_tlm_tests/rose-app.conf
+++ b/rose-stem/app/jedi_id_tlm_tests/rose-app.conf
@@ -1,4 +1,4 @@
-meta=jedi_id_tlm_tests/vn3.2_t400
+meta=jedi_id_tlm_tests/vn3.2_t670
[command]
default=$CORE_ROOT_DIR/bin/tweak_iodef; \
@@ -1558,18 +1558,30 @@ dynamics_mesh_name='dynamics'
multires_coupling_mesh_tags='dynamics'
multires_coupling_mode='test'
negative_correction='none'
-!!nudging_mesh_name=''
+!!nudging_mesh_name='dynamics'
physics_mesh_name='aerosol'
reconstruction='simple'
recovery_order='linear'
[!!namelist:nudging]
-nudge_data_levels=137
-nudging_level_bottom=5
+nudging_level_bottom=6
+nudging_level_taper_bottom=5
+nudging_level_taper_top=52
nudging_level_top=52
-nudging_source='era'
-nudging_width_bottom=1
-nudging_width_top=0
+nudging_method='convolution'
+nudging_min_tropopause_level=48
+nudging_relax_time_theta=6.0
+nudging_relax_time_u=6.0
+nudging_relax_time_v=6.0
+nudging_source='ecmwf'
+nudging_spinup_end=24.0
+nudging_spinup_start=12.0
+nudging_stop_time=10000.0
+num_ref_data_levels=137
+!!spectral_envelope_width=0.1
+!!spectral_kmax=20
+!!spectral_kmin=2
+!!spectral_stencil_extent=12
[!!namelist:orbit]
!!arg_periapsis=1.796767421
diff --git a/rose-stem/app/jedi_lfric_tests/rose-app.conf b/rose-stem/app/jedi_lfric_tests/rose-app.conf
index d37ecec260..35fa74f98b 100644
--- a/rose-stem/app/jedi_lfric_tests/rose-app.conf
+++ b/rose-stem/app/jedi_lfric_tests/rose-app.conf
@@ -1,4 +1,4 @@
-meta=jedi_lfric_tests/vn3.2_t400
+meta=jedi_lfric_tests/vn3.2_t670
[command]
default=rose env-cat iodef_temp.xml -o iodef.xml; $LAUNCH_SCRIPT/launch-exe
@@ -1560,18 +1560,30 @@ dynamics_mesh_name='dynamics'
multires_coupling_mesh_tags='dynamics'
multires_coupling_mode='test'
negative_correction='none'
-!!nudging_mesh_name=''
+!!nudging_mesh_name='dynamics'
physics_mesh_name='aerosol'
reconstruction='simple'
recovery_order='linear'
[!!namelist:nudging]
-nudge_data_levels=137
-nudging_level_bottom=5
+nudging_level_bottom=6
+nudging_level_taper_bottom=5
+nudging_level_taper_top=52
nudging_level_top=52
-nudging_source='era'
-nudging_width_bottom=1
-nudging_width_top=0
+nudging_method='convolution'
+nudging_min_tropopause_level=48
+nudging_relax_time_theta=6.0
+nudging_relax_time_u=6.0
+nudging_relax_time_v=6.0
+nudging_source='ecmwf'
+nudging_spinup_end=24.0
+nudging_spinup_start=12.0
+nudging_stop_time=10000.0
+num_ref_data_levels=137
+!!spectral_envelope_width=0.1
+!!spectral_kmax=20
+!!spectral_kmin=2
+!!spectral_stencil_extent=12
[!!namelist:orbit]
!!arg_periapsis=1.796767421
diff --git a/rose-stem/app/jedi_tlm_forecast_tl/rose-app.conf b/rose-stem/app/jedi_tlm_forecast_tl/rose-app.conf
index 0ad97d8130..83027ba9b0 100644
--- a/rose-stem/app/jedi_tlm_forecast_tl/rose-app.conf
+++ b/rose-stem/app/jedi_tlm_forecast_tl/rose-app.conf
@@ -1,4 +1,4 @@
-meta=jedi_tlm_forecast_tl/vn3.2_t400
+meta=jedi_tlm_forecast_tl/vn3.2_t670
[command]
default=$CORE_ROOT_DIR/bin/tweak_iodef; \
@@ -1558,18 +1558,30 @@ dynamics_mesh_name='dynamics'
multires_coupling_mesh_tags='dynamics'
multires_coupling_mode='test'
negative_correction='none'
-!!nudging_mesh_name=''
+!!nudging_mesh_name='dynamics'
physics_mesh_name='aerosol'
reconstruction='simple'
recovery_order='linear'
[!!namelist:nudging]
-nudge_data_levels=137
-nudging_level_bottom=5
+nudging_level_bottom=6
+nudging_level_taper_bottom=5
+nudging_level_taper_top=52
nudging_level_top=52
-nudging_source='era'
-nudging_width_bottom=1
-nudging_width_top=0
+nudging_method='convolution'
+nudging_min_tropopause_level=48
+nudging_relax_time_theta=6.0
+nudging_relax_time_u=6.0
+nudging_relax_time_v=6.0
+nudging_source='ecmwf'
+nudging_spinup_end=24.0
+nudging_spinup_start=12.0
+nudging_stop_time=10000.0
+num_ref_data_levels=137
+!!spectral_envelope_width=0.1
+!!spectral_kmax=20
+!!spectral_kmin=2
+!!spectral_stencil_extent=12
[!!namelist:orbit]
!!arg_periapsis=1.796767421
diff --git a/rose-stem/app/jedi_tlm_tests/rose-app.conf b/rose-stem/app/jedi_tlm_tests/rose-app.conf
index 35f6b1ccf0..863f78479c 100644
--- a/rose-stem/app/jedi_tlm_tests/rose-app.conf
+++ b/rose-stem/app/jedi_tlm_tests/rose-app.conf
@@ -1,4 +1,4 @@
-meta=jedi_tlm_tests/vn3.2_t400
+meta=jedi_tlm_tests/vn3.2_t670
[command]
default=$CORE_ROOT_DIR/bin/tweak_iodef; \
@@ -1558,18 +1558,30 @@ dynamics_mesh_name='dynamics'
multires_coupling_mesh_tags='dynamics'
multires_coupling_mode='test'
negative_correction='none'
-!!nudging_mesh_name=''
+!!nudging_mesh_name='dynamics'
physics_mesh_name='aerosol'
reconstruction='simple'
recovery_order='linear'
[!!namelist:nudging]
-nudge_data_levels=137
-nudging_level_bottom=5
+nudging_level_bottom=6
+nudging_level_taper_bottom=5
+nudging_level_taper_top=52
nudging_level_top=52
-nudging_source='era'
-nudging_width_bottom=1
-nudging_width_top=0
+nudging_method='convolution'
+nudging_min_tropopause_level=48
+nudging_relax_time_theta=6.0
+nudging_relax_time_u=6.0
+nudging_relax_time_v=6.0
+nudging_source='ecmwf'
+nudging_spinup_end=24.0
+nudging_spinup_start=12.0
+nudging_stop_time=10000.0
+num_ref_data_levels=137
+!!spectral_envelope_width=0.1
+!!spectral_kmax=20
+!!spectral_kmin=2
+!!spectral_stencil_extent=12
[!!namelist:orbit]
!!arg_periapsis=1.796767421
diff --git a/rose-stem/app/jules/rose-app.conf b/rose-stem/app/jules/rose-app.conf
index 2a35f7e3d1..ae7601ee7e 100644
--- a/rose-stem/app/jules/rose-app.conf
+++ b/rose-stem/app/jules/rose-app.conf
@@ -1,4 +1,4 @@
-meta=lfric-jules/vn3.2_t400
+meta=lfric-jules/vn3.2_t670
[command]
default=$CORE_ROOT_DIR/bin/tweak_iodef; \
@@ -1550,18 +1550,30 @@ dynamics_mesh_name='dynamics'
multires_coupling_mesh_tags='dynamics'
multires_coupling_mode='test'
negative_correction='none'
-!!nudging_mesh_name=''
+!!nudging_mesh_name='dynamics'
physics_mesh_name='aerosol'
reconstruction='simple'
recovery_order='linear'
[!!namelist:nudging]
-nudge_data_levels=137
-nudging_level_bottom=5
+nudging_level_bottom=6
+nudging_level_taper_bottom=5
+nudging_level_taper_top=52
nudging_level_top=52
-nudging_source='era'
-nudging_width_bottom=1
-nudging_width_top=0
+nudging_method='convolution'
+nudging_min_tropopause_level=48
+nudging_relax_time_theta=6.0
+nudging_relax_time_u=6.0
+nudging_relax_time_v=6.0
+nudging_source='ecmwf'
+nudging_spinup_end=24.0
+nudging_spinup_start=12.0
+nudging_stop_time=10000.0
+num_ref_data_levels=137
+!!spectral_envelope_width=0.1
+!!spectral_kmax=20
+!!spectral_kmin=2
+!!spectral_stencil_extent=12
[!!namelist:orbit]
!!arg_periapsis=1.796767421
diff --git a/rose-stem/app/lfric2lfric/rose-app.conf b/rose-stem/app/lfric2lfric/rose-app.conf
index 3c704b4ad1..690e6476a0 100644
--- a/rose-stem/app/lfric2lfric/rose-app.conf
+++ b/rose-stem/app/lfric2lfric/rose-app.conf
@@ -1,4 +1,4 @@
-meta=lfric-lfric2lfric/vn3.2_t400
+meta=lfric-lfric2lfric/vn3.2_t670
[command]
default=$CORE_ROOT_DIR/bin/tweak_iodef; \
@@ -960,12 +960,24 @@ coarse_rad_aerosol=.false.
!!nudging_mesh_name=''
[!!namelist:nudging]
-nudge_data_levels=137
-nudging_level_bottom=5
+nudging_level_bottom=6
+nudging_level_taper_bottom=5
+nudging_level_taper_top=52
nudging_level_top=52
-nudging_source='era'
-nudging_width_bottom=1
-nudging_width_top=0
+nudging_method='convolution'
+nudging_min_tropopause_level=48
+nudging_relax_time_theta=6.0
+nudging_relax_time_u=6.0
+nudging_relax_time_v=6.0
+nudging_source='ecmwf'
+nudging_spinup_end=24.0
+nudging_spinup_start=12.0
+nudging_stop_time=10000.0
+num_ref_data_levels=137
+!!spectral_envelope_width=0.1
+!!spectral_kmax=20
+!!spectral_kmin=2
+!!spectral_stencil_extent=12
[!!namelist:orbit]
!!arg_periapsis=0
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf
index 7b5ec60e18..c34aad17a2 100644
--- a/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-newtonian.conf
@@ -16,6 +16,7 @@ use_multires_coupling=.true.
[namelist:multires_coupling]
aerosol_mesh_name='dynamics'
coarse_nudging=.false.
+lowest_order_aero_flag=.false.
nudging_mesh_name='dynamics'
orography_mesh_name='dynamics'
physics_mesh_name='dynamics'
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf
index 1f4f5c7faa..ebfab9b67a 100644
--- a/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral-coarse.conf
@@ -29,6 +29,9 @@ nudging_relax_time_u=1.0
nudging_relax_time_v=1.0
nudging_spinup_end=2.0
nudging_spinup_start=1.0
+spectral_envelope_width=0.1
+spectral_kmax=20
+spectral_kmin=2
spectral_stencil_extent=4
[namelist:section_choice]
diff --git a/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf
index 602029ce69..e2087e53a6 100644
--- a/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf
+++ b/rose-stem/app/lfric_atm/opt/rose-app-nudging-spectral.conf
@@ -16,6 +16,7 @@ use_multires_coupling=.true.
[namelist:multires_coupling]
aerosol_mesh_name='dynamics'
coarse_nudging=.false.
+lowest_order_aero_flag=.false.
nudging_mesh_name='dynamics'
orography_mesh_name='dynamics'
physics_mesh_name='dynamics'
@@ -26,6 +27,9 @@ nudging_relax_time_u=1.0
nudging_relax_time_v=1.0
nudging_spinup_end=2.0
nudging_spinup_start=1.0
+spectral_envelope_width=0.1
+spectral_kmax=20
+spectral_kmin=2
spectral_stencil_extent=6
[namelist:section_choice]
diff --git a/rose-stem/app/lfric_atm/rose-app.conf b/rose-stem/app/lfric_atm/rose-app.conf
index 15c46ca5f8..f283a1c14f 100644
--- a/rose-stem/app/lfric_atm/rose-app.conf
+++ b/rose-stem/app/lfric_atm/rose-app.conf
@@ -1,4 +1,4 @@
-meta=lfric-lfric_atm/vn3.2_t400
+meta=lfric-lfric_atm/vn3.2_t670
[command]
default=$CORE_ROOT_DIR/bin/tweak_iodef; \
@@ -1504,18 +1504,30 @@ dynamics_mesh_name='dynamics'
multires_coupling_mesh_tags='dynamics'
multires_coupling_mode='test'
negative_correction='none'
-!!nudging_mesh_name=''
+!!nudging_mesh_name='dynamics'
physics_mesh_name='aerosol'
reconstruction='simple'
recovery_order='linear'
[!!namelist:nudging]
-nudge_data_levels=137
-nudging_level_bottom=5
+nudging_level_bottom=6
+nudging_level_taper_bottom=5
+nudging_level_taper_top=52
nudging_level_top=52
-nudging_source='era'
-nudging_width_bottom=1
-nudging_width_top=0
+nudging_method='convolution'
+nudging_min_tropopause_level=48
+nudging_relax_time_theta=6.0
+nudging_relax_time_u=6.0
+nudging_relax_time_v=6.0
+nudging_source='ecmwf'
+nudging_spinup_end=24.0
+nudging_spinup_start=12.0
+nudging_stop_time=10000.0
+num_ref_data_levels=137
+!!spectral_envelope_width=0.1
+!!spectral_kmax=20
+!!spectral_kmin=2
+!!spectral_stencil_extent=12
[namelist:orbit]
!!arg_periapsis=1.796767421
diff --git a/rose-stem/app/linear_model/rose-app.conf b/rose-stem/app/linear_model/rose-app.conf
index 6302bd6de9..4f16dee337 100644
--- a/rose-stem/app/linear_model/rose-app.conf
+++ b/rose-stem/app/linear_model/rose-app.conf
@@ -1,4 +1,4 @@
-meta=lfric-linear_model/vn3.2_t400
+meta=lfric-linear_model/vn3.2_t670
[command]
default=rose env-cat iodef_temp.xml -o iodef.xml; $LAUNCH_SCRIPT/launch-exe
@@ -1510,18 +1510,30 @@ dynamics_mesh_name='dynamics'
multires_coupling_mesh_tags='dynamics'
multires_coupling_mode='test'
negative_correction='none'
-!!nudging_mesh_name=''
+!!nudging_mesh_name='dynamics'
physics_mesh_name='aerosol'
reconstruction='simple'
recovery_order='linear'
[!!namelist:nudging]
-nudge_data_levels=137
-nudging_level_bottom=5
+nudging_level_bottom=6
+nudging_level_taper_bottom=5
+nudging_level_taper_top=52
nudging_level_top=52
-nudging_source='era'
-nudging_width_bottom=1
-nudging_width_top=0
+nudging_method='convolution'
+nudging_min_tropopause_level=48
+nudging_relax_time_theta=6.0
+nudging_relax_time_u=6.0
+nudging_relax_time_v=6.0
+nudging_source='ecmwf'
+nudging_spinup_end=24.0
+nudging_spinup_start=12.0
+nudging_stop_time=10000.0
+num_ref_data_levels=137
+!!spectral_envelope_width=0.1
+!!spectral_kmax=20
+!!spectral_kmin=2
+!!spectral_stencil_extent=12
[!!namelist:orbit]
!!arg_periapsis=1.796767421
diff --git a/rose-stem/app/name_transport/rose-app.conf b/rose-stem/app/name_transport/rose-app.conf
index abe7c05b79..73093574a0 100644
--- a/rose-stem/app/name_transport/rose-app.conf
+++ b/rose-stem/app/name_transport/rose-app.conf
@@ -1,4 +1,4 @@
-meta=lfric-name_transport/vn3.2_t400
+meta=lfric-name_transport/vn3.2_t670
[command]
default=$CORE_ROOT_DIR/bin/tweak_iodef; \
@@ -1464,7 +1464,7 @@ dynamics_mesh_name='dynamics'
multires_coupling_mesh_tags='dynamics'
multires_coupling_mode='test'
negative_correction='none'
-!!nudging_mesh_name=''
+!!nudging_mesh_name='dynamics'
physics_mesh_name='coarse'
reconstruction='reversible'
recovery_order='linear'
@@ -1473,12 +1473,24 @@ recovery_order='linear'
transport_density=.true.
[!!namelist:nudging]
-nudge_data_levels=137
-nudging_level_bottom=5
+nudging_level_bottom=6
+nudging_level_taper_bottom=5
+nudging_level_taper_top=52
nudging_level_top=52
-nudging_source='era'
-nudging_width_bottom=1
-nudging_width_top=0
+nudging_method='convolution'
+nudging_min_tropopause_level=48
+nudging_relax_time_theta=6.0
+nudging_relax_time_u=6.0
+nudging_relax_time_v=6.0
+nudging_source='ecmwf'
+nudging_spinup_end=24.0
+nudging_spinup_start=12.0
+nudging_stop_time=10000.0
+num_ref_data_levels=137
+!!spectral_envelope_width=0.1
+!!spectral_kmax=20
+!!spectral_kmin=2
+!!spectral_stencil_extent=12
[!!namelist:orbit]
!!arg_periapsis=1.796767421
diff --git a/rose-stem/app/ngarch/rose-app.conf b/rose-stem/app/ngarch/rose-app.conf
index 9422d244b5..261f9625a2 100644
--- a/rose-stem/app/ngarch/rose-app.conf
+++ b/rose-stem/app/ngarch/rose-app.conf
@@ -1,4 +1,4 @@
-meta=lfric-ngarch/vn3.2_t400
+meta=lfric-ngarch/vn3.2_t670
[command]
default=$CORE_ROOT_DIR/bin/tweak_iodef; \
@@ -1535,12 +1535,24 @@ orography_mesh_name='dynamics'
method='casim'
[!!namelist:nudging]
-nudge_data_levels=137
-nudging_level_bottom=5
+nudging_level_bottom=6
+nudging_level_taper_bottom=5
+nudging_level_taper_top=52
nudging_level_top=52
-nudging_source='era'
-nudging_width_bottom=1
-nudging_width_top=0
+nudging_method='convolution'
+nudging_min_tropopause_level=48
+nudging_relax_time_theta=6.0
+nudging_relax_time_u=6.0
+nudging_relax_time_v=6.0
+nudging_source='ecmwf'
+nudging_spinup_end=24.0
+nudging_spinup_start=12.0
+nudging_stop_time=10000.0
+num_ref_data_levels=137
+!!spectral_envelope_width=0.1
+!!spectral_kmax=20
+!!spectral_kmin=2
+!!spectral_stencil_extent=12
[!!namelist:orbit]
!!arg_periapsis=1.796767421
diff --git a/rose-stem/app/shallow_water/rose-app.conf b/rose-stem/app/shallow_water/rose-app.conf
index 8ddd7abd50..aed5a866bf 100644
--- a/rose-stem/app/shallow_water/rose-app.conf
+++ b/rose-stem/app/shallow_water/rose-app.conf
@@ -1,4 +1,4 @@
-meta=lfric-shallow_water/vn3.2_t400
+meta=lfric-shallow_water/vn3.2_t670
[command]
default=$CORE_ROOT_DIR/bin/tweak_iodef; \
@@ -1448,12 +1448,24 @@ coarse_rad_aerosol=.false.
!!nudging_mesh_name=''
[!!namelist:nudging]
-nudge_data_levels=137
-nudging_level_bottom=5
+nudging_level_bottom=6
+nudging_level_taper_bottom=5
+nudging_level_taper_top=52
nudging_level_top=52
-nudging_source='era'
-nudging_width_bottom=1
-nudging_width_top=0
+nudging_method='convolution'
+nudging_min_tropopause_level=48
+nudging_relax_time_theta=6.0
+nudging_relax_time_u=6.0
+nudging_relax_time_v=6.0
+nudging_source='ecmwf'
+nudging_spinup_end=24.0
+nudging_spinup_start=12.0
+nudging_stop_time=10000.0
+num_ref_data_levels=137
+!!spectral_envelope_width=0.1
+!!spectral_kmax=20
+!!spectral_kmin=2
+!!spectral_stencil_extent=12
[!!namelist:orbit]
!!arg_periapsis=1.796767421
diff --git a/rose-stem/app/transport/rose-app.conf b/rose-stem/app/transport/rose-app.conf
index 2ccd2c6a10..8ccb3890db 100644
--- a/rose-stem/app/transport/rose-app.conf
+++ b/rose-stem/app/transport/rose-app.conf
@@ -1,4 +1,4 @@
-meta=lfric-transport/vn3.2_t400
+meta=lfric-transport/vn3.2_t670
[command]
default=$CORE_ROOT_DIR/bin/tweak_iodef; \
@@ -1463,18 +1463,30 @@ dynamics_mesh_name='dynamics'
multires_coupling_mesh_tags='dynamics'
multires_coupling_mode='test'
negative_correction='none'
-!!nudging_mesh_name=''
+!!nudging_mesh_name='dynamics'
physics_mesh_name='coarse'
reconstruction='reversible'
recovery_order='linear'
[!!namelist:nudging]
-nudge_data_levels=137
-nudging_level_bottom=5
+nudging_level_bottom=6
+nudging_level_taper_bottom=5
+nudging_level_taper_top=52
nudging_level_top=52
-nudging_source='era'
-nudging_width_bottom=1
-nudging_width_top=0
+nudging_method='convolution'
+nudging_min_tropopause_level=48
+nudging_relax_time_theta=6.0
+nudging_relax_time_u=6.0
+nudging_relax_time_v=6.0
+nudging_source='ecmwf'
+nudging_spinup_end=24.0
+nudging_spinup_start=12.0
+nudging_stop_time=10000.0
+num_ref_data_levels=137
+!!spectral_envelope_width=0.1
+!!spectral_kmax=20
+!!spectral_kmin=2
+!!spectral_stencil_extent=12
[!!namelist:orbit]
!!arg_periapsis=1.796767421
diff --git a/science/adjoint/rose-meta/lfric-adjoint/versions.py b/science/adjoint/rose-meta/lfric-adjoint/versions.py
index e33d8e39bc..3e9051d6c4 100644
--- a/science/adjoint/rose-meta/lfric-adjoint/versions.py
+++ b/science/adjoint/rose-meta/lfric-adjoint/versions.py
@@ -349,3 +349,82 @@ def upgrade(self, config, meta_config=None):
)
return config, self.reports
+
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2_t400"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
+ )
+ # Remove retired settings
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
+
+ return config, self.reports
diff --git a/science/gungho/rose-meta/lfric-gungho/versions.py b/science/gungho/rose-meta/lfric-gungho/versions.py
index 17b2067286..4fab710ea7 100644
--- a/science/gungho/rose-meta/lfric-gungho/versions.py
+++ b/science/gungho/rose-meta/lfric-gungho/versions.py
@@ -286,7 +286,6 @@ def upgrade(self, config, meta_config=None):
self.add_setting(config, ["namelist:vapour_relax", "times"], "0.0")
self.add_setting(config, ["namelist:vapour_relax", "timescale"], "1.0")
self.add_setting(config, ["namelist:vapour_relax", "variable"], "'mr'")
-
return config, self.reports
@@ -332,13 +331,13 @@ def upgrade(self, config, meta_config=None):
self.add_setting(
config, ["namelist:jules_sea_seaice", "snowpatch"], "0.02"
)
-
return config, self.reports
+
class vn32_t670(MacroUpgrade):
"""Upgrade macro for ticket #670 by Thomas Bendall."""
- BEFORE_TAG = "vn3.2_t760"
+ BEFORE_TAG = "vn3.2_t400"
AFTER_TAG = "vn3.2_t670"
def upgrade(self, config, meta_config=None):
@@ -390,7 +389,9 @@ def upgrade(self, config, meta_config=None):
)
# Remove retired settings
self.remove_setting(config, ["namelist:nudging", "nudging_source"])
- self.remove_setting(config, ["namelist:nudging", "nudging_width_bottom"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
# If nudging_mesh_name is still the default '' value, set it to
@@ -407,4 +408,6 @@ def upgrade(self, config, meta_config=None):
config,
["namelist:multires_coupling", "nudging_mesh_name"],
dynamics_mesh_name,
- )
\ No newline at end of file
+ )
+
+ return config, self.reports
diff --git a/science/linear/rose-meta/lfric-linear/versions.py b/science/linear/rose-meta/lfric-linear/versions.py
index e33d8e39bc..3e9051d6c4 100644
--- a/science/linear/rose-meta/lfric-linear/versions.py
+++ b/science/linear/rose-meta/lfric-linear/versions.py
@@ -349,3 +349,82 @@ def upgrade(self, config, meta_config=None):
)
return config, self.reports
+
+
+class vn32_t670(MacroUpgrade):
+ """Upgrade macro for ticket #670 by Thomas Bendall."""
+
+ BEFORE_TAG = "vn3.2_t400"
+ AFTER_TAG = "vn3.2_t670"
+
+ def upgrade(self, config, meta_config=None):
+ # Commands From: rose-meta/lfric-gungho
+ # Add new nudging namelist options
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_method"], "'convolution'"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_theta"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_u"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_relax_time_v"], "6.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_start"], "12.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_spinup_end"], "24.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_stop_time"], "10000.0"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_bottom"], "5"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_level_taper_top"], "52"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "nudging_min_tropopause_level"], "48"
+ )
+ self.change_setting_value(
+ config, ["namelist:nudging", "nudging_level_bottom"], "6"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "num_ref_data_levels"], "137"
+ )
+ self.add_setting(config, ["namelist:nudging", "spectral_kmax"], "20")
+ self.add_setting(config, ["namelist:nudging", "spectral_kmin"], "2")
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_stencil_extent"], "12"
+ )
+ self.add_setting(
+ config, ["namelist:nudging", "spectral_envelope_width"], "0.1"
+ )
+ # Remove retired settings
+ self.remove_setting(config, ["namelist:nudging", "nudging_source"])
+ self.remove_setting(
+ config, ["namelist:nudging", "nudging_width_bottom"]
+ )
+ self.remove_setting(config, ["namelist:nudging", "nudging_width_top"])
+ self.remove_setting(config, ["namelist:nudging", "nudge_data_levels"])
+ # If nudging_mesh_name is still the default '' value, set it to
+ # match dynamics_mesh_name
+ nudging_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "nudging_mesh_name"]
+ )
+ if nudging_mesh_name == "''":
+ dynamics_mesh_name = self.get_setting_value(
+ config, ["namelist:multires_coupling", "dynamics_mesh_name"]
+ )
+ if dynamics_mesh_name is not None:
+ self.change_setting_value(
+ config,
+ ["namelist:multires_coupling", "nudging_mesh_name"],
+ dynamics_mesh_name,
+ )
+
+ return config, self.reports
From 2f8223bf866e326cd2b11d4c266ed2f6acc1933f Mon Sep 17 00:00:00 2001
From: James Bruten <109733895+james-bruten-mo@users.noreply.github.com>
Date: Thu, 22 Oct 2026 15:49:53 +0100
Subject: [PATCH 35/35] update kgos
---
...ing-newtonian-C48_MG_azspice_gnu_fast-debug-32bit.txt | 9 +++++++++
...tonian-coarse-C48_MG_azspice_gnu_fast-debug-32bit.txt | 9 +++++++++
...ging-spectral-C48_MG_azspice_gnu_fast-debug-32bit.txt | 9 +++++++++
...ectral-coarse-C48_MG_azspice_gnu_fast-debug-32bit.txt | 9 +++++++++
4 files changed, 36 insertions(+)
create mode 100644 rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal10_nudging-newtonian-C48_MG_azspice_gnu_fast-debug-32bit.txt
create mode 100644 rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal10_nudging-newtonian-coarse-C48_MG_azspice_gnu_fast-debug-32bit.txt
create mode 100644 rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal10_nudging-spectral-C48_MG_azspice_gnu_fast-debug-32bit.txt
create mode 100644 rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal10_nudging-spectral-coarse-C48_MG_azspice_gnu_fast-debug-32bit.txt
diff --git a/rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal10_nudging-newtonian-C48_MG_azspice_gnu_fast-debug-32bit.txt b/rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal10_nudging-newtonian-C48_MG_azspice_gnu_fast-debug-32bit.txt
new file mode 100644
index 0000000000..2d649163e9
--- /dev/null
+++ b/rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal10_nudging-newtonian-C48_MG_azspice_gnu_fast-debug-32bit.txt
@@ -0,0 +1,9 @@
+Inner product checksum rho = 48D818B5
+Inner product checksum theta = 539489C0
+Inner product checksum u = 6AA166FA
+Inner product checksum mr1 = 41CA06A7
+Inner product checksum mr2 = 39A78CDA
+Inner product checksum mr3 = 37752CF2
+Inner product checksum mr4 = 39012D34
+Inner product checksum mr5 = 0
+Inner product checksum mr6 = 0
diff --git a/rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal10_nudging-newtonian-coarse-C48_MG_azspice_gnu_fast-debug-32bit.txt b/rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal10_nudging-newtonian-coarse-C48_MG_azspice_gnu_fast-debug-32bit.txt
new file mode 100644
index 0000000000..5d705d34ba
--- /dev/null
+++ b/rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal10_nudging-newtonian-coarse-C48_MG_azspice_gnu_fast-debug-32bit.txt
@@ -0,0 +1,9 @@
+Inner product checksum rho = 48D824B7
+Inner product checksum theta = 53948A24
+Inner product checksum u = 6AA28F5C
+Inner product checksum mr1 = 41C8CE76
+Inner product checksum mr2 = 39FDA9D0
+Inner product checksum mr3 = 378A3600
+Inner product checksum mr4 = 393E4026
+Inner product checksum mr5 = 0
+Inner product checksum mr6 = 0
diff --git a/rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal10_nudging-spectral-C48_MG_azspice_gnu_fast-debug-32bit.txt b/rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal10_nudging-spectral-C48_MG_azspice_gnu_fast-debug-32bit.txt
new file mode 100644
index 0000000000..90f577228d
--- /dev/null
+++ b/rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal10_nudging-spectral-C48_MG_azspice_gnu_fast-debug-32bit.txt
@@ -0,0 +1,9 @@
+Inner product checksum rho = 48D81B6A
+Inner product checksum theta = 539488DE
+Inner product checksum u = 6AA1B33F
+Inner product checksum mr1 = 41C90D4D
+Inner product checksum mr2 = 39B82B80
+Inner product checksum mr3 = 378B48CA
+Inner product checksum mr4 = 3914278C
+Inner product checksum mr5 = 0
+Inner product checksum mr6 = 0
diff --git a/rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal10_nudging-spectral-coarse-C48_MG_azspice_gnu_fast-debug-32bit.txt b/rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal10_nudging-spectral-coarse-C48_MG_azspice_gnu_fast-debug-32bit.txt
new file mode 100644
index 0000000000..ce7eb98788
--- /dev/null
+++ b/rose-stem/site/meto/kgos/lfric_atm/azspice/checksum_lfric_atm_nwp_gal10_nudging-spectral-coarse-C48_MG_azspice_gnu_fast-debug-32bit.txt
@@ -0,0 +1,9 @@
+Inner product checksum rho = 48D82256
+Inner product checksum theta = 53948A2E
+Inner product checksum u = 6AA2709D
+Inner product checksum mr1 = 41C8E788
+Inner product checksum mr2 = 39E1359F
+Inner product checksum mr3 = 378627B0
+Inner product checksum mr4 = 3931CE45
+Inner product checksum mr5 = 0
+Inner product checksum mr6 = 0