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 @@ + + + + + + + + 2026-07-31T15:13:27.955709 + image/svg+xml + + + Matplotlib v3.9.4, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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