diff --git a/applications/adjoint_tests/example/configuration.nml b/applications/adjoint_tests/example/configuration.nml index 0a582c2718..39f628ab26 100644 --- a/applications/adjoint_tests/example/configuration.nml +++ b/applications/adjoint_tests/example/configuration.nml @@ -376,7 +376,7 @@ timestep_start='1', ×tepping alpha=0.55, dt=1800, -inner_iterations=1, +inner_iterations_si=1,1 method='semi_implicit', outer_iterations=2, runge_kutta_method='forward_euler', diff --git a/applications/adjoint_tests/source/algorithm/core_dynamics/atlt_rhs_alg_mod.x90 b/applications/adjoint_tests/source/algorithm/core_dynamics/atlt_rhs_alg_mod.x90 index 5ae6d7bfe9..b1c7143f75 100644 --- a/applications/adjoint_tests/source/algorithm/core_dynamics/atlt_rhs_alg_mod.x90 +++ b/applications/adjoint_tests/source/algorithm/core_dynamics/atlt_rhs_alg_mod.x90 @@ -29,6 +29,7 @@ module atlt_rhs_alg_mod use adjoint_test_parameters_mod, only : ls_u_range, ls_theta_range, & ls_rho_range, ls_exner_range, & ls_md1_range, ls_md2_range, ls_md3_range + use dycore_constants_mod, only : stepper_siqn implicit none @@ -208,7 +209,7 @@ module atlt_rhs_alg_mod ! Tangent linear call tl_rhs_alg( rhs, alpha_dt, base_state, state, moist_dyn, & ls_state, ls_moist_dyn, compute_eos, & - dlayer_rhs, model_clock ) + dlayer_rhs, stepper_siqn, model_clock ) ! < Mx, Mx > call invoke( x_innerproduct_x( base_state_inner_prod(igh_u), base_state(igh_u) ), & @@ -312,7 +313,7 @@ module atlt_rhs_alg_mod ! Adjoint call atl_rhs_alg( rhs, alpha_dt, base_state, state, moist_dyn, & ls_state, ls_moist_dyn, compute_eos, & - dlayer_rhs, model_clock ) + dlayer_rhs, stepper_siqn, model_clock ) ! < AMx, x > call invoke( x_innerproduct_y( base_state_base_state_input_inner_prod(igh_u), & diff --git a/applications/adjoint_tests/source/algorithm/transport/control/adjt_transport_controller_alg_mod.x90 b/applications/adjoint_tests/source/algorithm/transport/control/adjt_transport_controller_alg_mod.x90 index bde20b358f..b3380f49ec 100644 --- a/applications/adjoint_tests/source/algorithm/transport/control/adjt_transport_controller_alg_mod.x90 +++ b/applications/adjoint_tests/source/algorithm/transport/control/adjt_transport_controller_alg_mod.x90 @@ -23,6 +23,7 @@ module adjt_transport_controller_alg_mod use finite_element_config_mod, only : element_order_h, & element_order_v use fs_continuity_mod, only : W2, W3 + use dycore_constants_mod, only : stepper_siqn use init_from_controller_alg_mod, only : init_ctrlr_fieldvals use inner_from_controller_rdef_alg_mod, only : ctrlr_ref_field_prod_rdef, & ctrlr_ref_field_inp_prod_rdef, & @@ -166,6 +167,7 @@ module adjt_transport_controller_alg_mod ref_field_inner_prod = 0.0_r_def call ls_wind_pert_rho_controller%initialise( config, & model_clock, & + stepper_siqn, & ref_field_rdef, & ls_wind_n_rdef, & ls_wind_np1_rdef, & @@ -355,6 +357,7 @@ module adjt_transport_controller_alg_mod wind_np1_inner_prod = 0.0_r_def call pert_wind_ls_rho_controller%initialise( config, & model_clock, & + stepper_siqn, & ls_ref_field_rdef, & wind_n_rdef, & wind_np1_rdef, & @@ -398,7 +401,7 @@ module adjt_transport_controller_alg_mod wn_wn_inp_inner_prod = 0.0_r_def wnp1_wnp1_inp_inner_prod = 0.0_r_def call adj_pert_wind_ls_rho_initialiser( pert_wind_ls_rho_controller, & - model_clock, & + model_clock, stepper_siqn, & wind_n_rdef, & wind_np1_rdef ) diff --git a/applications/adjoint_tests/source/algorithm/transport/control/atlt_transport_control_alg_mod.x90 b/applications/adjoint_tests/source/algorithm/transport/control/atlt_transport_control_alg_mod.x90 index 47721ac9b9..d1bc39bffe 100644 --- a/applications/adjoint_tests/source/algorithm/transport/control/atlt_transport_control_alg_mod.x90 +++ b/applications/adjoint_tests/source/algorithm/transport/control/atlt_transport_control_alg_mod.x90 @@ -11,6 +11,7 @@ module atlt_transport_control_alg_mod use field_mod, only : field_type use function_space_mod, only : function_space_type use constants_mod, only : r_def, i_def, l_def + use dycore_constants_mod, only : stepper_siqn use log_mod, only : log_event, & log_scratch_space, & LOG_LEVEL_INFO, & @@ -275,7 +276,7 @@ module atlt_transport_control_alg_mod wind_np1, wind_n, mr_out, mr_in, & ls_advected_fields, & ls_wind_np1, ls_wind_n, ls_mr_in, & - model_clock, outer ) + model_clock, outer, stepper_siqn ) ! < Mx, Mx > adv_inc_inner_prod = 0.0_r_def @@ -384,7 +385,8 @@ module atlt_transport_control_alg_mod wind_np1, wind_n, mr_out, mr_in, & ls_advected_fields, & ls_wind_np1, ls_wind_n, ls_mr_in, & - model_clock, outer, adj_lookup_table_cache ) + model_clock, outer, stepper_siqn, & + adj_lookup_table_cache ) ! < AMx, x > ai_ai_inp_inner_prod = 0.0_r_def diff --git a/applications/adjoint_tests/source/algorithm/transport/control/atlt_transport_controller_alg_mod.x90 b/applications/adjoint_tests/source/algorithm/transport/control/atlt_transport_controller_alg_mod.x90 index 10e66cb437..c9facaf4da 100644 --- a/applications/adjoint_tests/source/algorithm/transport/control/atlt_transport_controller_alg_mod.x90 +++ b/applications/adjoint_tests/source/algorithm/transport/control/atlt_transport_controller_alg_mod.x90 @@ -9,6 +9,7 @@ module atlt_transport_controller_alg_mod use config_mod, only : config_type use constants_mod, only : i_def, r_def, l_def + use dycore_constants_mod, only : stepper_siqn use field_mod, only : field_type use r_tran_field_mod, only : r_tran_field_type use log_mod, only : log_event, & @@ -197,7 +198,7 @@ module atlt_transport_controller_alg_mod setval_X( wind_np1_rdef_inp, wind_np1_rdef ) ) ! Initialise tl_transport_controller object - call tl_transport_controller%initialise( config, model_clock, & + call tl_transport_controller%initialise( config, model_clock, stepper_siqn, & ref_field_rdef, ls_ref_field_rdef, & wind_n_rdef, wind_np1_rdef, & ls_wind_n_rdef, ls_wind_np1_rdef, & @@ -257,6 +258,7 @@ module atlt_transport_controller_alg_mod rf_rf_inp_inner_prod = 0.0_r_def call atl_transport_controller_initialiser( tl_transport_controller, & model_clock, & + stepper_siqn, & ref_field_rdef, & wind_n_rdef, & wind_np1_rdef ) diff --git a/applications/adjoint_tests/source/algorithm/transport/setup_test_alg_mod.x90 b/applications/adjoint_tests/source/algorithm/transport/setup_test_alg_mod.x90 index 1c2d4d4651..d93074f823 100644 --- a/applications/adjoint_tests/source/algorithm/transport/setup_test_alg_mod.x90 +++ b/applications/adjoint_tests/source/algorithm/transport/setup_test_alg_mod.x90 @@ -25,6 +25,7 @@ module setup_test_alg_mod use transport_metadata_collection_mod, only : transport_metadata_collection use transport_controller_mod, only : transport_controller_type use transport_counter_mod, only : transport_counter_type + use dycore_constants_mod, only : stepper_siqn implicit none @@ -167,7 +168,7 @@ module setup_test_alg_mod outer = 1_i_def ! Initialise tl_transport_controller object - call tl_transport_controller%initialise( config, model_clock, & + call tl_transport_controller%initialise( config, model_clock, stepper_siqn, & ref_field_rdef, ls_ref_field_rdef, & wind_n_rdef, wind_np1_rdef, & ls_wind_n_rdef, ls_wind_np1_rdef, & diff --git a/applications/gravity_wave/example/configuration.nml b/applications/gravity_wave/example/configuration.nml index 9422c87aea..261a42fd4f 100644 --- a/applications/gravity_wave/example/configuration.nml +++ b/applications/gravity_wave/example/configuration.nml @@ -188,7 +188,7 @@ max_tiled_multigrid_level = 1 ×tepping alpha = 0.5 dt = 100.0 -inner_iterations = 2 +inner_iterations_si = 2,2 method = 'semi_implicit' outer_iterations = 2 runge_kutta_method = 'ssp3' diff --git a/applications/gungho_model/example/configuration.nml b/applications/gungho_model/example/configuration.nml index 442cd45e9b..322f312dbe 100644 --- a/applications/gungho_model/example/configuration.nml +++ b/applications/gungho_model/example/configuration.nml @@ -286,7 +286,7 @@ timestep_start='1', ×tepping alpha=0.55, dt=3600, -inner_iterations=2, +inner_iterations_si=2,2 method='semi_implicit', outer_iterations=2, runge_kutta_method='forward_euler', diff --git a/applications/gungho_model/lam_example/baroclinic/config_driver.nml b/applications/gungho_model/lam_example/baroclinic/config_driver.nml index 37e927597e..2c1db1c255 100644 --- a/applications/gungho_model/lam_example/baroclinic/config_driver.nml +++ b/applications/gungho_model/lam_example/baroclinic/config_driver.nml @@ -189,7 +189,7 @@ calendar_start='2016-01-01 15:00:00', ×tepping alpha=0.5, dt=1000.0, -inner_iterations=2, +inner_iterations_si=2,2 method='semi_implicit', outer_iterations=2, runge_kutta_method='ssp5', diff --git a/applications/gungho_model/lam_example/baroclinic/config_lam.nml b/applications/gungho_model/lam_example/baroclinic/config_lam.nml index f4c433a3d2..7de928f7b1 100644 --- a/applications/gungho_model/lam_example/baroclinic/config_lam.nml +++ b/applications/gungho_model/lam_example/baroclinic/config_lam.nml @@ -205,7 +205,7 @@ calendar_start='2016-01-01 15:00:00', ×tepping alpha=0.5, dt=1000.0, -inner_iterations=2, +inner_iterations_si=2,2 method='semi_implicit', outer_iterations=2, runge_kutta_method='ssp5', diff --git a/applications/gungho_model/lam_example/straka/config_driver.nml b/applications/gungho_model/lam_example/straka/config_driver.nml index 5d66c1cdbb..fdd68907b2 100644 --- a/applications/gungho_model/lam_example/straka/config_driver.nml +++ b/applications/gungho_model/lam_example/straka/config_driver.nml @@ -190,7 +190,7 @@ calendar_start='2016-01-01 15:00:00', ×tepping alpha=0.5, dt=10.0, -inner_iterations=2, +inner_iterations_si=2,2 method='semi_implicit', outer_iterations=2, runge_kutta_method='ssp5', diff --git a/applications/gungho_model/lam_example/straka/config_lam.nml b/applications/gungho_model/lam_example/straka/config_lam.nml index 9acf174831..fdab423e5b 100644 --- a/applications/gungho_model/lam_example/straka/config_lam.nml +++ b/applications/gungho_model/lam_example/straka/config_lam.nml @@ -202,7 +202,7 @@ calendar_start='2016-01-01 15:00:00', ×tepping alpha=0.5, dt=10.0, -inner_iterations=2, +inner_iterations_si=2,2 method='semi_implicit', outer_iterations=2, runge_kutta_method='ssp5', diff --git a/applications/jedi_lfric_tests/example/configuration.nml b/applications/jedi_lfric_tests/example/configuration.nml index 0f63936506..763ea7ae4d 100644 --- a/applications/jedi_lfric_tests/example/configuration.nml +++ b/applications/jedi_lfric_tests/example/configuration.nml @@ -359,7 +359,7 @@ timestep_start='1', ×tepping alpha=0.55, dt=1800, -inner_iterations=1, +inner_iterations_si=1,1 method='semi_implicit', outer_iterations=2, runge_kutta_method='forward_euler', diff --git a/applications/jedi_lfric_tests/example_forecast/configuration.nml b/applications/jedi_lfric_tests/example_forecast/configuration.nml index 5cd7cf6842..fd18173612 100644 --- a/applications/jedi_lfric_tests/example_forecast/configuration.nml +++ b/applications/jedi_lfric_tests/example_forecast/configuration.nml @@ -305,7 +305,7 @@ timestep_start='1', ×tepping alpha=0.5, dt=90, -inner_iterations=2, +inner_iterations_si=2,2 method='semi_implicit', outer_iterations=2, runge_kutta_method='forward_euler', diff --git a/applications/jedi_lfric_tests/example_id_tlm_tests/configuration.nml b/applications/jedi_lfric_tests/example_id_tlm_tests/configuration.nml index 2f60bc925a..501b695137 100644 --- a/applications/jedi_lfric_tests/example_id_tlm_tests/configuration.nml +++ b/applications/jedi_lfric_tests/example_id_tlm_tests/configuration.nml @@ -400,7 +400,7 @@ timestep_start='1', ×tepping alpha=0.55, dt=1800, -inner_iterations=1, +inner_iterations_si=1,1 method='semi_implicit', outer_iterations=2, runge_kutta_method='forward_euler', diff --git a/applications/jedi_lfric_tests/example_tlm_forecast_tl/configuration.nml b/applications/jedi_lfric_tests/example_tlm_forecast_tl/configuration.nml index 079def8985..aee2fd3b55 100644 --- a/applications/jedi_lfric_tests/example_tlm_forecast_tl/configuration.nml +++ b/applications/jedi_lfric_tests/example_tlm_forecast_tl/configuration.nml @@ -399,7 +399,7 @@ timestep_start='1', ×tepping alpha=0.55, dt=1800, -inner_iterations=1, +inner_iterations_si=1, method='semi_implicit', outer_iterations=1, runge_kutta_method='forward_euler', diff --git a/applications/jedi_lfric_tests/example_tlm_forecast_tl/configuration_op.nml b/applications/jedi_lfric_tests/example_tlm_forecast_tl/configuration_op.nml index 0df951baeb..66c837258c 100644 --- a/applications/jedi_lfric_tests/example_tlm_forecast_tl/configuration_op.nml +++ b/applications/jedi_lfric_tests/example_tlm_forecast_tl/configuration_op.nml @@ -342,7 +342,7 @@ timestep_start='1', ×tepping alpha=0.55, dt=3600, -inner_iterations=2, +inner_iterations_si=2,2 method='semi_implicit', outer_iterations=2, runge_kutta_method='forward_euler', diff --git a/applications/jedi_lfric_tests/example_tlm_tests/configuration.nml b/applications/jedi_lfric_tests/example_tlm_tests/configuration.nml index e86dccd0fc..a138035d43 100644 --- a/applications/jedi_lfric_tests/example_tlm_tests/configuration.nml +++ b/applications/jedi_lfric_tests/example_tlm_tests/configuration.nml @@ -399,7 +399,7 @@ timestep_start='1', ×tepping alpha=0.55, dt=1800, -inner_iterations=1, +inner_iterations_si=1, method='semi_implicit', outer_iterations=1, runge_kutta_method='forward_euler', diff --git a/applications/jules/example/configuration.nml b/applications/jules/example/configuration.nml index c59b4d4c32..92fd2ac968 100644 --- a/applications/jules/example/configuration.nml +++ b/applications/jules/example/configuration.nml @@ -778,7 +778,7 @@ timestep_start='1', ×tepping alpha=0.55, dt=600, -inner_iterations=2, +inner_iterations_si=2, method='jules', outer_iterations=1, runge_kutta_method='forward_euler', diff --git a/applications/lfric_atm/example/configuration.nml b/applications/lfric_atm/example/configuration.nml index bcbbdc3ba1..69e5bb5133 100644 --- a/applications/lfric_atm/example/configuration.nml +++ b/applications/lfric_atm/example/configuration.nml @@ -974,7 +974,7 @@ timestep_start='1', ×tepping alpha=0.55, dt=1200, -inner_iterations=2, +inner_iterations_si=2 method='semi_implicit', outer_iterations=1, runge_kutta_method='forward_euler', diff --git a/applications/name_transport/example/configuration.nml b/applications/name_transport/example/configuration.nml index 31881266a8..0f9c109463 100644 --- a/applications/name_transport/example/configuration.nml +++ b/applications/name_transport/example/configuration.nml @@ -127,7 +127,7 @@ calendar_origin='2016-01-01 15:00:00' ×tepping alpha=0.5, dt=4050.0, -inner_iterations=2, +inner_iterations_si=2,2 method='rk', outer_iterations=2, runge_kutta_method='forward_euler', diff --git a/applications/name_transport/source/algorithm/name_transport_control_alg_mod.x90 b/applications/name_transport/source/algorithm/name_transport_control_alg_mod.x90 index d54229b705..4f988e9de6 100644 --- a/applications/name_transport/source/algorithm/name_transport_control_alg_mod.x90 +++ b/applications/name_transport/source/algorithm/name_transport_control_alg_mod.x90 @@ -192,6 +192,7 @@ contains subroutine name_transport_step( config, model_clock, wind, tracer_con, & rho, transport_density ) + use dycore_constants_mod, only: stepper_rk use init_gungho_prognostics_alg_mod, only: init_u_field use model_clock_mod, only: model_clock_type use sci_mass_matrix_solver_alg_mod, only: mass_matrix_solver_alg @@ -226,7 +227,7 @@ contains primary_mesh => wind%get_mesh() ! Initialise the main transport controller --------------------------------- - call transport_controller%initialise( config, model_clock, rho, wind ) + call transport_controller%initialise( config, model_clock, stepper_rk, rho, wind ) ! Transport dry density if (transport_density) then diff --git a/applications/ngarch/example/configuration_bl.nml b/applications/ngarch/example/configuration_bl.nml index da180c28bd..00c8622317 100644 --- a/applications/ngarch/example/configuration_bl.nml +++ b/applications/ngarch/example/configuration_bl.nml @@ -477,7 +477,7 @@ timestep_start='1', ×tepping alpha=0.55, dt=600, -inner_iterations=2, +inner_iterations_si=2, method='semi_implicit', outer_iterations=1, runge_kutta_method='forward_euler', diff --git a/applications/ngarch/example/configuration_casim.nml b/applications/ngarch/example/configuration_casim.nml index a8f1ecc593..7bc4a93ce1 100644 --- a/applications/ngarch/example/configuration_casim.nml +++ b/applications/ngarch/example/configuration_casim.nml @@ -538,7 +538,7 @@ timestep_start='1', ×tepping alpha=0.55, dt=600, -inner_iterations=2, +inner_iterations_si=2, method='semi_implicit', outer_iterations=1, runge_kutta_method='forward_euler', diff --git a/applications/shallow_water/example/configuration.nml b/applications/shallow_water/example/configuration.nml index 2c4c890a60..38279db701 100644 --- a/applications/shallow_water/example/configuration.nml +++ b/applications/shallow_water/example/configuration.nml @@ -169,7 +169,7 @@ dt = 0.001 ! O(500) for thermal double vortex test case (res 120: 486 s) alpha = 0.5 outer_iterations = 2 -inner_iterations = 2 +inner_iterations_si = 2,2 runge_kutta_method = 'ssp3' ! This runge_kutta_method does not affect the shallow water miniapp spinup_alpha = .false. diff --git a/applications/shallow_water/source/algorithm/swe_timestep_alg_mod.x90 b/applications/shallow_water/source/algorithm/swe_timestep_alg_mod.x90 index 0acc86c13d..2a877a02ac 100644 --- a/applications/shallow_water/source/algorithm/swe_timestep_alg_mod.x90 +++ b/applications/shallow_water/source/algorithm/swe_timestep_alg_mod.x90 @@ -15,6 +15,7 @@ module swe_timestep_alg_mod ! Constants use constants_mod, only: i_def, r_def + use dycore_constants_mod, only: stepper_siqn, stepper_rk use field_indices_mod, only: isw_u, & isw_g, & isw_b, & @@ -36,7 +37,7 @@ module swe_timestep_alg_mod tik, LPROF use timestepping_config_mod, only: alpha, & outer_iterations, & - inner_iterations + inner_iterations_si use transport_config_mod, only: cheap_update ! Derived Types @@ -486,13 +487,14 @@ contains outer_dynamics_loop: do outer = 1,outer_iterations ! Advect quantities - call swe_transport_control_alg_step( config, & - rhs_adv, & - adv_fields, & - state_n, & - state, & - model_clock, & - outer, & + call swe_transport_control_alg_step( config, & + rhs_adv, & + adv_fields, & + state_n, & + state, & + model_clock, & + outer, & + stepper_siqn, & wind_prev ) ! Set wind_prev to be latest estimate of wind @@ -530,7 +532,7 @@ contains !============================================================================ ! Start the inner loop !============================================================================ - inner_dynamics_loop: do inner = 1,inner_iterations + inner_dynamics_loop: do inner = 1,inner_iterations_si(outer) ! Build residual: -R = -( rhs(state, alpha) - rhs(state_n, alpha-1) - rhs_adv) call swe_rhs_alg( rhs_np1, alpha_dt, state, s_geopot ) @@ -668,13 +670,14 @@ contains ! Advect quantities outer_iteration = 1_i_def call rhs_adv%set_scalar(0.0_r_def) - call swe_transport_control_alg_step( config, & - rhs_adv, & - adv_fields, & - state_n, & - state, & - model_clock, & - outer_iteration ) + call swe_transport_control_alg_step( config, & + rhs_adv, & + adv_fields, & + state_n, & + state, & + model_clock, & + outer_iteration, & + stepper_rk ) ! Put RHS together call rhs_n%axpy(1.0_r_def, rhs_adv) diff --git a/applications/shallow_water/source/algorithm/swe_transport_control_alg_mod.x90 b/applications/shallow_water/source/algorithm/swe_transport_control_alg_mod.x90 index 5b397f3b22..a1f4f2ec9c 100644 --- a/applications/shallow_water/source/algorithm/swe_transport_control_alg_mod.x90 +++ b/applications/shallow_water/source/algorithm/swe_transport_control_alg_mod.x90 @@ -12,6 +12,7 @@ module swe_transport_control_alg_mod use combine_w2_field_kernel_mod, only: combine_w2_field_kernel_type use constants_mod, only: i_def, r_def, l_def, r_tran use derived_config_mod, only: bundle_size + use dycore_constants_mod, only: stepper_siqn use sci_enforce_bc_kernel_mod, only: enforce_bc_kernel_type use flux_precomputations_alg_mod, only: flux_precomputations_type use field_mod, only: field_type @@ -138,6 +139,7 @@ contains !> @param[in] state_np1 Estimate of fields at time n+1 !> @param[in] model_clock Time within the model !> @param[in] outer Outer (advection) iteration number + !> @param[in] stepper Enumerator for timestepper !> @param[in] wind_prev (Optional) Estimate of end of timestep wind !! from previous outer iteration subroutine swe_transport_control_alg_step( config, & @@ -147,6 +149,7 @@ contains state_np1, & model_clock, & outer, & + stepper, & wind_prev ) use sci_fem_constants_mod, only: get_mass_matrix_fe @@ -165,6 +168,7 @@ contains type(field_vector_type), target, intent(in) :: state_n class(model_clock_type), intent(in) :: model_clock integer(kind=i_def), intent(in) :: outer + integer(kind=i_def), intent(in) :: stepper type(field_type), intent(in), optional :: wind_prev ! Fields from advected_fields, state_n and state_np1 vector @@ -239,8 +243,9 @@ contains prev_inc = advection_inc ! Create transport_controller object (advecting wind etc) - call transport_controller%initialise(config, & - model_clock, q, wind_for_adv, wind_np1, & + call transport_controller%initialise( & + config, model_clock, stepper, & + q, wind_for_adv, wind_np1, & outer=outer, cheap_update_step=cheap_update_step & ) @@ -434,7 +439,7 @@ contains if ( LPROF ) call start_timing( id_swe_tracer, 'swe_tracer_transport_alg' ) ! Initialise the main transport controller --------------------------------- - call transport_controller%initialise( config, model_clock, & + call transport_controller%initialise( config, model_clock, stepper_siqn, & mass_n, wind_n, wind_np1 ) ! Initialise transported variables diff --git a/applications/transport/example/configuration.nml b/applications/transport/example/configuration.nml index 5788c51cca..d15788b154 100644 --- a/applications/transport/example/configuration.nml +++ b/applications/transport/example/configuration.nml @@ -194,7 +194,7 @@ tolerance=1.0e-6, ×tepping alpha=0.5, dt=4050.0, -inner_iterations=2, +inner_iterations_si=2,2 method='rk', outer_iterations=2, runge_kutta_method='forward_euler', diff --git a/applications/transport/source/algorithm/transport_control_alg_mod.x90 b/applications/transport/source/algorithm/transport_control_alg_mod.x90 index a7c151dc55..a6760fa561 100644 --- a/applications/transport/source/algorithm/transport_control_alg_mod.x90 +++ b/applications/transport/source/algorithm/transport_control_alg_mod.x90 @@ -260,6 +260,7 @@ contains wt_aerosol, aerosol_wind, & nummr_to_transport ) + use dycore_constants_mod, only: stepper_rk use sci_field_bundle_builtins_mod, only: clone_bundle, copy_bundle use formulation_config_mod, only: use_multires_coupling use init_gungho_prognostics_alg_mod, only: init_u_field @@ -325,10 +326,13 @@ contains call map_rho_intermesh(aerosol_reference_rho, rho) ! Initialise the main transport controller --------------------------------- - call transport_controller%initialise( config, model_clock, rho, wind ) - call aerosol_transport_controller%initialise( config, model_clock, & - aerosol_reference_rho, & - aerosol_wind ) + call transport_controller%initialise( & + config, model_clock, stepper_rk, rho, wind & + ) + call aerosol_transport_controller%initialise( & + config, model_clock, stepper_rk, & + aerosol_reference_rho, aerosol_wind & + ) ! Transport dry density call log_event( "Transporting density...", LOG_LEVEL_INFO) diff --git a/documentation/source/science_guide/references.bib b/documentation/source/science_guide/references.bib new file mode 100644 index 0000000000..c9c4227734 --- /dev/null +++ b/documentation/source/science_guide/references.bib @@ -0,0 +1,43 @@ +@article{melvin2019mixed, + title={A mixed finite-element, finite-volume, semi-implicit discretization for atmospheric dynamics: Cartesian geometry}, + author={Melvin, Thomas and Benacchio, Tommaso and Shipway, Ben and Wood, Nigel and Thuburn, John and Cotter, Colin}, + journal={Quarterly Journal of the Royal Meteorological Society}, + volume={145}, + number={724}, + pages={2835--2853}, + year={2019}, + publisher={Wiley Online Library} +} + +@article{melvin2024mixed, + title={A mixed finite-element, finite-volume, semi-implicit discretisation for atmospheric dynamics: spherical geometry}, + author={Melvin, Thomas and Shipway, Ben and Wood, Nigel and Benacchio, Tommaso and Bendall, Thomas and Boutle, Ian and Brown, Alex and Johnson, Christine and Kent, James and Pring, Stephen and others}, + journal={Quarterly Journal of the Royal Meteorological Society}, + volume={150}, + number={764}, + pages={4252--4269}, + year={2024}, + publisher={Wiley Online Library} +} + +@article{wood2014inherently, + title={An inherently mass-conserving semi-implicit semi-{L}agrangian discretization of the deep-atmosphere global non-hydrostatic equations}, + author={Wood, Nigel and Staniforth, Andrew and White, Andy and Allen, Thomas and Diamantakis, Michail and Gross, Markus and Melvin, Thomas and Smith, Chris and Vosper, Simon and Zerroukat, Mohamed and others}, + journal={Quarterly Journal of the Royal Meteorological Society}, + volume={140}, + number={682}, + pages={1505--1520}, + year={2014}, + publisher={Wiley Online Library} +} + +@article{tumolo2015semi, + title={A semi-implicit, semi-{L}agrangian discontinuous {G}alerkin framework for adaptive numerical weather prediction}, + author={Tumolo, Giovanni and Bonaventura, Luca}, + journal={Quarterly Journal of the Royal Meteorological Society}, + volume={141}, + number={692}, + pages={2582--2601}, + year={2015}, + publisher={Wiley Online Library} +} \ No newline at end of file diff --git a/documentation/source/science_guide/timestepping/index.rst b/documentation/source/science_guide/timestepping/index.rst new file mode 100644 index 0000000000..98215623f8 --- /dev/null +++ b/documentation/source/science_guide/timestepping/index.rst @@ -0,0 +1,59 @@ +.. ----------------------------------------------------------------------------- + (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. + ----------------------------------------------------------------------------- + +.. _science_guide_timestepping: + +Timestepping +============ + +GungHo's dynamical core can be advanced in time using one of several +timestepping schemes (see the :ref:`User Guide ` for +how these are configured). This section describes +the numerical formulation of each scheme: + +.. toctree:: + :maxdepth: 2 + + semi_implicit + runge_kutta + tr_bdf2 + +.. attention:: + + The formulations below use the notation of a state vector + :math:`\boldsymbol{X}`, an operator :math:`\mathcal{F}` representing the + evaluation of forces (such as the pressure gradient, Coriolis and + gravitational forces), and a transport operator + :math:`\mathcal{T}^{\Delta t}_{\boldsymbol{u}}`, relating to a velocity + :math:`\boldsymbol{u}`, over a time step :math:`\Delta t`. The transport + operator returns a transported field (rather than a transport increment). + Increments from physics parametrisation schemes are represented by + :math:`\mathcal{P}`. + +Available Schemes +----------------- + +:ref:`Semi-Implicit ` + GungHo's default timestepping scheme. The Semi-Implicit Quasi-Newton (SIQN) + scheme is a Semi-Implicit Semi-Lagrangian (SISL) method, combining a + Crank-Nicolson-like treatment of forcing terms with a mass-conserving + transport scheme, solved through an outer/inner Quasi-Newton iteration. + +:ref:`Runge-Kutta ` + An explicit, multi-stage scheme, used either to timestep the whole + dynamical core or in combination with the Method-of-Lines transport + scheme. Strong Stability Preserving (SSP) variants and a simple forward + Euler option are available. + +:ref:`TR-BDF2 ` + A two-stage semi-implicit method, combining a Trapezoidal (TR) stage and + a Backwards-Difference-2 (BDF2) stage, each solved using the same + Quasi-Newton structure as the SIQN scheme. Unlike SIQN, it has no + off-centering parameter, and is inherently damping of high-frequency + waves while maintaining second-order accuracy in time. + diff --git a/documentation/source/science_guide/timestepping/runge_kutta.rst b/documentation/source/science_guide/timestepping/runge_kutta.rst new file mode 100644 index 0000000000..f918961ca6 --- /dev/null +++ b/documentation/source/science_guide/timestepping/runge_kutta.rst @@ -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. + Some of the content of this file has been produced with the assistance of + Met Office GitHub Copilot Enterprise. + ----------------------------------------------------------------------------- + +.. _science_guide_timestepping_runge_kutta: + +Runge-Kutta +=========== + +The Runge-Kutta timestepping method is an explicit, +multi-stage scheme. It can be used either to timestep +the whole dynamical core, or in combination with the Method-of-Lines +transport scheme. The Strong Stability Preserving (SSP) variants +are explicit multi-stage methods +which combine the values computed over multiple stages as a weighted linear +sum, in order to provide stability. A simple forward Euler option is also +available. + +Runge-Kutta timestepping of the whole dynamical core has not been implemented +in combination with physics parametrization schemes. + +Rather than splitting terms as in the Quasi-Newton schemes, the Runge-Kutta +treats the whole equation set for the state vector :math:`\boldsymbol{X}` as one: + +.. math:: + + \frac{\partial\boldsymbol{X}}{\partial t} + \mathcal{G}(\boldsymbol{X}) = \boldsymbol{0}, + +where :math:`\mathcal{G}` encompasses forcing and transport terms. +An explicit Runge-Kutta scheme advances one timestep by evaluating a sequence of +:math:`M` stages, + +.. math:: + + \begin{aligned} + \boldsymbol{X}^{(0)} &= \boldsymbol{X}^{n}, \\ + \boldsymbol{X}^{(m)} &= \boldsymbol{X}^{(0)} + - \Delta t\sum_{i=1}^{m} c_{m,i}\mathcal{G}[\boldsymbol{X}^{(i-1)}], \qquad \forall m = 1, \ldots, M, \\ + \boldsymbol{X}^{n+1} &= \boldsymbol{X}^{(M)}. + \end{aligned} + +The scheme is explicit because each stage :math:`i` depends only on +previous stages so no implicit solve is required. +In practice this means a +Runge-Kutta step is a sequence of tendency evaluations and weighted sums, +with higher-order SSP variants using more stages than Forward Euler to +improve stability and accuracy. + +In GungHo, this same stage-update pattern is used whether Runge-Kutta is +applied to the whole dynamical core, or used inside Method-of-Lines +transport where :math:`\mathcal{G}` is the transport tendency operator. diff --git a/documentation/source/science_guide/timestepping/semi_implicit.rst b/documentation/source/science_guide/timestepping/semi_implicit.rst new file mode 100644 index 0000000000..5f153134fe --- /dev/null +++ b/documentation/source/science_guide/timestepping/semi_implicit.rst @@ -0,0 +1,450 @@ +.. ----------------------------------------------------------------------------- + (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. + ----------------------------------------------------------------------------- + +.. _science_guide_timestepping_semi_implicit: + +Semi-Implicit +============= + +Overview +-------- + +The Semi-Implicit Quasi-Newton (SIQN) scheme is GungHo's default timestepping +scheme. It belongs to the family of +Semi-Implicit Semi-Lagrangian (SISL) schemes, which have been used +successfully in operational numerical weather prediction for several decades. +The appeal of SISL schemes is that they permit a large, stable timestep while +retaining good accuracy. This is achieved by combining two ideas: + +* a Crank-Nicolson-like (trapezoidal) treatment of the wave-like and forcing + terms, which is unconditionally stable with respect to the timestep for + wave equations; and +* a transport scheme with some Semi-Lagrangian aspect so that stability does + not depend on the Courant number. + +In a classical SISL scheme, the explicit :math:`n`-level values are evaluated +at the departure points, while the implicit :math:`(n+1)`-level values are +evaluated at the arrival points. This results in a discretisation of the +material derivative which preserves the numerical properties of the +Crank-Nicolson scheme: low frequency waves (such as Rossby waves) are +represented accurately, while high frequency (acoustic) waves can be damped +through off-centering. + +GungHo builds on the classical SISL picture by providing local mass conservation +through the use of conservative transport schemes. Like the ENDGame dynamical +core of [Wood2014]_ used by the Unified Model, GungHo uses a +Quasi-Newton iteration to solve the non-linear system of equations arising from +the implicit treatment of the wave-like and forcing terms. + +For a full description of the formulation of GungHo, see [Melvin2019]_ +and [Melvin2024]_. + +Details +------- + +Basic formulation +~~~~~~~~~~~~~~~~~~ + +Given the off-centering parameter :math:`\alpha`, the SIQN scheme advances +the state from time level :math:`n` to time level :math:`n+1` according to: + +.. math:: :label: siqn_eqn + + \boldsymbol{X}^{n+1} + \alpha\Delta t \mathcal{F}(\boldsymbol{X}^{n+1}) + = \mathcal{T}^{\Delta t}_{\overline{\boldsymbol{u}}_n^{n+1}} + \left[\boldsymbol{X}^n - (1-\alpha)\Delta t \mathcal{F}(\boldsymbol{X}^n)\right], + +where the transporting velocity is the average of the start- and +end-of-timestep winds, + +.. math:: :label: siqn_transport_velocity + + \overline{\boldsymbol{u}}_n^{n+1} = \tfrac{1}{2}\left(\boldsymbol{u}^n + \boldsymbol{u}^{n+1}\right). + +The forcing terms are handled with a weighting of :math:`\alpha` on the +implicit (:math:`n+1`)-th time level and :math:`(1-\alpha)` on the explicit +:math:`n`-th time level. + +A value of :math:`\alpha=0.5` corresponds to a centred Crank-Nicolson scheme, +which is formally second-order accurate in time but does not damp any waves. +If orography is present, it can generate spurious small-scale waves which, +without any damping, may persist and pollute the solution, potentially leading +to instability; centred SISL schemes can also suffer from orographic resonance. +For these reasons the scheme is run slightly off-centred +(:math:`\alpha > 0.5`), which introduces damping of high-frequency (typically +acoustic) modes at the cost of reducing the formal accuracy to first order. +A value of :math:`\alpha=1.0` corresponds to a fully implicit scheme. + +Quasi-Newton iteration +~~~~~~~~~~~~~~~~~~~~~~~ + +Equation :eq:`siqn_eqn` is non-linear in the unknown state +:math:`\boldsymbol{X}^{n+1}`, and is solved iteratively. Let us use :math:`k` +to index the iteration, so that :math:`k`-th iteration of +:math:`\boldsymbol{X}^{n+1}` is :math:`\boldsymbol{X}^{n+1}_{(k)}`, where +:math:`\boldsymbol{X}^{n+1}_{(0)} = \boldsymbol{X}^n` is the first guess. +The :math:`k`-th transported state :math:`\boldsymbol{X}^T_{(k)}` is introduced as: + +.. math:: + + \boldsymbol{X}^T_{(k)} = \mathcal{T}^{\Delta t}_{\overline{\boldsymbol{u}}_n^{n+1,(k)}} + \left[\boldsymbol{X}^n - (1-\alpha)\Delta t \mathcal{F}(\boldsymbol{X}^n)\right], + +which depends upon the latest value of the end-of-timestep transporting velocity. +Then the residual of the :math:`k`-th iterate :math:`\boldsymbol{X}^{n+1}_{(k)}` is + +.. math:: :label: siqn_residual + + \boldsymbol{R}_{(k)} = -\left(\boldsymbol{X}^{n+1}_{(k)} + + \alpha\Delta t \mathcal{F}(\boldsymbol{X}^{n+1}_{(k)}) - \boldsymbol{X}^T_{(k)}\right). + +A full Newton method would drive this residual to zero by solving + +.. math:: + + \mathcal{J}\left[\boldsymbol{X}^{n+1}_{(k)}\right] + \left(\boldsymbol{X}^{n+1}_{(k+1)} - \boldsymbol{X}^{n+1}_{(k)}\right) + = \boldsymbol{R}_{(k)}, + +where :math:`\mathcal{J}` is the Jacobian of the residual with respect to +:math:`\boldsymbol{X}^{n+1}`. Forming and inverting the exact Jacobian is +expensive, so (like ENDGame) GungHo instead uses a *Quasi-Newton* method: the Jacobian is +approximated by a fixed linearisation :math:`\mathcal{S}` of the equation set +about a reference state. Writing the increment as +:math:`\boldsymbol{X}' = \boldsymbol{X}^{n+1}_{(k+1)} - \boldsymbol{X}^{n+1}_{(k)}`, +each iteration then solves the linear system + +.. math:: :label: siqn_linear_system + + \mathcal{S}\,\boldsymbol{X}' = \boldsymbol{R}_{(k)}. + +This "incremental" form is the one used in GungHo, and the scheme is not +iterated to convergence: a small, fixed number of iterations is taken. Although +this can still be accurate, it can be unstable for certain numbers of iterations. + +Outer and inner iterations +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In practice, the change to transport increments from one iteration to the next +is smaller than the change from the implicit forcing terms. This allows us +to split the Quasi-Newton iteration into two nested loops: + +* an **outer loop**, which is iterated :math:`n_o` times, + in which the transporting velocity + is updated with the latest estimate of :math:`\boldsymbol{u}^{n+1}` and the + prognostic variables are transported. The transport scheme is therefore + called :math:`n_o` times per timestep. +* an **inner loop**, which is iterated :math:`n^j_i` times, where :math:`j` + is the outer-loop index. + In the inner loop, implicit forcing terms + are evaluated to form the residual :eq:`siqn_residual`, and the linear + system :eq:`linear_solver_eqn` is assembled and solved for the increment. + The number of inner iterations can be varied between outer iterations, but + has historically been kept the same at :math:`n_i` for each outer iteration, + so that the linear solve is performed :math:`n_o \times n_i` times per + timestep. + +Like ENDGame, GungHo uses a default of :math:`n_o=2` outer iterations and +:math:`n_i=2` inner iterations, for a total of 4 linear solves per timestep. + +The density and potential temperature fields do not receive updates from the +forcings evaluated in the inner loop, and are only updated in the outer loop. +Thus, when using more than one inner iteration, the density and potential +temperature contributions to the residual are set to zero for any inner +iteration after the first. This is why it is recommended to set the density and +potential-temperature relaxation parameters to 1 when more than one inner +iteration is used. + +Linear solver +~~~~~~~~~~~~~ + +The linearised operator :math:`\mathcal{S}` in Equation +:eq:`siqn_linear_system` is obtained by linearising the residuals about a +reference state :math:`\boldsymbol{X}^* = (\boldsymbol{0}, \rho^*, \theta^*, +\Pi^*)^\mathrm{T}`. The resulting linear problem takes the general form + +.. math:: :label: linear_solver_eqn + + \boldsymbol{X}' + \sigma \mathcal{I}[\boldsymbol{X}'] + + \tau_i \Delta t \mathcal{L}[\boldsymbol{X}'] = \boldsymbol{R}, + +where :math:`\mathcal{I}` is an implicit operator (corresponding to the +damping layer) and :math:`\mathcal{L}` is the linear operator corresponding +to the forcing and transport terms. The relaxation parameters +:math:`\sigma` and :math:`\tau_i` control the implicit weighting; the +:math:`\tau_i` may differ between the momentum, density and +potential-temperature variables :math:`i`. The implicit terms in the +continuity and thermodynamic equations correspond to linearisations of the +transport terms, :math:`\nabla\cdot(\rho^*\boldsymbol{u}')` and +:math:`\boldsymbol{u}'\cdot\nabla\theta^*` respectively. + +For the SIQN scheme, the relaxation parameters are typically chosen as: + +.. math:: + + \tau_u = \alpha, \qquad \tau_\rho=\tau_\theta=1, \qquad \sigma=1. + +Right-Hand Side and Predictors +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As GungHo uses a finite element discretisation, the right-hand side of the +linear system :eq:`linear_solver_eqn` is actually calculated by assembling the +residual in weak form. When field values are needed, for instance for the +transport operator or physics parametrisations, it may be necessary to +solve a mass matrix system to obtain the field. This is known as calculating +the "predictors". + +Dynamical Core Algorithm +~~~~~~~~~~~~~~~~~~~~~~~~ + +We now bring together the outer loop (indexed by :math:`j=1,\dots,n_o`) and +inner loop (indexed by :math:`k=1,\dots,n^j_i`) to summarise the whole +Semi-Implicit Quasi-Newton timestep as a sequence of equations. The state +:math:`\boldsymbol{X}^{n+1}_{(j,k)}` denotes the :math:`k`-th inner-loop +estimate of the end-of-timestep state within the :math:`j`-th outer loop, with +:math:`\boldsymbol{X}^{n+1}_{(j,0)} = \boldsymbol{X}^{n+1}_{(j-1)}`. +The state :math:`\boldsymbol{X}^{n+1}_{(j)}` is the final inner-loop estimate of the +end-of-timestep state within the :math:`j`-th outer loop, and is used for +carrying the final state of the previous outer loop forward. +Similarly, :math:`\boldsymbol{X}^T_{(j)}` is the transported state formed +within outer loop :math:`j`. + +The first guess for the end-of-timestep state is simply the start-of-timestep +state, + +.. math:: :label: dyn_core_first_guess + + \boldsymbol{X}^{n+1}_{(0)} = \boldsymbol{X}^n. + +The explicit forcing contribution only depends on the start-of-timestep state, +so it is evaluated once (in weak form), before the outer loop begins, + +.. math:: :label: dyn_core_explicit_forcing + + \boldsymbol{X}^n_{FE} = \boldsymbol{X}^n - (1-\alpha)\Delta t\,\mathcal{F}(\boldsymbol{X}^n). + +As :math:`\boldsymbol{X}^n_{FE}` is generally calculated in weak form, a +mass matrix system is solved to obtain the "predictor" values that are +subsequently transported. + +Each outer loop :math:`j` begins by updating the transporting velocity using +the latest outer-loop estimate of the end-of-timestep wind, +:math:`\boldsymbol{u}^{n+1}_{(j-1)}`, + +.. math:: :label: dyn_core_transport_velocity + + \overline{\boldsymbol{u}}_n^{n+1,(j)} = \tfrac{1}{2}\left(\boldsymbol{u}^n + \boldsymbol{u}^{n+1}_{(j-1)}\right). + +The transport step is then performed using this updated velocity, + +.. math:: :label: dyn_core_transport + + \boldsymbol{X}^T_{(j)} = \mathcal{T}^{\Delta t}_{\overline{\boldsymbol{u}}_n^{n+1,(j)}} + \left[\boldsymbol{X}^n_{FE}\right]. + +Note that :math:`\boldsymbol{X}^T_{(j)}` is also generally calculated in weak +form. + +Within outer loop :math:`j`, each inner iteration :math:`k` first calculates +the implicit forcing contribution in weak form, + +.. math:: :label: dyn_core_implicit_forcing + + \boldsymbol{X}^{n+1}_{(j,k-1),FI} = \boldsymbol{X}^{n+1}_{(j,k-1)} + + \alpha\Delta t\,\mathcal{F}\!\left(\boldsymbol{X}^{n+1}_{(j,k-1)}\right). + +The residual of the current iterate against this fixed transported state is then + +.. math:: :label: dyn_core_residual + + \boldsymbol{R}_{(j,k)} = -\left(\boldsymbol{X}^{n+1}_{(j,k-1),FI} + - \boldsymbol{X}^T_{(j)}\right), + +solves the linearised system for the increment :math:`\boldsymbol{X}'`, + +.. math:: :label: dyn_core_linear_solve + + \mathcal{S}\left[\boldsymbol{X}'\right] = \boldsymbol{R}_{(j,k)}, + +and updates the state accordingly, + +.. math:: :label: dyn_core_increment + + \boldsymbol{X}^{n+1}_{(j,k)} = \boldsymbol{X}^{n+1}_{(j,k-1)} + \boldsymbol{X}'. + +Once all :math:`n_o` outer loops have completed, the timestep is advanced by +setting + +.. math:: :label: dyn_core_advance + + \boldsymbol{X}^n = \boldsymbol{X}^{n+1}_{(n_o)}. + +This process is summarised by the pseudo-code below: + +.. code-block:: text + + Set the first guess: + X^{n+1}_(0) = X^n + Explicit forcing: + X_FE = X^n - (1 - alpha) * dt * F(X^n) + Calculate transport predictor from X_FE + + # Outer Loop ---------------------------------------------------------------- + for j = 1 to n_o: + Update transporting velocity: + u_bar^(j) = 0.5 * (u_n + u_{n+1}^(j-1)) + Transport: + X^T_(j) = T_{u_bar^(j)}(X_FE) + + X^{n+1}_(j,0) = X^{n+1}_(j-1) + + # Inner Loop ------------------------------------------------------------ + for k = 1 to n_i^j: + Implicit forcing: + X_FI^(j,k-1) = X^{n+1}_(j,k-1) + alpha * dt * F(X^{n+1}_(j,k-1)) + Form the residual: + R_(j,k) = -( X_FI^(j,k-1) - X^T_(j) ) + Solve the linear system for the increment: + S[X'] = R_(j,k) + Increment the state: + X^{n+1}_(j,k) = X^{n+1}_(j,k-1) + X' + + Set final estimate: + X^{n+1}_(j) = X^{n+1}_(j,n_i^j) + + Advance the timestep: + X^n = X^{n+1}_(n_o) + + +Inclusion of Physics Parametrisations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +So far the description of the timestep has been limited to the dynamical core. +We now describe how physics parametrisation schemes are included. +Physics parametrisations can be divided into three categories: + +1. **Slow Physics**: denoted :math:`\mathcal{P}_S`. This is evaluated on the + start-of-timestep state :math:`\boldsymbol{X}^n`. It represents explicit + physics processes which are not strongly coupled to the dynamics. Note that + explicit forcing terms are not evaluated on the slow physics increments. +2. **Fast Physics**: denoted :math:`\mathcal{P}_F`. This is evaluated on the + "physics predictor", which is the latest estimate of the end-of-timestep + state following the evaluation of transport terms. It describes implicit + physics processes that are strongly coupled to the dynamics, and is called + within the outer loop of the Quasi-Newton iteration. +3. **End-of-step Physics**: denoted :math:`\mathcal{P}_E`. Evaluated after the + Quasi-Newton iteration has completed, it also represents explicit physics + processes. + +The whole system of equations then becomes: + +.. math:: :label: siqn_with_physics_eqn + + \begin{aligned} + \boldsymbol{X}^\dagger &= \boldsymbol{X}^n - \Delta t\,\mathcal{P}_S(\boldsymbol{X}^n), \\ + \boldsymbol{X}^{n+1} + \alpha\Delta t \mathcal{F}(\boldsymbol{X}^{n+1}) + + \mathcal{P}_F(\boldsymbol{X}^{n+1}) + &= \mathcal{T}^{\Delta t}_{\overline{\boldsymbol{u}}_n^{n+1}} + \left[\boldsymbol{X}^\dagger - (1-\alpha)\Delta t \mathcal{F}(\boldsymbol{X}^n) + \right], \\ + \boldsymbol{X}^{n+1} &= \boldsymbol{X}^{n+1} - \Delta t\,\mathcal{P}_E(\boldsymbol{X}^{n+1}). + \end{aligned} + +With the inclusion of these physics parametrisation schemes, the full timestep +can be summarised by the following pseudocode. + +.. code-block:: text + + Set the first guess: + X^{n+1}_(0) = X^n + Slow physics: + X^dag = X^n + dt * P_S(X^n) + Explicit forcing: + X_FE = X^dag - (1 - alpha) * dt * F(X^n) + Calculate transport predictor (X_FE) + + # Outer Loop ---------------------------------------------------------------- + for j = 1 to n_o: + Update transporting velocity: + u_bar^(j) = 0.5 * (u_n + u_{n+1}^(j-1)) + Transport: + X^T_(j) = T_{u_bar^(j)}(X_FE) + Implicit forcing: + X_FI^(j,1) = X^{n+1}_(j-1) + alpha * dt * F(X^{n+1}_(j-1)) + Fast physics predictor: + X_star^(j) = X^T_(j) + (X_FI^(j,0) - X^{n+1}_(j-1)) + Fast physics increment: + dX_F^(j) = dt * P_F(X_star^(j)) + + X^{n+1}_(j,0) = X^{n+1}_(j-1) + + # Inner Loop ------------------------------------------------------------ + for k = 1 to n_i^j: + if k > 1: + Implicit forcing: + X_FI^(j,k) = X^{n+1}_(j,k) + alpha * dt * F(X^{n+1}_(j,k-1)) + Form the residual: + R_(j,k) = -( X_FI^(j,k) - X^T_(j) - dX_F^(j) ) + Solve the linear system for the increment: + S[X'] = R_(j,k) + Increment the state: + X^{n+1}_(j,k) = X^{n+1}_(j,k-1) + X' + + Set final estimate: + X^{n+1}_(j) = X^{n+1}_(j,n_i^j) + + End-of-step physics: + X^{n+1} = X^{n+1}_(n_o) + dt * P_E(X^{n+1}_(n_o)) + Advance the timestep: + X^n = X^{n+1} + +Tracers +~~~~~~~ + +Some tracers (for instance chemical or aerosol species) do not feed back directly +onto the dynamical core and therefore do not need including in the Quasi-Newton +iteration. These tracers are transported in only the final outer iteration of +the timestep. + +These tracers may also be updated by physics parametrisations (or for example a +chemical or aerosol scheme, which for now can be considered as a physics +parametrisation). Let tracers be represented by the state vector +:math:`\boldsymbol{Y}`. The evolution of tracers through the timestep is given by: + +.. math:: :label: tracer_eqn + + \begin{aligned} + \boldsymbol{Y}^\dagger &= \boldsymbol{Y}^n + - \Delta t\,\mathcal{P}_S(\boldsymbol{X}^n; \boldsymbol{Y}^n), \\ + \boldsymbol{Y}^T &= \mathcal{T}^{\Delta t}_{\overline{\boldsymbol{u}}_n^{n+1}} + \left[\boldsymbol{Y}^\dagger \right] + \mathcal{P}_F(\boldsymbol{X}^{n+1}; \boldsymbol{Y}^\dagger), \\ + \boldsymbol{Y}^{n+1} &= \boldsymbol{Y}^T - \Delta t\,\mathcal{P}_E(\boldsymbol{X}^{n+1};\boldsymbol{Y}^T). + \end{aligned} + +References +---------- + +.. [Wood2014] Wood, N., Staniforth, A., White, A., Allen, T., Diamantakis, M., + Gross, M., Melvin, T., Smith, C., Vosper, S., Zerroukat, M., and Thuburn, J. + (2014). An inherently mass-conserving semi-implicit semi-Lagrangian + discretization of the deep-atmosphere global non-hydrostatic equations. + *Quarterly Journal of the Royal Meteorological Society*, 140(682), 1505-1520. + https://doi.org/10.1002/qj.2235 + +.. [Melvin2019] Melvin, T., Benacchio, T., Shipway, B., Wood, N., Thuburn, J., + and Cotter, C. (2019). A mixed finite-element, finite-volume, semi-implicit + discretization for atmospheric dynamics: Cartesian geometry. + *Quarterly Journal of the Royal Meteorological Society*, 145(724), 2835-2853. + https://doi.org/10.1002/qj.3501 + +.. [Melvin2024] Melvin, T., Shipway, B., Wood, N., Benacchio, T., Bendall, T., + Boutle, I., Brown, A., Johnson, C., Kent, J., Pring, S., et al. (2024). A + mixed finite-element, finite-volume, semi-implicit discretisation for + atmospheric dynamics: spherical geometry. + *Quarterly Journal of the Royal Meteorological Society*, 150(764), 4252-4269. + https://doi.org/10.1002/qj.4887 \ No newline at end of file diff --git a/documentation/source/science_guide/timestepping/tr_bdf2.rst b/documentation/source/science_guide/timestepping/tr_bdf2.rst new file mode 100644 index 0000000000..ff2e6b5608 --- /dev/null +++ b/documentation/source/science_guide/timestepping/tr_bdf2.rst @@ -0,0 +1,431 @@ +.. ----------------------------------------------------------------------------- + (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. + ----------------------------------------------------------------------------- + +.. _science_guide_timestepping_tr_bdf2: + +TR-BDF2 +======= + +Overview +-------- + +The Trapezoidal Backwards-Difference-2 (TR-BDF2) timestepper +is a two-stage semi-implicit method, using a +Quasi-Newton iterative structure like the :ref:`Semi-Implicit +` scheme. The Trapezoidal (TR) and +Backwards Difference (BDF2) stages each take the form of a Semi-Implicit +step. Unlike the Semi-Implicit Quasi-Newton (SIQN) scheme, TR-BDF2 has no +off-centering parameter. + +A TR-BDF2 timestepper may have several advantages over the SIQN scheme: + +* **Numerical stability and accuracy**: the SIQN scheme does not damp wave modes + unless it uses an implicit off-centering. The solution can then become + polluted by high-frequency waves (typically acoustic modes) triggered by + small-scale features such as those from physics parametrizations or orography. + The growth of these waves can cause numerical instabilities. In contrast, the + TR-BDF2 scheme is inherently damping of high-frequency waves, and in fact + is more damping of the highest frequencies than an off-centred SIQN scheme. + However, the TR-BDF2 scheme maintains second-order accuracy in time, which is + lost when off-centering is used in the SIQN scheme. +* **Computational efficiency**: the TR-BDF2 scheme consists of two stages, and + involves calculating a "midpoint" value. A single timestep with the TR-BDF2 + scheme involves approximately twice as much computational work as a SIQN + timestep, so it is natural to take twice the timestep length with TR-BDF2. + The TR-BDF2 formulation also provides the mathematical basis for calling some + schemes (physics parametrizations, or the transport of some variables) less + frequently, reducing the computational cost. Further, the improved stability + of the scheme may allow it to run stably with fewer Quasi-Newton iterations, + again improving the computational efficiency. + +The inspiration for much of this formulation comes from [Tumolo2015]_, +who presented a semi-Lagrangian formulation of the compressible Euler +equations using a TR-BDF2 timestepper with Discontinuous Galerkin spatial +discretisations. + +Details +------- + +Basic formulation +~~~~~~~~~~~~~~~~~~ + +The TR-BDF2 scheme is a two-stage scheme with a "midpoint" level denoted by +:math:`m`. Using the off-centering-free implicit weight :math:`\gamma`, it +follows: + +.. math:: :label: trbdf2_tr_stage + + \boldsymbol{X}^{m} + \gamma\Delta t \mathcal{F}(\boldsymbol{X}^m) + = \mathcal{T}^{2\gamma\Delta t}_{\overline{\boldsymbol{u}}_n^m} + \left[\boldsymbol{X}^n - \gamma\Delta t \mathcal{F}(\boldsymbol{X}^n)\right], + +.. math:: :label: trbdf2_bdf2_stage + + \boldsymbol{X}^{n+1} + \gamma_2\Delta t \mathcal{F}(\boldsymbol{X}^{n+1}) + = (1-\gamma_3)\mathcal{T}^{\Delta t}_{\overline{\boldsymbol{u}}_n^{n+1}} + \left[\boldsymbol{X}^n\right] + + \gamma_3\mathcal{T}^{(1-2\gamma)\Delta t}_{\overline{\boldsymbol{u}}_m^{n+1}} + \left[\boldsymbol{X}^m\right], + +with transporting velocities: + +.. math:: + + \overline{\boldsymbol{u}}_n^{m} = \tfrac{1}{2}\left(\boldsymbol{u}^n + \boldsymbol{u}^{m}\right), \qquad + \overline{\boldsymbol{u}}_n^{n+1} = \boldsymbol{u}^{n+1}, \qquad + \overline{\boldsymbol{u}}_m^{n+1} = \boldsymbol{u}^{n+1}. + +The parameters :math:`\gamma_2` and :math:`\gamma_3` are given by + +.. math:: + + \gamma_2 = \frac{1-2\gamma}{2(1-\gamma)}, \qquad + \gamma_3 = \frac{1-\gamma_2}{2\gamma}. + +GungHo takes :math:`\gamma=1-\sqrt{2}/2\approx 0.293`, which gives +:math:`\gamma_2=1-\sqrt{2}/2\approx 0.293` and +:math:`\gamma_3=(1+\sqrt{2})/2\approx 1.207`. The TR stage has an effective +timestep length of :math:`2\gamma\Delta t\approx 0.586\Delta t`, while the +BDF2 stage has an effective timestep length of +:math:`(1-2\gamma)\Delta t \approx 0.414\Delta t`. +Both stages take the same basic form as the Semi-Implicit scheme. + + +Combining BDF2 Transport Steps +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The BDF2 stage appears to require two separate transport calls. However, +these can be combined to develop a more efficient scheme. + +Introduce the intermediate variables: + +.. math:: + + \boldsymbol{X}^\ast=\boldsymbol{X}^n - \gamma\Delta t \mathcal{F}(\boldsymbol{X}^n), \qquad + \boldsymbol{X}^p = \mathcal{T}^{2\gamma\Delta t}_{\overline{\boldsymbol{u}}_n^m}\left[\boldsymbol{X}^\ast\right], \qquad + \boldsymbol{X}^q = \boldsymbol{X}^n + \boldsymbol{X}^p - \boldsymbol{X}^\ast. + +The TR step gives an approximation to the transport of :math:`\boldsymbol{X}^n` +over :math:`2\gamma\Delta t`: + +.. math:: + + \boldsymbol{X}^q + = \mathcal{T}^{2\gamma\Delta t}_{\overline{\boldsymbol{u}}_n^m}\left[\boldsymbol{X}^n - \gamma\Delta t \mathcal{F}(\boldsymbol{X}^n)\right] + \gamma\Delta t \mathcal{F}(\boldsymbol{X}^n) + \approx \mathcal{T}^{2\gamma\Delta t}_{\overline{\boldsymbol{u}}_n^m}\left[\boldsymbol{X}^n\right]. + +In the BDF2 stage, rather than transporting :math:`\boldsymbol{X}^n` over +:math:`\Delta t` from time level :math:`n` to :math:`n+1`, the approximation +from the TR stage can be used and transported over the remaining +:math:`(1-2\gamma)\Delta t`. Since the transporting velocity and length of +timestep is then the same as that used for :math:`\boldsymbol{X}^m`, these +fields can be added together and transported once: + +.. math:: + + \mathcal{T}^{\Delta t}_{\overline{\boldsymbol{u}}_n^{n+1}}\left[\boldsymbol{X}^n\right] + \approx \mathcal{T}^{(1-2\gamma)\Delta t}_{\overline{\boldsymbol{u}}_m^{n+1}} + \left[\mathcal{T}^{2\gamma\Delta t}_{\overline{\boldsymbol{u}}_n^{m}}\left[\boldsymbol{X}^n \right]\right], + +and + +.. math:: + + (1-\gamma_3)\mathcal{T}^{(1-2\gamma)\Delta t}_{\overline{\boldsymbol{u}}_m^{n+1}}\left[\boldsymbol{X}^q\right] + + \gamma_3 \mathcal{T}^{(1-2\gamma)\Delta t}_{\overline{\boldsymbol{u}}_m^{n+1}}\left[\boldsymbol{X}^m\right] + \approx + \mathcal{T}^{(1-2\gamma)\Delta t}_{\overline{\boldsymbol{u}}_m^{n+1}}\left[(1-\gamma_3)\boldsymbol{X}^q+ \gamma_3 \boldsymbol{X}^m\right]. + +The whole scheme is then written as: + +.. math:: :label: cheaper_trbdf2 + + \boldsymbol{X}^{m} + \gamma\Delta t \mathcal{F}(\boldsymbol{X}^m) = \boldsymbol{X}^p, \qquad + \boldsymbol{X}^{n+1} + \gamma_2\Delta t \mathcal{F}(\boldsymbol{X}^{n+1}) + = \mathcal{T}^{(1-2\gamma)\Delta t}_{\overline{\boldsymbol{u}}_m^{n+1}}\left[(1-\gamma_3)\boldsymbol{X}^q+ \gamma_3 \boldsymbol{X}^m\right]. + +This is the form of the TR-BDF2 scheme implemented in GungHo. Pseudo-code for +this "cheaper" version of the whole scheme is given in the +:ref:`Algorithm ` section +below. + +Transport +~~~~~~~~~ + +* The transport operator for dynamical prognostics in the TR stage is + :math:`\mathcal{T}_{\overline{u}_{n}^{m}}^{2\gamma\Delta t}`, where + :math:`\overline{u}_{n}^{m}=\tfrac{1}{2}(u^n+u^m)`; +* The transport operator for dynamical prognostics in the BDF2 stage is + :math:`\mathcal{T}_{\overline{u}_{m}^{n+1}}^{(1-2\gamma)\Delta t}`, where + :math:`\overline{u}_m^{n+1}=u^{n+1}`; +* The transport operator for tracers in the BDF2 stage is + :math:`\mathcal{T}_{\overline{u}_{n}^{n+1}}^{\Delta t}`, where + :math:`\overline{u}_{n}^{n+1}=\tfrac{1}{2}(u^n+u^{n+1})`. + +Linear solver +~~~~~~~~~~~~~ + +As for the Semi-Implicit scheme, each stage's implicit problem is solved +using the linear problem of the form + +.. math:: + + \boldsymbol{X}' + \sigma \mathcal{I}[\boldsymbol{X}'] + \tau_i \Delta t \mathcal{L}[\boldsymbol{X}'] = \boldsymbol{R}, + +with relaxation parameters :math:`\sigma` and :math:`\tau_i`. For the TR +stage, these are + +.. math:: + + \tau_u = \gamma, \qquad \tau_\rho=\tau_\theta=2\gamma, \qquad \sigma=2\gamma, + +while for the BDF2 stage they are + +.. math:: + + \tau_u = \gamma_2, \qquad \tau_\rho=\tau_\theta=1, \qquad \sigma=1. + +Physics coupling +~~~~~~~~~~~~~~~~~ + +Physics parametrisation schemes are included using the same slow physics +:math:`\mathcal{P}_S`, fast physics :math:`\mathcal{P}_F`, and end-of-step +physics :math:`\mathcal{P}_E` operators defined for the +:ref:`Semi-Implicit ` scheme. + +1. **Slow Physics**: This is evaluated on the + start-of-timestep state :math:`\boldsymbol{X}^n` before the TR step. +2. **Fast Physics**: This is evaluated within the outer loop of the BDF2 step. +3. **End-of-step Physics**: denoted :math:`\mathcal{P}_E`. Evaluated after the + Quasi-Newton iteration has completed for the BDF2 step. + +All three physics operators can be included simultaneously, giving intermediate +states :math:`\boldsymbol{X}^\dagger`, :math:`\boldsymbol{X}^{q+\dagger}` and +:math:`\boldsymbol{X}^{n+\dagger}` in addition to the +:math:`\boldsymbol{X}^\ast`, :math:`\boldsymbol{X}^p` and +:math:`\boldsymbol{X}^q` defined above, + +.. math:: :label: trbdf2_physics_coupling + + \begin{aligned} + \boldsymbol{X}^\dagger &= \boldsymbol{X}^n - \Delta t\,\mathcal{P}_S(\boldsymbol{X}^n), \\ + \boldsymbol{X}^\ast &= \boldsymbol{X}^\dagger - \gamma\Delta t\,\mathcal{F}(\boldsymbol{X}^n), \\ + \boldsymbol{X}^p &= \mathcal{T}^{2\gamma\Delta t}_{\overline{\boldsymbol{u}}_n^m}\left[\boldsymbol{X}^\ast\right], \\ + \boldsymbol{X}^{m} + \gamma\Delta t\,\mathcal{F}(\boldsymbol{X}^m) &= \boldsymbol{X}^p, \\ + \boldsymbol{X}^q &= \boldsymbol{X}^\dagger + \boldsymbol{X}^p-\boldsymbol{X}^\ast, \\ + \boldsymbol{X}^{q+\dagger} &= \mathcal{T}^{(1-2\gamma)\Delta t}_{\overline{\boldsymbol{u}}_m^{n+1}} + \left[(1-\gamma_3)\boldsymbol{X}^q+ \gamma_3 \boldsymbol{X}^m\right], \\ + \boldsymbol{X}^{n+\dagger} + \gamma_2\Delta t\,\mathcal{F}(\boldsymbol{X}^{n+\dagger}) + + \Delta t\,\mathcal{P}_F(\boldsymbol{X}^{n+\dagger}) + &= \boldsymbol{X}^{q+\dagger}, \\ + \boldsymbol{X}^{n+1} &= \boldsymbol{X}^{n+\dagger} - \Delta t\,\mathcal{P}_E(\boldsymbol{X}^{n+\dagger}). + \end{aligned} + +Moisture +~~~~~~~~~ + +To ensure correlation between water vapour and potential temperature, +moisture needs careful handling through the TR-BDF2 timestep. Post-solver +conservation corrections to moisture are included, which can be re-used to +update the moisture to be transported in the BDF2 stage. + +Moist receives a conservative correction at the beginning of the BDF step, using +the dry flux used to update the dry density from the linear solver. Since + +.. math:: + + \rho^{BDF}=(1-\gamma_3)\rho^p + \gamma_3\rho^m \equiv \rho^p -\gamma_3\boldsymbol{\nabla\cdot}\boldsymbol{F}^d_{TR}, + +where :math:`\boldsymbol{F}^d_{TR}` is the dry density flux from the TR solver, +the moisture mixing ratio is updated to be consistent with the dry density: + +.. math:: + + m^{BDF} = \dfrac{1}{\rho^{BDF}}\left[\rho^p m^p - \gamma_3\boldsymbol{\nabla\cdot}\boldsymbol{F}^m_{TR} \right]. + +where :math:`\boldsymbol{F}^m_{TR}` is the moisture flux calculated from +:math:`\boldsymbol{F}^d_{TR}` and the upwind values of :math:`m^p`. + +.. _science_guide_timestepping_tr_bdf2_algorithm: + +Algorithm +--------- + +Equation :eq:`cheaper_trbdf2` above summarises the whole TR-BDF2 +scheme, as implemented in GungHo. We now describe this in more detail as a +sequence of equations, using the same intermediate states +:math:`\boldsymbol{X}^\ast`, :math:`\boldsymbol{X}^p` and +:math:`\boldsymbol{X}^q` introduced above. + +Both of the TR and BDF2 stages are solved using the outer/inner Quasi-Newton +structure described in the :ref:`Semi-Implicit ` section. +The outer loop includes transport and fast physics terms, while the inner loop +calls the implicit forcing terms and the linear solver. + +The explicit forcing contribution is evaluated once, using forces at time +level :math:`n`, before the TR stage begins, + +.. math:: :label: trbdf2_alg_explicit_forcing + + \boldsymbol{X}^\ast = \boldsymbol{X}^n - \gamma\Delta t\,\mathcal{F}(\boldsymbol{X}^n). + +This is transported over :math:`2\gamma\Delta t`, using the velocity +:math:`\overline{\boldsymbol{u}}_n^m`, to give the TR-stage transported +state, + +.. math:: :label: trbdf2_alg_tr_transport + + \boldsymbol{X}^p = \mathcal{T}^{2\gamma\Delta t}_{\overline{\boldsymbol{u}}_n^m}\left[\boldsymbol{X}^\ast\right]. + +The TR-stage implicit problem is then solved for :math:`\boldsymbol{X}^m`, +using the outer/inner Quasi-Newton iteration with relaxation parameters +:math:`\tau_u=\gamma`, :math:`\tau_\rho=\tau_\theta=2\gamma`, +:math:`\sigma=2\gamma`, + +.. math:: :label: trbdf2_alg_tr_solve + + \boldsymbol{X}^{m} + \gamma\Delta t\,\mathcal{F}(\boldsymbol{X}^m) = \boldsymbol{X}^p. + +An approximation to the transport of :math:`\boldsymbol{X}^n` over +:math:`2\gamma\Delta t` is then formed, + +.. math:: :label: trbdf2_alg_q + + \boldsymbol{X}^q = \boldsymbol{X}^n + \boldsymbol{X}^p-\boldsymbol{X}^\ast. + +Convergence of the BDF2 stage can be accelerated by extrapolating from the +TR solution and the previous timestep, to give the first guess for the BDF2 +stage, via + +.. math:: :label: trbdf2_alg_bdf_first_guess + + \boldsymbol{X}^{n+1}_{(0)} = (1-1/(2\gamma))\boldsymbol{X}^n + 1/(2\gamma)\boldsymbol{X}^m. + +Finally, the combination :math:`(1-\gamma_3)\boldsymbol{X}^q+ \gamma_3 +\boldsymbol{X}^m` is transported over the remaining +:math:`(1-2\gamma)\Delta t`, using the velocity +:math:`\overline{\boldsymbol{u}}_m^{n+1}`, and the BDF2-stage implicit +problem is solved for :math:`\boldsymbol{X}^{n+1}`, using the outer/inner +Quasi-Newton iteration with relaxation parameters :math:`\tau_u=\gamma_2`, +:math:`\tau_\rho=\tau_\theta=1`, :math:`\sigma=1`, + +.. math:: :label: trbdf2_alg_bdf_solve + + \boldsymbol{X}^{n+1} + \gamma_2\Delta t\,\mathcal{F}(\boldsymbol{X}^{n+1}) + = \mathcal{T}^{(1-2\gamma)\Delta t}_{\overline{\boldsymbol{u}}_m^{n+1}} + \left[(1-\gamma_3)\boldsymbol{X}^q+ \gamma_3 \boldsymbol{X}^m\right]. + +This process is summarised by the pseudo-code below: + +.. code-block:: text + + Slow physics: + X^dag = X^n - dt * P_S(X^n) + + # TR Stage ------------------------------------------------------------------ + Explicit forcing: + X_FE = X^dag - gamma * dt * F(X^n) + Calculate transport predictor (X_FE) + + Set the first guess: + X^m_(0) = X^dag + + # Outer Loop ---------------------------------------------------------------- + for j = 1 to n_o_TR: + Update transporting velocity: + u_bar_n_m^(j) = 0.5 * (u_n + u_m^(j-1)) + Transport: + X^p_(j) = T_{u_bar_n_m^(j)}(X_FE) + Implicit forcing: + X_FI^(j,1) = X^{m}_(j-1) + gamma * dt * F(X^{m}_(j-1)) + + X^m_(j,0) = X^m_(j-1) + + # Inner Loop ------------------------------------------------------------ + for k = 1 to n_i_TR^j: + if k > 1: + Implicit forcing: + X^m_FI^(j,k) = X^m_(j,k-1) + gamma * dt * F(X^m_(j,k-1)) + Form the residual: + R_(j,k) = -( X^m_FI^(j,k) - X^p_(j) ) + Solve the linear system for the increment: + S_TR[X'] = R_(j,k) + Increment the state: + X^m_(j,k) = X^m_(j,k-1) + X' + + Set final estimate: + X^m_(j) = X^m_(j,n_i_TR^j) + + Advance the TR stage: + X^m = X^m_(n_o_TR) + X^p = X^p_(n_o_TR) + + # BDF2 Stage ---------------------------------------------------------------- + Form the approximate transport of X^n: + X^q = X^dag + X^p - X_FE + Set BDF2 pre-transport state: + X_BDF = (1 - gamma3) * X^q + gamma3 * X^m + + Set the first guess: + X^{n+1}_(0) = (1-1/(2*gamma)) * X^n + 1/(2*gamma) * X^m + + # Outer Loop ---------------------------------------------------------------- + for j = 1 to n_o_BDF: + Update transporting velocity: + u_bar_m_np1^(j) = u_{n+1}^(j-1) + Transport: + X^q_dag_(j) = T_{u_bar_m_np1^(j)}(X_BDF) + Implicit forcing: + X^{n+1}_FI^(j,1) = X^{n+1}_(j-1) + gamma2 * dt * F(X^{n+1}_(j-1)) + Fast physics predictor: + X^star_(j) = X^q_dag_(j) + (X^{n+1}_FI^(j,1) - X^{n+1}_(j-1)) + Fast physics increment: + dX_F^(j) = dt * P_F(X^star_(j)) + + X^{n+1}_(j,0) = X^{n+1}_(j-1) + + # Inner Loop ------------------------------------------------------------ + for k = 1 to n_i_BDF^j: + if k > 1: + Implicit forcing: + X^{n+1}_FI^(j,k) = \ + X^{n+1}_(j,k-1) + gamma2 * dt * F(X^{n+1}_(j,k-1)) + Form the residual: + R_(j,k) = -( X^{n+1}_FI^(j,k) - X^q_dag_(j) - dX_F^(j) ) + Solve the linear system for the increment: + S_BDF[X'] = R_(j,k) + Increment the state: + X^{n+1}_(j,k) = X^{n+1}_(j,k-1) + X' + + Set final estimate: + X^{n+1}_(j) = X^{n+1}_(j,n_i_BDF^j) + + End-of-step physics: + X^{n+1} = X^{n+1}_(n_o_BDF) - dt * P_E(X^{n+1}_(n_o_BDF)) + Advance the timestep: + X^n = X^{n+1} + +Tracer Transport +----------------- + +Tracers that do not feed back onto the dynamical core can be transported in +just the final outer iteration of the BDF2 stage. If following the advective +form of the transport equation, tracers can be transported using the +time-centered wind +:math:`\overline{\boldsymbol{u}}_n^{n+1} = \tfrac{1}{2}(\boldsymbol{u}^n + \boldsymbol{u}^{n+1})`. +Tracers that are transported conservatively can use the mass flux used to +transport the dry density over the combination of the TR and BDF2 steps. + +References +---------- + +.. [Tumolo2015] Tumolo, G. and Bonaventura, L. (2015). A semi-implicit, + semi-Lagrangian discontinuous Galerkin framework for adaptive numerical + weather prediction. + *Quarterly Journal of the Royal Meteorological Society*, 141(692), 2582-2601. + https://doi.org/10.1002/qj.2544 diff --git a/documentation/source/user_guide/index.rst b/documentation/source/user_guide/index.rst index 97cd4f3564..57995e0733 100644 --- a/documentation/source/user_guide/index.rst +++ b/documentation/source/user_guide/index.rst @@ -13,4 +13,5 @@ User Guide applications/index checkpointing/index + timestepping/index nudging/index diff --git a/documentation/source/user_guide/timestepping/index.rst b/documentation/source/user_guide/timestepping/index.rst new file mode 100644 index 0000000000..26384207ba --- /dev/null +++ b/documentation/source/user_guide/timestepping/index.rst @@ -0,0 +1,105 @@ +.. ------------------------------------------------------------------------------ + (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. + ------------------------------------------------------------------------------ + +.. _user_guide_timestepping: + +Timestepping +============ + +This page describes the namelist options in the ``timestepping`` namelist, +found in the ``lfric-gungho`` rose-meta metadata +(``science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf``), which control +how the GungHo dynamical core (and its coupling to physics schemes) is advanced +in time. A description of the underlying numerical schemes can be found in the +:ref:`Science Guide `. + +Choosing a timestepping method +------------------------------- + +The ``method`` option selects the overall timestepping algorithm. The +available values are: + +* ``semi_implicit``: the Semi-Implicit Quasi-Newton (SIQN) scheme. This is + GungHo's default timestepping scheme, evolved from the ENDGame dynamical + core's timestepping scheme. It combines an off-centred Crank-Nicolson scheme + with a Semi-Lagrangian treatment of transport (although in GungHo transport + has an Eulerian formulation). The scheme consists of a nested outer loop + (which includes transport and fast physics), and an inner loop which calls + the linear solver. An off-centering parameter, ``alpha``, controls the + implicitness of the scheme. +* ``tr_bdf2``: a two-stage semi-implicit method (Trapezoidal, then Backwards + Difference), using a Quasi-Newton iterative structure like the + Semi-Implicit scheme. Each of the two stages takes the form of a + Semi-Implicit step. There is no off-centering parameter, which improves the + accuracy of the scheme relative to Semi-Implicit. +* ``rk``: an explicit multi-stage Runge-Kutta method. This has not been + implemented with physics schemes. +* ``jules``: a timestepping scheme which calls the standalone JULES surface + scheme without coupling to a dynamical core. +* ``no_timestepping``: a null timestepping scheme, which does not call the + dynamics or physics schemes. Time-varying fields such as ancillaries and + LBCs are still updated on each timestep, and diagnostics are output. + +Iteration counts +----------------- + +The Semi-Implicit and TR-BDF2 schemes both involve nested outer and inner +iterative loops. The outer loop includes transport and fast physics terms; the +inner loop calls the implicit forcing terms and the linear solver. + +* ``outer_iterations``: the number of outer iterations to perform. This + applies to both the Semi-Implicit and TR-BDF2 schemes (and to the ``jules`` + method). +* ``inner_iterations_si``: the number of inner Newton iterations to perform + within the Semi-Implicit scheme. +* ``inner_iterations_tr``: the number of inner Newton iterations to perform in + the Trapezoidal (TR) stage of the TR-BDF2 scheme. +* ``inner_iterations_bdf2``: the number of inner Newton iterations to perform + in the Backwards Difference (BDF2) stage of the TR-BDF2 scheme. + +Semi-Implicit off-centering and relaxation parameters +------------------------------------------------------- + +These options are only relevant when ``method='semi_implicit'``. + +* ``alpha``: the off-centering parameter for the Semi-Implicit scheme, in the + range ``0.0`` to ``1.0``. A value of ``0.5`` corresponds to a + Crank-Nicolson scheme, while a value of ``1.0`` corresponds to a fully + implicit scheme. A value of ``0.5`` can be appropriate for dynamics-only + runs, but can lead to noisy solutions when orography and physics are included. + For this reason, the model is typically run with a value slightly above + ``0.5``. +* ``spinup_alpha``: if ``.true.``, the off-centering parameter is set to + ``1`` (fully implicit) during the spin-up period, to improve stability. + +The relaxation parameters give the implicitness weighting of semi-implicit +terms (for example transport or forcing terms) on the left-hand side of the +linear solver. See the :ref:`Science Guide ` for +the role that these parameters play in the linear solver formulation. + +* ``tau_u``: relaxation parameter for the momentum equation. It is + recommended to set ``tau_u=alpha``. +* ``tau_r``: relaxation parameter for density. When using more than one + Quasi-Newton inner iteration, it is recommended to set ``tau_r=1``, as the + density transport term is not updated between inner iterations. +* ``tau_t``: relaxation parameter for potential temperature. When using more + than one Quasi-Newton inner iteration, it is recommended to set + ``tau_t=1``, as the potential temperature transport term is not updated + between inner iterations. + +Runge-Kutta options +-------------------- + +This option is only relevant when ``method='rk'``. + +* ``runge_kutta_method``: selects the choice of explicit Runge-Kutta scheme + used either to timestep the whole dynamical core, or for use with the + Method-of-Lines transport scheme. The Strong Stability Preserving (SSP) + methods are explicit multi-stage methods which combine the values over + multiple stages as a weighted linear sum to provide stability. Available + values are ``forward_euler``, ``ssp2``, ``ssp3``, ``ssp4`` and ``ssp5``. diff --git a/rose-stem/app/gungho_model/opt/rose-app-schar_cart.conf b/rose-stem/app/gungho_model/opt/rose-app-schar_cart.conf index c5f027b994..23ecc66546 100644 --- a/rose-stem/app/gungho_model/opt/rose-app-schar_cart.conf +++ b/rose-stem/app/gungho_model/opt/rose-app-schar_cart.conf @@ -27,7 +27,7 @@ smp_init_wind=.true. u0=10.0 [namelist:io] -diagnostic_frequency=900 +diagnostic_frequency=450 [namelist:orography] orog_init_option='analytic' diff --git a/rose-stem/app/gungho_model/opt/rose-app-skamarock_klemp_gw_p0.conf b/rose-stem/app/gungho_model/opt/rose-app-skamarock_klemp_gw_p0.conf index f3b1c3d13d..9c14f09289 100644 --- a/rose-stem/app/gungho_model/opt/rose-app-skamarock_klemp_gw_p0.conf +++ b/rose-stem/app/gungho_model/opt/rose-app-skamarock_klemp_gw_p0.conf @@ -14,7 +14,7 @@ profile='constant_uv' u0=20.0 [namelist:io] -diagnostic_frequency=100 +diagnostic_frequency=50 [namelist:partitioning] partitioner='planar' diff --git a/rose-stem/app/gungho_model/opt/rose-app-tr_bdf2.conf b/rose-stem/app/gungho_model/opt/rose-app-tr_bdf2.conf new file mode 100644 index 0000000000..81751d204e --- /dev/null +++ b/rose-stem/app/gungho_model/opt/rose-app-tr_bdf2.conf @@ -0,0 +1,4 @@ +[namelist:timestepping] +inner_iterations_bdf2=1,1 +inner_iterations_tr=2,1 +method='tr_bdf2' diff --git a/rose-stem/app/lfric_atm/opt/README b/rose-stem/app/lfric_atm/opt/README index 1185bd5947..7a33419dfc 100644 --- a/rose-stem/app/lfric_atm/opt/README +++ b/rose-stem/app/lfric_atm/opt/README @@ -65,6 +65,11 @@ Science settings transport scheme and mixed solver configuration - ral_opts - extra options for regional models, including LBC blending weights - reynolds - use Reynolds daily SST time-series +- tr_bdf2 - using the TR-BDF2 timestepper +- tr_bdf2_big - using the TR-BDF2 timestepper and a larger stencil extent +- tr_bdf2_big_ral - using the TR-BDF2 timestepper, a larger stencil extent and + an alternative radiation timestep, appropriate to a 2.5 min + overall timestep for regional models - ukca - UKCA Glomap aerosol scheme - urban2t - 2-tile urban scheme - dry_atm - a dry atmosphere diff --git a/rose-stem/app/lfric_atm/opt/rose-app-tr_bdf2.conf b/rose-stem/app/lfric_atm/opt/rose-app-tr_bdf2.conf new file mode 100644 index 0000000000..848f702c65 --- /dev/null +++ b/rose-stem/app/lfric_atm/opt/rose-app-tr_bdf2.conf @@ -0,0 +1,7 @@ +[namelist:formulation] +solver_moisture_conservation=.true. + +[namelist:timestepping] +inner_iterations_bdf2=1,1 +inner_iterations_tr=2,1 +method='tr_bdf2' diff --git a/rose-stem/app/lfric_atm/opt/rose-app-tr_bdf2_big.conf b/rose-stem/app/lfric_atm/opt/rose-app-tr_bdf2_big.conf new file mode 100644 index 0000000000..4bb06ef223 --- /dev/null +++ b/rose-stem/app/lfric_atm/opt/rose-app-tr_bdf2_big.conf @@ -0,0 +1,17 @@ +[namelist:formulation] +solver_moisture_conservation=.true. + +[namelist:timestepping] +inner_iterations_bdf2=1,1 +!!inner_iterations_si=2,2 +inner_iterations_tr=2,1 +method='tr_bdf2' +!!tau_r=1.0 +!!tau_t=1.0 +!!tau_u=0.55 + +[namelist:radiation] +n_radstep=3 + +[namelist:transport] +dep_pt_stencil_extent=10 diff --git a/rose-stem/app/lfric_atm/opt/rose-app-tr_bdf2_big_ral.conf b/rose-stem/app/lfric_atm/opt/rose-app-tr_bdf2_big_ral.conf new file mode 100644 index 0000000000..0b1d5ffbce --- /dev/null +++ b/rose-stem/app/lfric_atm/opt/rose-app-tr_bdf2_big_ral.conf @@ -0,0 +1,19 @@ +[namelist:formulation] +solver_moisture_conservation=.true. + +[namelist:timestepping] +inner_iterations_bdf2=1,1 +!!inner_iterations_si=2,2 +inner_iterations_tr=2,1 +method='tr_bdf2' +!!tau_r=1.0 +!!tau_t=1.0 +!!tau_u=0.55 + +[namelist:radiation] +l_inc_radstep=.true. +n_inc_radstep=2 +n_radstep=6 + +[namelist:transport] +dep_pt_stencil_extent=10 diff --git a/rose-stem/site/common/gungho_model/tasks_gungho_model.cylc b/rose-stem/site/common/gungho_model/tasks_gungho_model.cylc index d7c72341c9..bdb6c1547e 100644 --- a/rose-stem/site/common/gungho_model/tasks_gungho_model.cylc +++ b/rose-stem/site/common/gungho_model/tasks_gungho_model.cylc @@ -127,6 +127,18 @@ "plot_str": "baroclinic.py $NODAL_DATA_DIR/lfric_diag.nc $PLOT_DIR dcmip dry surface_pressure_temperature", }) %} +{% elif task_ns.conf_name == "baroclinic-tr_bdf2-C24_MG" %} + + {% do task_dict.update({ + "opt_confs": ["baroclinic", "tr_bdf2"], + "resolution": "C24_MG", + "DT": 3600, + "threads": 4, + "mpi_parts": 6, + "tsteps": 240, + "plot_str": "baroclinic.py $NODAL_DATA_DIR/lfric_diag.nc $PLOT_DIR dcmip dry surface_pressure_temperature", + }) %} + {% elif task_ns.conf_name == "baroclinic-no_io-C24_MG" %} {% do task_dict.update({ @@ -235,6 +247,17 @@ "plot_str": "plot_zonal.py $NODAL_DATA_DIR/lfric_diag.nc $PLOT_DIR held-suarez earth dcmip 30 31" }) %} +{% elif task_ns.conf_name == "held-suarez-tr_bdf2-C24_MG" %} + + {% do task_dict.update({ + "opt_confs": ["held-suarez", "io_nwp", "tr_bdf2"], + "resolution": "C24_MG", + "DT": 3600, + "mpi_parts": 6, + "tsteps": 480, + "plot_str": "plot_zonal.py $NODAL_DATA_DIR/lfric_diag.nc $PLOT_DIR held-suarez earth dcmip 30 31" + }) %} + {% elif task_ns.conf_name == "lfric-real-domain-C48_MG" %} {% do task_dict.update({ @@ -360,6 +383,18 @@ "plot_str": "sbr.py $NODAL_DATA_DIR/lfric_diag.nc $PLOT_DIR um70 80 71 sbr", }) %} +{% elif task_ns.conf_name == "sbr-tr_bdf2-C24_MG" %} + + {% do task_dict.update({ + "opt_confs": ["sbr", "tr_bdf2"], + "resolution": "C24_MG", + "DT": 3600, + "mpi_parts": 6, + "tsteps": 240, + "threads": 4, + "plot_str": "sbr.py $NODAL_DATA_DIR/lfric_diag.nc $PLOT_DIR um70 80 71 sbr", + }) %} + {% elif task_ns.conf_name == "sbr_lam-n96_MG_lam" %} {% do task_dict.update({ @@ -430,6 +465,21 @@ "plot_str": "cartesian_mountain_plot.py diagGungho $NODAL_DATA_DIR u 3 T000900 2 2 4 schar zoom_1 lines $PLOT_DIR", }) %} +{% elif task_ns.conf_name == "schar_cart-tr_bdf2-BiP200x8-500x500" %} + + {% do task_dict.update({ + "opt_confs": ["schar_cart", "tr_bdf2"], + "resolution": "BiP200x8-500x500", + "DT": 40, + "mpi_parts": 5, + "tsteps": 450, + "threads": 3, + "panel_decomp": "row", + "wallclock": 30, + "use_xios": false, + "plot_str": "cartesian_mountain_plot.py diagGungho $NODAL_DATA_DIR u 3 T000450 2 2 8 schar zoom_1 lines $PLOT_DIR", + }) %} + {% elif task_ns.conf_name == "semi-implicit-for-linear-C12" %} {% do task_dict.update({ @@ -465,6 +515,17 @@ "plot_str": "gw_cart_plot.py diagGungho $NODAL_DATA_DIR 300 8 theta T000000:T000100:T000200:T000300 $PLOT_DIR", }) %} +{% elif task_ns.conf_name == "skamarock_klemp_gw-tr_bdf2-BiP300x8-1000x2000" %} + + {% do task_dict.update({ + "opt_confs": ["skamarock_klemp_gw_p0", "tr_bdf2"], + "resolution": "BiP300x8-1000x2000", + "DT": 20, + "tsteps": 150, + "use_xios": false, + "plot_str": "gw_cart_plot.py diagGungho $NODAL_DATA_DIR 300 8 theta T000000:T000050:T000100:T000150 $PLOT_DIR", + }) %} + {% elif task_ns.conf_name == "skamarock_klemp_gw_p1-BiP75x4-4000x2000" %} {% do task_dict.update({ @@ -562,6 +623,19 @@ "plot_str": "straka_plot_x.py diagGungho $NODAL_DATA_DIR 256 8 theta T000180 $PLOT_DIR", }) %} +{% elif task_ns.conf_name == "straka_200m-tr_bdf2-BiP256x8-200x200" %} + + {% do task_dict.update({ + "opt_confs": ["straka_200m", "tr_bdf2"], + "resolution": "BiP256x8-200x200", + "DT": 10, + "mpi_parts": 8, + "tsteps": 90, + "panel_decomp": "row", + "use_xios": false, + "plot_str": "straka_plot_x.py diagGungho $NODAL_DATA_DIR 256 8 theta T000090 $PLOT_DIR", + }) %} + {% elif task_ns.conf_name == "tidally-locked-earth-C24s_rot_MG" %} {% do task_dict.update({ diff --git a/rose-stem/site/common/gungho_model/tasks_gungho_model_weekly.cylc b/rose-stem/site/common/gungho_model/tasks_gungho_model_weekly.cylc index 5f8ee5d23c..3768101007 100644 --- a/rose-stem/site/common/gungho_model/tasks_gungho_model_weekly.cylc +++ b/rose-stem/site/common/gungho_model/tasks_gungho_model_weekly.cylc @@ -63,6 +63,19 @@ "plot_str": "baroclinic.py $NODAL_DATA_DIR/lfric_diag.nc $PLOT_DIR dcmip dry surface_pressure_temperature", }) %} +{% elif task_ns.conf_name == "baroclinic-tr_bdf2-C192_MG" %} + + {% do task_dict.update({ + "opt_confs": ["baroclinic", "tr_bdf2"], + "resolution": "C192_MG", + "DT": 1800, + "mpi_parts": 384, + "tsteps": 480, + "wallclock": 30, + "kgo_checks": [], + "plot_str": "baroclinic.py $NODAL_DATA_DIR/lfric_diag.nc $PLOT_DIR dcmip dry surface_pressure_temperature", + }) %} + {% elif task_ns.conf_name == "baroclinic-no_io-C192_MG" %} {% do task_dict.update({ 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 9f3cff3d50..0adaca5953 100644 --- a/rose-stem/site/common/lfric_atm/tasks_lfric_atm.cylc +++ b/rose-stem/site/common/lfric_atm/tasks_lfric_atm.cylc @@ -105,6 +105,21 @@ theta", }) %} +{% elif task_ns.conf_name == "nwp_gal10-tr_bdf2-C12" %} + + {% do task_dict.update({ + "opt_confs": ["tr_bdf2", "um_dump"], + "resolution": "C12", + "DT": 1800, + "tsteps": 144, + "crun": 2, + "crun_compare": false, + "metadata_test": true, + "mpi_parts": 6, + "kgo_checks": ["checksum"], + "plot_str": "plot_map.py $NODAL_DATA_DIR/lfric_diagnostics.nc $PLOT_DIR", + }) %} + {% elif task_ns.conf_name == "nwp_gal10_1T-C12" %} {% do task_dict.update({ "opt_confs": ["physics_segmentation","um_dump","no_diags"], @@ -282,6 +297,22 @@ "memory_plot_ex": true, }) %} +{% elif task_ns.conf_name == "nwp_gal10-tr_bdf2-C224_MG" %} + + {% do task_dict.update({ + "opt_confs": ["tr_bdf2_big", "um_dump","june_case"], + "resolution": "C224_MG", + "DT": 1200, + "tsteps": 72, + "mpi_parts": 588, + "xios_nodes": 1, + "mpi_parts_xios" : 64, + "log_level": "info", + "xios_info_level": 1, + "xios_print_file": ".true.", + "plot_str": "plot_map.py $NODAL_DATA_DIR/lfric_diagnostics.nc $PLOT_DIR", + }) %} + {% elif task_ns.conf_name == "nwp_gal10_debug-C224_MG" %} {% do task_dict.update({ @@ -724,6 +755,20 @@ "plot_str": "plot_map.py -encorr $NODAL_DATA_DIR/lfric_diagnostics.nc $PLOT_DIR", }) %} +{% elif task_ns.conf_name == "clim_gal10_chem-tr_bdf2-C12" %} + + {% do task_dict.update({ + "opt_confs": ["climate","reynolds","chemistry","tr_bdf2","lowres_stp","um_dump"], + "resolution": "C12", + "DT": 1800, + "tsteps": 144, + "crun": 2, + "crun_compare": false, + "mpi_parts": 6, + "kgo_checks": ["checksum"], + "plot_str": "plot_map.py -encorr $NODAL_DATA_DIR/lfric_diagnostics.nc $PLOT_DIR", + }) %} + {% elif task_ns.conf_name == "clim_gal10_chem_1T-C12" %} {% do task_dict.update({ "opt_confs": ["climate","reynolds","chemistry", "lowres_stp","physics_segmentation","um_dump","no_diags"], @@ -898,6 +943,18 @@ "xios_min_buffer_size": 2097152, }) %} +{% elif task_ns.conf_name == "ral4-tr_bdf2-seuk_MG" %} + + {% do task_dict.update({ + "opt_confs": ["l70_40km","ral4","ral_opts","hh_solve","native_wind_transport","tr_bdf2_big_ral"], + "resolution": "seuk_MG", + "DT": 150, + "tsteps": 48, + "mpi_parts": 16, + "kgo_checks": ["checksum"], + "plot_str": "plot_map.py -ral $NODAL_DATA_DIR/lfric_diagnostics.nc $PLOT_DIR", + }) %} + {% elif task_ns.conf_name == "ral4_mixmol-uk_MG" %} {% do task_dict.update({ diff --git a/rose-stem/site/meto/groups/groups_gungho_model.cylc b/rose-stem/site/meto/groups/groups_gungho_model.cylc index 9ac4d72c10..14a4389ff9 100644 --- a/rose-stem/site/meto/groups/groups_gungho_model.cylc +++ b/rose-stem/site/meto/groups/groups_gungho_model.cylc @@ -10,6 +10,7 @@ "gungho_model_azspice_developer": [ "gungho_model_agnesi_hyd_cart-BiP120x8-2000x2000_azspice_gnu_fast-debug-64bit", "gungho_model_baroclinic-C24_MG_azspice_gnu_fast-debug-64bit", + "gungho_model_baroclinic-tr_bdf2-C24_MG_azspice_gnu_fast-debug-64bit", "gungho_model_baroclinic-C48_MG_azspice_gnu_fast-debug-64bit", "gungho_model_baroclinic-C48_MG-2panel_azspice_gnu_fast-debug-64bit", "gungho_model_baroclinic-C48_MG-3panel_azspice_gnu_fast-debug-64bit", @@ -26,6 +27,7 @@ "gungho_model_force_profile-BiP4x4-600x400_azspice_gnu_fast-debug-64bit", "gungho_model_geostrophic-BiP4x4-600x400_azspice_gnu_fast-debug-64bit", "gungho_model_held-suarez-C24_MG_azspice_gnu_fast-debug-64bit", + "gungho_model_held-suarez-tr_bdf2-C24_MG_azspice_gnu_fast-debug-64bit", "gungho_model_lfric-real-domain-C48_MG_azspice_gnu_fast-debug-64bit", "gungho_model_no-timestep-method-C12_azspice_gnu_fast-debug-64bit", "gungho_model_relax_profile-BiP4x4-600x400_azspice_gnu_fast-debug-64bit", @@ -35,16 +37,19 @@ "gungho_model_robert-moist-smag-BiP100x8-10x10_azspice_gnu_fast-debug-64bit", "gungho_model_runge-kutta-for-linear-C12_azspice_gnu_fast-debug-64bit", "gungho_model_sbr-C24_MG_azspice_gnu_fast-debug-64bit", + "gungho_model_sbr-tr_bdf2-C24_MG_azspice_gnu_fast-debug-64bit", "gungho_model_sbr-alt2-C24_MG_op_azspice_gnu_fast-debug-64bit", "gungho_model_sbr-alt3-C24_MG_azspice_gnu_fast-debug-64bit-rtran32", "gungho_model_sbr_lam-n96_MG_lam_azspice_gnu_fast-debug-64bit", "gungho_model_sbr_lam-n96_MG_lam_rotate_azspice_gnu_fast-debug-64bit", "gungho_model_schar_cart-BiP200x8-500x500_azspice_gnu_fast-debug-64bit", + "gungho_model_schar_cart-tr_bdf2-BiP200x8-500x500_azspice_gnu_fast-debug-64bit", "gungho_model_schar_cart-consistent-BiP200x8-500x500_azspice_gnu_fast-debug-64bit", "gungho_model_schar_cart-alt2-BiP100x4-1000x1000_azspice_gnu_fast-debug-64bit", "gungho_model_semi-implicit-for-linear-C12_azspice_gnu_fast-debug-64bit", "gungho_model_shallow-hot-jupiter-C24_MG_azspice_gnu_fast-debug-64bit", "gungho_model_skamarock_klemp_gw_p0-BiP300x8-1000x2000_azspice_gnu_fast-debug-64bit", + "gungho_model_skamarock_klemp_gw-tr_bdf2-BiP300x8-1000x2000_azspice_gnu_fast-debug-64bit", "gungho_model_skamarock_klemp_gw_p1-BiP75x4-4000x2000_azspice_gnu_fast-debug-64bit", "gungho_model_skamarock_klemp_gw_ph1pv0-BiP75x4-4000x2000_azspice_gnu_fast-debug-64bit", "gungho_model_skamarock_klemp_gw_ph0pv1-BiP75x4-4000x2000_azspice_gnu_fast-debug-64bit", @@ -52,6 +57,7 @@ "gungho_model_straka_200m-alt1-BiP256x4-200x200_azspice_gnu_fast-debug-64bit", "gungho_model_straka_200m-alt2-BiP256x16-200x50_op_azspice_gnu_fast-debug-64bit", "gungho_model_straka_200m-alt3-BiP256x8-200x200_azspice_gnu_fast-debug-64bit-rtran32", + "gungho_model_straka_200m-tr_bdf2-BiP256x8-200x200_azspice_gnu_fast-debug-64bit", "gungho_model_tidally-locked-earth-C24s_rot_MG_azspice_gnu_fast-debug-64bit", "gungho_model_tidally-locked-earth-C24_MG_azspice_gnu_fast-debug-64bit", "gungho_model_azspice_canned", @@ -79,6 +85,7 @@ "gungho_model_baroclinic-alt1-C24s_MG_ex1a_gnu_fast-debug-64bit", "gungho_model_baroclinic-alt2-C24_MG_op_ex1a_gnu_fast-debug-64bit", "gungho_model_baroclinic-alt3-C24_MG_ex1a_gnu_fast-debug-64bit", + "gungho_model_baroclinic-tr_bdf2-C24_MG_ex1a_gnu_fast-debug-64bit", "gungho_model_baroclinic-profile_perf-C24_MG_ex1a_perftools-gnu_fast-debug-64bit", "gungho_model_bryan_fritsch-dry-BiP200x10-100x100_ex1a_gnu_fast-debug-64bit", "gungho_model_dcmip200-C24_MG_ex1a_gnu_fast-debug-64bit", @@ -89,6 +96,7 @@ "gungho_model_force_profile-BiP4x4-600x400_ex1a_gnu_fast-debug-64bit", "gungho_model_geostrophic-BiP4x4-600x400_ex1a_gnu_fast-debug-64bit", "gungho_model_held-suarez-C24_MG_ex1a_gnu_fast-debug-64bit", + "gungho_model_held-suarez-tr_bdf2-C24_MG_ex1a_gnu_fast-debug-64bit", "gungho_model_lfric-real-domain-C48_MG_ex1a_gnu_fast-debug-64bit", "gungho_model_no-timestep-method-C12_ex1a_gnu_fast-debug-64bit", "gungho_model_rk-dcmip301-C24_ex1a_gnu_fast-debug-64bit", @@ -98,14 +106,17 @@ "gungho_model_sbr-C24_MG_ex1a_gnu_fast-debug-64bit", "gungho_model_sbr-alt2-C24_MG_op_ex1a_gnu_fast-debug-64bit", "gungho_model_sbr-alt3-C24_MG_ex1a_gnu_fast-debug-64bit-rtran32", + "gungho_model_sbr-tr_bdf2-C24_MG_ex1a_gnu_fast-debug-64bit", "gungho_model_sbr_lam-n96_MG_lam_ex1a_gnu_fast-debug-64bit", "gungho_model_sbr_lam-n96_MG_lam_rotate_ex1a_gnu_fast-debug-64bit", "gungho_model_schar_cart-BiP200x8-500x500_ex1a_gnu_fast-debug-64bit", "gungho_model_schar_cart-consistent-BiP200x8-500x500_ex1a_gnu_fast-debug-64bit", "gungho_model_schar_cart-alt2-BiP100x4-1000x1000_ex1a_gnu_fast-debug-64bit", + "gungho_model_schar_cart-tr_bdf2-BiP200x8-500x500_ex1a_gnu_fast-debug-64bit", "gungho_model_semi-implicit-for-linear-C12_ex1a_gnu_fast-debug-64bit", "gungho_model_shallow-hot-jupiter-C24_MG_ex1a_gnu_fast-debug-64bit", "gungho_model_skamarock_klemp_gw_p0-BiP300x8-1000x2000_ex1a_gnu_fast-debug-64bit", + "gungho_model_skamarock_klemp_gw-tr_bdf2-BiP300x8-1000x2000_ex1a_gnu_fast-debug-64bit", "gungho_model_skamarock_klemp_gw_p1-BiP75x4-4000x2000_ex1a_gnu_fast-debug-64bit", "gungho_model_skamarock_klemp_gw_ph1pv0-BiP75x4-4000x2000_ex1a_gnu_fast-debug-64bit", "gungho_model_skamarock_klemp_gw_ph0pv1-BiP75x4-4000x2000_ex1a_gnu_fast-debug-64bit", @@ -113,6 +124,7 @@ "gungho_model_straka_200m-alt1-BiP256x4-200x200_ex1a_gnu_fast-debug-64bit", "gungho_model_straka_200m-alt2-BiP256x16-200x50_op_ex1a_gnu_fast-debug-64bit", "gungho_model_straka_200m-alt3-BiP256x8-200x200_ex1a_gnu_fast-debug-64bit", + "gungho_model_straka_200m-tr_bdf2-BiP256x8-200x200_ex1a_gnu_fast-debug-64bit", "gungho_model_tidally-locked-earth-C24s_rot_MG_ex1a_gnu_fast-debug-64bit", "gungho_model_tidally-locked-earth-C24_MG_ex1a_gnu_fast-debug-64bit", "gungho_model_ex1a_canned", @@ -250,6 +262,7 @@ "gungho_model_baroclinic-C48_MG_ex1a_gnu_fast-debug-64bit", "gungho_model_baroclinic-C96_MG_ex1a_gnu_fast-debug-64bit", "gungho_model_baroclinic-C192_MG_ex1a_gnu_fast-debug-64bit", + "gungho_model_baroclinic-tr_bdf2-C192_MG_ex1a_gnu_fast-debug-64bit", "gungho_model_bell_3d_cart-BiP300x200-200x200_ex1a_gnu_fast-debug-64bit", "gungho_model_bryan_fritsch-moist-BiP200x10-100x100_ex1a_gnu_fast-debug-64bit", "gungho_model_earth-like-C48_MG_ex1a_gnu_fast-debug-64bit", diff --git a/rose-stem/site/meto/groups/groups_lfric_atm.cylc b/rose-stem/site/meto/groups/groups_lfric_atm.cylc index 6c5397dd34..852ba0c8aa 100644 --- a/rose-stem/site/meto/groups/groups_lfric_atm.cylc +++ b/rose-stem/site/meto/groups/groups_lfric_atm.cylc @@ -26,6 +26,7 @@ "lfric_atm_nwp_gal10_eda_jada-C12_azspice_gnu_fast-debug-32bit", "lfric_atm_nwp_gal10_ls_and_jedi-C12_azspice_gnu_fast-debug-32bit", "lfric_atm_nwp_gal10-pert-C12_azspice_gnu_fast-debug-32bit", + "lfric_atm_nwp_gal10-tr_bdf2-C12_azspice_gnu_fast-debug-32bit", "lfric_atm_nwp_gal10_regrav-C12_azspice_gnu_fast-debug-32bit", "lfric_atm_nwp_casim-C12_azspice_gnu_fast-debug-32bit", "lfric_atm_nwp_coma9-C12_azspice_gnu_fast-debug-32bit", @@ -41,6 +42,7 @@ "lfric_atm_ral4_ens-seuk_MG_azspice_gnu_fast-debug-32bit", "lfric_atm_ral_dev-seuk_MG_azspice_gnu_fast-debug-32bit", "lfric_atm_ral4_mixmol-seuk_MG_azspice_gnu_fast-debug-32bit", + "lfric_atm_ral4-tr_bdf2-seuk_MG_azspice_gnu_fast-debug-32bit", ], "lfric_atm_scm_azspice_developer": [ "lfric_atm_scm_gal10_bomex-BiP2x2-50000x50000_azspice_gnu_fast-debug-32bit", @@ -83,6 +85,7 @@ "lfric_atm_azspice_extra": [ "lfric_atm_nwp_azspice_extra", "lfric_atm_clim_gal10_chem-C12_azspice_gnu_fast-debug-32bit", + "lfric_atm_clim_gal10_chem-tr_bdf2-C12_azspice_gnu_fast-debug-32bit", "lfric_atm_ral4-seuk_ls_and_jedi_azspice_gnu_fast-debug-32bit", "lfric_atm_aquaplanet-C12_azspice_gnu_fast-debug-32bit", "lfric_atm_thai_ben1-C48_MG_azspice_gnu_fast-debug-32bit", @@ -133,6 +136,7 @@ "lfric_atm_nwp_gal10_eda_jada-C12_ex1a_cce_fast-debug-32bit", "lfric_atm_nwp_gal10_ls_and_jedi-C12_ex1a_cce_fast-debug-32bit", "lfric_atm_nwp_gal10-pert-C12_ex1a_cce_fast-debug-32bit", + "lfric_atm_nwp_gal10-tr_bdf2-C12_ex1a_cce_fast-debug-32bit", "lfric_atm_nwp_casim-C12_ex1a_cce_fast-debug-32bit", "lfric_atm_nwp_coma9-C12_ex1a_cce_fast-debug-32bit", "lfric_atm_nwp_comorph_tb-C12_ex1a_cce_fast-debug-32bit", @@ -148,6 +152,7 @@ "lfric_atm_ral4_ens-seuk_MG_ex1a_cce_fast-debug-32bit", "lfric_atm_ral_dev-seuk_MG_ex1a_cce_fast-debug-32bit", "lfric_atm_ral4_mixmol-seuk_MG_ex1a_cce_fast-debug-32bit", + "lfric_atm_ral4-tr_bdf2-seuk_MG_ex1a_cce_fast-debug-32bit", ], "lfric_atm_ral_ex1a_weekly": [ "lfric_atm_ral4-uk_MG_ex1a_cce_production-32bit", @@ -201,6 +206,7 @@ "lfric_atm_ex1a_extra": [ "lfric_atm_nwp_ex1a_extra", "lfric_atm_clim_gal10_chem-C12_ex1a_cce_fast-debug-32bit", + "lfric_atm_clim_gal10_chem-tr_bdf2-C12_ex1a_cce_fast-debug-32bit", "lfric_atm_ral4-seuk_ls_and_jedi_ex1a_cce_fast-debug-32bit", "lfric_atm_comp_tran_ref_3d_l120-BiP64x64-1500x1500_MG_ex1a_cce_fast-debug-32bit", "lfric_atm_aquaplanet-C12_ex1a_cce_fast-debug-32bit", @@ -215,6 +221,7 @@ "lfric_atm_nwp_gal10-C224_MG_ex1a_cce_production-32bit-milan", "lfric_atm_nwp_gal10-C224_MG_ex1a_cce_production-32bit-genoa", "lfric_atm_nwp_gal10-C224_MG_op_ex1a_cce_production-32bit", + "lfric_atm_nwp_gal10-tr_bdf2-C224_MG_ex1a_cce_production-32bit", "lfric_atm_nwp_gal10_oper-C224_MG_ex1a_cce_production-32bit", "lfric_atm_nwp_gal10_debug-C224_MG_ex1a_cce_full-debug-32bit", "lfric_atm_nwp_gal10_ls_and_jedi-C224_MG_ex1a_cce_production-32bit", diff --git a/rose-stem/site/meto/lfric_atm/tasks_lfric_atm_ex1a.cylc b/rose-stem/site/meto/lfric_atm/tasks_lfric_atm_ex1a.cylc index a758beaf91..4d32557275 100644 --- a/rose-stem/site/meto/lfric_atm/tasks_lfric_atm_ex1a.cylc +++ b/rose-stem/site/meto/lfric_atm/tasks_lfric_atm_ex1a.cylc @@ -46,12 +46,24 @@ "memory": [24, "GB"], }) %} +{% elif task_ns.conf_name == "clim_gal10_chem-tr_bdf2-C12" %} + + {% do task_dict.update({ + "memory": [24, "GB"], + }) %} + {% elif task_ns.conf_name == "ral4-seuk_MG" %} {% do task_dict.update({ "memory": [24, "GB"], }) %} +{% elif task_ns.conf_name == "ral4-tr_bdf2-seuk_MG" %} + + {% do task_dict.update({ + "memory": [24, "GB"], + }) %} + {% elif task_ns.conf_name == "ral4-seuk_ls_and_jedi" %} {% do task_dict.update({ diff --git a/science/adjoint/source/algorithm/core_dynamics/atl_rhs_alg_mod.x90 b/science/adjoint/source/algorithm/core_dynamics/atl_rhs_alg_mod.x90 index 16ecf9dc38..9fa0ba8163 100644 --- a/science/adjoint/source/algorithm/core_dynamics/atl_rhs_alg_mod.x90 +++ b/science/adjoint/source/algorithm/core_dynamics/atl_rhs_alg_mod.x90 @@ -73,10 +73,11 @@ contains !> @param[in] compute_eos Flag to control computation of the equation of !! state !> @param[in] dlayer_rhs Use damping layer in rhs yes/no + !> @param[in] stepper Enumerator for timestepper !> @param[in] model_clock Time in the model subroutine atl_rhs_alg(rhs, alpha_dt, base_state, state, moist_dyn, & ls_state, ls_moist_dyn, compute_eos, & - dlayer_rhs, model_clock) + dlayer_rhs, stepper, model_clock) implicit none @@ -91,6 +92,7 @@ subroutine atl_rhs_alg(rhs, alpha_dt, base_state, state, moist_dyn, & type(field_type), intent(in) :: ls_moist_dyn(num_moist_factors) type(field_type), intent(inout) :: rhs(bundle_size) logical(kind=l_def), intent(in) :: compute_eos + integer(kind=i_def), intent(in) :: stepper class(model_clock_type), intent(in) :: model_clock type(operator_type), pointer :: mm_vel, & @@ -142,7 +144,7 @@ subroutine atl_rhs_alg(rhs, alpha_dt, base_state, state, moist_dyn, & ! Using modified velocity mass matrix if using damping layer if (dlayer_rhs) then - mm_vel => get_w2_mass_matrix(w2_damping_layer_matrix, mesh%get_id(), model_clock) + mm_vel => get_w2_mass_matrix(w2_damping_layer_matrix, stepper, mesh%get_id(), model_clock) else mm_vel => get_mass_matrix_fe(W2, mesh%get_id()) end if diff --git a/science/adjoint/source/algorithm/solver/adj_mixed_operator_alg_mod.x90 b/science/adjoint/source/algorithm/solver/adj_mixed_operator_alg_mod.x90 index 8ecb9e6bc6..4599af4418 100644 --- a/science/adjoint/source/algorithm/solver/adj_mixed_operator_alg_mod.x90 +++ b/science/adjoint/source/algorithm/solver/adj_mixed_operator_alg_mod.x90 @@ -157,7 +157,7 @@ contains ! Obtain operators from runtime constants mt_lumped_inv => get_normalisation_r_solver( Wtheta, mesh%get_id() ) m2_diag => get_normalisation_r_solver( W2, mesh%get_id() ) - mm_vel => get_w2_mass_matrix_r_solver( w2_si_matrix, mesh%get_id() ) + mm_vel => get_w2_mass_matrix_r_solver( w2_si_matrix, self%stepper, mesh%get_id() ) ! Obtain operators from SI operators p2theta => get_p2theta() diff --git a/science/adjoint/source/algorithm/timestepping/atl_si_timestep_alg_mod.x90 b/science/adjoint/source/algorithm/timestepping/atl_si_timestep_alg_mod.x90 index b2519ae35d..d9934a98ce 100644 --- a/science/adjoint/source/algorithm/timestepping/atl_si_timestep_alg_mod.x90 +++ b/science/adjoint/source/algorithm/timestepping/atl_si_timestep_alg_mod.x90 @@ -19,7 +19,7 @@ module atl_si_timestep_alg_mod use_physics, use_wavedynamics use mixed_solver_config_mod, only: guess_np1, reference_reset_time use timestepping_config_mod, only: alpha, spinup_alpha, & - outer_iterations, inner_iterations + outer_iterations, inner_iterations_si use linear_config_mod, only: fixed_ls, & l_stabilise_bl, & n_bl_levels_to_stabilise, & @@ -110,7 +110,8 @@ module atl_si_timestep_alg_mod type(field_type), allocatable :: ls_rhs_phys(:,:,:) type(field_type), allocatable :: ls_mr_itns(:,:,:) type(field_type), allocatable :: ls_moist_dyn_itns(:,:,:) - integer( i_def ) :: ls_outer_iterations, ls_inner_iterations + integer( i_def ), allocatable :: ls_inner_iterations(:) + integer( i_def ) :: ls_outer_iterations ! theta_fv transport increment to change to weak form type(field_type) :: theta_fv_inc @@ -173,12 +174,14 @@ contains ! but ls_outer_iterations and ls_inner_iterations are set to ! 1 so that the arrays are defined. self%ls_outer_iterations = 1_i_def - self%ls_inner_iterations = 1_i_def + if (.not. allocated(self%ls_inner_iterations)) allocate(self%ls_inner_iterations(1)) + self%ls_inner_iterations(:) = 1_i_def else ! The linearisation state is evolved, and the intermediate iterations ! are used to calculate the evolution of the perturbation. self%ls_outer_iterations = outer_iterations - self%ls_inner_iterations = inner_iterations + if (.not. allocated(self%ls_inner_iterations)) allocate(self%ls_inner_iterations(outer_iterations)) + self%ls_inner_iterations(:) = inner_iterations_si(:) end if allocate(self%state(bundle_size)) @@ -209,13 +212,20 @@ contains allocate(self%ls_mr_n(nummr)) allocate(self%ls_mr_after_slow(nummr)) - allocate(self%ls_state_itns( bundle_size, self%ls_outer_iterations, self%ls_inner_iterations )) - allocate(self%ls_rhs_np1( bundle_size, self%ls_outer_iterations, self%ls_inner_iterations )) - allocate(self%ls_rhs_adv( bundle_size, self%ls_outer_iterations, self%ls_inner_iterations )) - allocate(self%ls_rhs_phys( bundle_size, self%ls_outer_iterations, self%ls_inner_iterations )) - allocate(self%ls_mr_inc( nummr, self%ls_outer_iterations, self%ls_inner_iterations )) - allocate(self%ls_mr_itns( nummr, self%ls_outer_iterations, self%ls_inner_iterations )) - allocate(self%ls_moist_dyn_itns( num_moist_factors, self%ls_outer_iterations, self%ls_inner_iterations )) + allocate(self%ls_state_itns( bundle_size, self%ls_outer_iterations, & + maxval(self%ls_inner_iterations) )) + allocate(self%ls_rhs_np1( bundle_size, self%ls_outer_iterations, & + maxval(self%ls_inner_iterations) )) + allocate(self%ls_rhs_adv( bundle_size, self%ls_outer_iterations, & + maxval(self%ls_inner_iterations) )) + allocate(self%ls_rhs_phys( bundle_size, self%ls_outer_iterations, & + maxval(self%ls_inner_iterations) )) + allocate(self%ls_mr_inc( nummr, self%ls_outer_iterations, & + maxval(self%ls_inner_iterations) )) + allocate(self%ls_mr_itns( nummr, self%ls_outer_iterations, & + maxval(self%ls_inner_iterations) )) + allocate(self%ls_moist_dyn_itns( num_moist_factors, & + self%ls_outer_iterations, maxval(self%ls_inner_iterations) )) ! Initialise internal state field objects @@ -251,7 +261,7 @@ contains call clone_bundle( self%state, self%ls_rhs_n, bundle_size ) do outer = 1, self%ls_outer_iterations - do inner = 1, self%ls_inner_iterations + do inner = 1, self%ls_inner_iterations(outer) call clone_bundle( self%state, self%ls_state_itns(:,outer,inner), bundle_size ) call clone_bundle( self%state, self%ls_rhs_np1(:,outer,inner), bundle_size ) call clone_bundle( self%state, self%ls_rhs_adv(:,outer,inner), bundle_size ) @@ -276,7 +286,7 @@ contains call clone_bundle( ls_mr, self%ls_mr_after_slow, nummr ) do outer = 1, self%ls_outer_iterations - do inner = 1, self%ls_inner_iterations + do inner = 1, self%ls_inner_iterations(outer) call clone_bundle( ls_moist_dyn, self%ls_moist_dyn_itns(:,outer,inner), num_moist_factors ) call clone_bundle( ls_mr, self%ls_mr_itns(:,outer,inner), nummr ) if (self%use_moisture) call clone_bundle( ls_mr, self%ls_mr_inc(:,outer,inner), nummr ) @@ -419,7 +429,8 @@ contains self%ls_state_after_slow, self%ls_state_n, & self%ls_moist_dyn_itns(:,1,1), & compute_eos=.false., compute_rhs_t_d=.true., & - dlayer_rhs=.false., model_clock=modeldb%clock ) + dlayer_rhs=.false., stepper=stepper_siqn, & + model_clock=modeldb%clock ) if (.not. si_momentum_equation) call mass_matrix_solver_alg( self%ls_advected_state(igh_u), self%ls_rhs_n(igh_u) ) @@ -439,6 +450,7 @@ contains self%ls_mr_itns(:,outer,1), & self%ls_mr_after_slow(:), & modeldb%clock, outer, & + stepper_siqn, & cheap_update=.false. ) ! Convert theta increment to weak form @@ -461,10 +473,10 @@ contains !----------------------------------------------------------------------- ! Start the inner (nonlinear, Coriolis) loop !----------------------------------------------------------------------- - ls_inner_dynamics_loop: do inner = 1,self%ls_inner_iterations + ls_inner_dynamics_loop: do inner = 1,self%ls_inner_iterations(outer) ! Calculate the counters for the next iteration - if (inner == inner_iterations) then + if (inner == inner_iterations_si(outer)) then next_inner = 1 next_outer = outer + 1 else @@ -474,7 +486,7 @@ contains ! Only intermediate iterations are required for the adjoint and not ! the final iteration so the final loop of the nonlinear isn't required - if (next_outer <= outer_iterations .and. next_inner <= inner_iterations) then + if (next_outer <= outer_iterations .and. next_inner <= inner_iterations_si(outer)) then ! Compute the time-level n + 1 dynamics terms call rhs_alg( modeldb%config, & @@ -486,6 +498,7 @@ contains compute_eos=.true., & compute_rhs_t_d=.true., & dlayer_rhs=dlayer_on, & + stepper=stepper_siqn, & model_clock=modeldb%clock ) ! Compute the LAM LBCs and RHS @@ -633,7 +646,7 @@ contains !------------------------------------------------------------------------- ! Start the inner (nonlinear, Coriolis) loop !------------------------------------------------------------------------- - inner_dynamics_loop: do inner = inner_iterations, 1, -1 + inner_dynamics_loop: do inner = inner_iterations_si(outer), 1, -1 if (fixed_ls) then ls_inner = 1 @@ -697,6 +710,7 @@ contains self%ls_moist_dyn_itns(:,ls_outer,ls_inner), & .true., & dlayer_on, & + stepper_siqn, & modeldb%clock ) call add_bundle( self%state, self%state2, self%state, bundle_size ) @@ -738,6 +752,7 @@ contains self%ls_moist_dyn_itns(:,ls_outer,1), & .true., & dlayer_on, & + stepper_siqn, & modeldb%clock ) call add_bundle( self%state, self%state2, self%state, bundle_size ) @@ -761,6 +776,7 @@ contains self%ls_mr_after_slow, & modeldb%clock, & outer, & + stepper_siqn, & self%adj_trans_lookup_cache ) ! advected_state finished with end do outer_dynamics_loop @@ -784,7 +800,8 @@ contains call atl_rhs_alg( self%rhs_n, varbeta * cast_dt, self%state_after_slow, & self%state_n, moist_dyn, self%ls_state_n, & - self%ls_moist_dyn_itns(:, 1,1), .false., .false., modeldb%clock ) + self%ls_moist_dyn_itns(:, 1,1), .false., .false., & + stepper_siqn, modeldb%clock ) call copy_bundle( self%state, self%state_copy, bundle_size ) call add_bundle( self%state_copy, self%state_after_slow, self%state, bundle_size ) @@ -862,6 +879,8 @@ contains if (allocated(self%ls_mr_itns)) deallocate(self%ls_mr_itns) if (allocated(self%ls_moist_dyn_itns)) deallocate(self%ls_moist_dyn_itns) + if (allocated(self%ls_inner_iterations)) deallocate(self%ls_inner_iterations) + call self%adj_solver_lookup_cache%finalise() call self%adj_trans_lookup_cache%finalise() diff --git a/science/adjoint/source/algorithm/transport/control/adj_transport_controller_mod.x90 b/science/adjoint/source/algorithm/transport/control/adj_transport_controller_mod.x90 index b5093ca082..5a096a38e3 100644 --- a/science/adjoint/source/algorithm/transport/control/adj_transport_controller_mod.x90 +++ b/science/adjoint/source/algorithm/transport/control/adj_transport_controller_mod.x90 @@ -10,6 +10,8 @@ module adj_transport_controller_mod use adj_copy_field_mod, only: adj_copy_field use adj_substep_transport_alg_mod, only: adj_substep_transport_alg use constants_mod, only: i_def, r_tran, l_def + use dycore_constants_mod, only: stepper_siqn, stepper_tr, & + stepper_bdf2, stepper_rk, gamma use field_mod, only: field_type use log_mod, only: log_event, & LOG_LEVEL_ERROR @@ -50,10 +52,12 @@ module adj_transport_controller_mod !> @brief Adjoint routine for initialisation of pert wind controller. !> @param[in,out] transport_controller The transport controller object !> @param[in] model_clock The model clock + !> @param[in] stepper Enumerator for timestepper !> @param[in,out] wind_n_rdef Wind at timestep n - !> @param[in,out] wind_np1_rdef Wind at timestep n plus 1 + !> @param[in,out] wind_np1_rdef Wind at timestep n plus 1 subroutine adj_pert_wind_ls_rho_initialiser( transport_controller, & model_clock, & + stepper, & wind_n_rdef, & wind_np1_rdef ) @@ -68,6 +72,7 @@ module adj_transport_controller_mod ! Arguments class(transport_controller_type), intent(inout) :: transport_controller class(model_clock_type), target, intent(in) :: model_clock + integer(kind=i_def), intent(in) :: stepper type(field_type), intent(inout) :: wind_n_rdef type(field_type), optional, intent(inout) :: wind_np1_rdef @@ -81,7 +86,21 @@ module adj_transport_controller_mod type(r_tran_field_type), pointer :: wind_n_rtran type(r_tran_field_type), pointer :: wind_np1_rtran - dt_rtran = real(model_clock%get_seconds_per_step(), r_tran) + ! Determine time increment for this step, based on the stepper + select case (stepper) + case (stepper_siqn, stepper_rk) + dt_rtran = real(model_clock%get_seconds_per_step(), r_tran) + case (stepper_tr) + dt_rtran = real(model_clock%get_seconds_per_step(), r_tran) + dt_rtran = 2.0_r_tran*real(gamma, r_tran)*dt_rtran + case (stepper_bdf2) + dt_rtran = real(model_clock%get_seconds_per_step(), r_tran) + dt_rtran = (1.0_r_tran - 2.0_r_tran*real(gamma, r_tran))*dt_rtran + case default + call log_event( "Unknown stepper passed to adj_pert_wind_ls_rho " // & + "transport controller initialise method", LOG_LEVEL_ERROR ) + end select + num_substeps = transport_controller%get_num_substeps() fixed_multiple_substeps = ( & diff --git a/science/adjoint/source/algorithm/transport/control/atl_transport_control_alg_mod.x90 b/science/adjoint/source/algorithm/transport/control/atl_transport_control_alg_mod.x90 index fe76e16cc3..3e9bd39d7c 100644 --- a/science/adjoint/source/algorithm/transport/control/atl_transport_control_alg_mod.x90 +++ b/science/adjoint/source/algorithm/transport/control/atl_transport_control_alg_mod.x90 @@ -57,13 +57,15 @@ contains !> @param[in] ls_mr_in PASSIVE Moisture fields before transport !> @param[in] model_clock Time within the model !> @param[in] outer Outer (advection) iteration number + !> @param[in] stepper Enumerator for timestepper !> @param[in] adj_lookup_table_cache Lookup table cache !> subroutine atl_transport_control_alg(config, advection_inc, advected_fields, & wind_np1, wind_n, mr_out, mr_in, & ls_advected_fields, & ls_wind_np1, ls_wind_n, ls_mr_in, & - model_clock, outer, adj_lookup_table_cache ) + model_clock, outer, stepper, & + adj_lookup_table_cache ) use derived_config_mod, only: bundle_size use sci_field_bundle_builtins_mod, only: clone_bundle, set_bundle_scalar @@ -98,6 +100,7 @@ contains type(field_type), intent(in) :: ls_mr_in(nummr) class(model_clock_type), intent(in) :: model_clock integer(kind=i_def), intent(in) :: outer + integer(kind=i_def), intent(in) :: stepper type(adj_trans_lookup_cache_type), intent(in) :: adj_lookup_table_cache ! Internal variables @@ -145,8 +148,9 @@ contains setval_c( zero_wind_n, 0.0_r_def ), & setval_c( zero_wind_np1, 0.0_r_def ) ) - call tl_transport_controller%initialise( config, & - model_clock, zero_adv_fields, ls_advected_fields(igh_d), & + call tl_transport_controller%initialise( & + config, model_clock, stepper, & + zero_adv_fields, ls_advected_fields(igh_d), & zero_wind_n, zero_wind_np1, ls_wind_n, ls_wind_np1, & outer=outer, cheap_update_step=cheap_update_step ) @@ -272,7 +276,7 @@ contains ref_field_rtran ) call atl_transport_controller_initialiser( tl_transport_controller, & - model_clock, & + model_clock, stepper, & advected_fields(igh_d), & wind_n, & wind_np1 ) diff --git a/science/adjoint/source/algorithm/transport/control/atl_transport_controller_mod.f90 b/science/adjoint/source/algorithm/transport/control/atl_transport_controller_mod.f90 index 3e20d2d7c8..8403e58bc4 100644 --- a/science/adjoint/source/algorithm/transport/control/atl_transport_controller_mod.f90 +++ b/science/adjoint/source/algorithm/transport/control/atl_transport_controller_mod.f90 @@ -8,6 +8,7 @@ module atl_transport_controller_mod use adj_transport_controller_mod, only: adj_ls_wind_pert_rho_initialiser, & adj_pert_wind_ls_rho_initialiser + use constants_mod, only: i_def use field_mod, only: field_type use model_clock_mod, only: model_clock_type use tl_transport_controller_mod, only: tl_transport_controller_type @@ -23,12 +24,14 @@ module atl_transport_controller_mod !> @brief Adjoint of the initialisation of the tl_transport_controller object !> @param[in,out] tl_transport_controller The tangent linear transport controller object !> @param[in] model_clock Tracks the time within the model + !> @param[in] stepper Enumerator for timestepper !> @param[in,out] ref_field_rdef Perturbed reference density to use for !! conservative transport of tracers. !> @param[in,out] wind_n_rdef Perturbed wind at n-th time level. !> @param[in,out] wind_np1_rdef Perturbed wind at (n+1)-th time level. subroutine atl_transport_controller_initialiser( tl_transport_controller, & model_clock, & + stepper, & ref_field_rdef, & wind_n_rdef, & wind_np1_rdef ) @@ -37,6 +40,7 @@ subroutine atl_transport_controller_initialiser( tl_transport_controller, & ! Arguments type(tl_transport_controller_type), intent(inout) :: tl_transport_controller type(model_clock_type), intent(in) :: model_clock + integer(kind=i_def), intent(in) :: stepper type(field_type), intent(inout) :: ref_field_rdef type(field_type), intent(inout) :: wind_n_rdef type(field_type), intent(inout) :: wind_np1_rdef @@ -50,6 +54,7 @@ subroutine atl_transport_controller_initialiser( tl_transport_controller, & call adj_pert_wind_ls_rho_initialiser( pert_wind_ls_rho_controller, & model_clock, & + stepper, & wind_n_rdef, & wind_np1_rdef ) call adj_ls_wind_pert_rho_initialiser( ls_wind_pert_rho_controller, & 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 e6c1fba8cf..d6febe1e1b 100644 --- a/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf +++ b/science/gungho/rose-meta/lfric-gungho/HEAD/rose-meta.conf @@ -6416,114 +6416,190 @@ type=real #============================================================================== [namelist:timestepping=alpha] compulsory=true -description=?????? -help=Time off-centering parameter +description=Off-centering parameter for the Semi-Implicit timestepping scheme +help=This controls the implicitness of the Semi-Implicit timestepping scheme. + =A value of 0.5 corresponds to a Crank-Nicolson scheme, while a value of 1.0 + =corresponds to the Backward Euler scheme. !kind=default -sort-key=Panel-A03 +range=0.0:1.0 +sort-key=Panel-A04 type=real [!namelist:timestepping=beta] compulsory=false expression=1.0_r_def - namelist:timestepping=alpha -help=?????? - =?????? +help=The explicitness parameter for the Semi-Implicit timestepping scheme. This + =is calculated as 1 - alpha, where alpha is the off-centering parameter. !kind=default type=real -[namelist:timestepping=inner_iterations] +[namelist:timestepping=inner_iterations_bdf2] +!bounds=namelist:timestepping=outer_iterations compulsory=true -description=?????? +description=Number of inner Newton iterations to perform in BDF stage of TR-BDF2 fail-if=this < 1 ; -help=Number of inner (Newton) iterations to do +help=The stages of the TR-BDF2 timestepping scheme involve two nested loops. The + =inner loop calls the implicit forcing terms and the linear solver, and the + =number of iterations can vary between outer loops. !kind=default +length=: range=1: -sort-key=Panel-A08 +sort-key=Panel-A03c +type=integer + +[namelist:timestepping=inner_iterations_si] +!bounds=namelist:timestepping=outer_iterations +compulsory=true +description=Number of inner Newton iterations to perform in Semi-Implicit scheme +fail-if=this < 1 ; +help=The Semi-Implicit timestepping scheme involves two nested loops. The inner + =loop calls the implicit forcing terms and the linear solver, and the number + =of iterations can vary between outer loops. +!kind=default +length=: +range=1: +sort-key=Panel-A03b +type=integer + +[namelist:timestepping=inner_iterations_tr] +!bounds=namelist:timestepping=outer_iterations +compulsory=true +description=Number of inner Newton iterations to perform in TR stage of TR-BDF2 +fail-if=this < 1 ; +help=The stages of the TR-BDF2 timestepping scheme involve two nested loops. The + =inner loop calls the implicit forcing terms and the linear solver, and the + =number of iterations can vary between outer loops. +!kind=default +length=: +range=1: +sort-key=Panel-A03b type=integer [namelist:timestepping=method] compulsory=true -description=Use either explicit Runge-Kutta timestepping or (iterative-) Semi-Implicit scheme +description=The overall timestepping algorithm to use !enumeration=true -help=Set the timestepping method for the GungHo dynamical core, either a fully explicit multi-stage - =Runge-Kutta method ('rk') or an (iterative-) Semi-Implicit scheme ('semi_implicit'). - =It is also possible to set 'no_timestepping'. There are no calls to dynamics or physics and the prognostic fields remain the same. However, time-varying fields such as ancillaries, and LBCs are still updated on each timestep, and diagnostics are output. - ='no_timestepping' is intended as a temporary switch required for basic-ral development. - =But it may also be useful for testing the tangent linear in 4DVar. +help=Set the timestepping method for the GungHo dynamical core, and its + =interaction with physics schemes. Options are: + =Runge-Kutta: an explicit multi-stage method. Note that this has + = not been implemented with physics schemes. + =Semi-Implicit: the Semi-Implicit Quasi-Newton timestepping scheme is + = designed as GungHo's default. It has evolved from ENDGame's + = timestepping scheme, which combines an off-centered + = Crank-Nicolson scheme with a Semi-Lagrangian treatment of + = transport (in GungHo transport can be an Eulerian or Semi- + = Lagrangian). + = The Semi-Implicit scheme consists of a nested outer loop + = (which includes transport and fast physics), and an inner + = loop which calls the linear solver. This scheme uses an off- + = centering parameter alpha to control the implicitness. + =TR-BDF2: a two-stage semi-implicit method, using a Quasi-Newton iterative + = structure like the Semi-Implicit scheme. The Trapezoidal (TR) and + = Backwards Difference (BDF2) stages each takes the form of a Semi- + = Implicit step. There is no off-centering parameter which improves + = accuracy. + = Slow physics happens before the TR stage, while fast physics + = happens during the BDF2 outer loop. + =JULES: a timestepping scheme which calls the standalone JULES surface + = scheme without coupling to a dynamical core. + =No-Timestepping: a null timestepping scheme, which does not call the + = dynamics or physics schemes. However, time-varying fields + = such as ancillaries, and LBCs are still updated on each + = timestep, and diagnostics are output. sort-key=Panel-A01 trigger=namelist:timestepping=tau_t: 'semi_implicit' ; =namelist:timestepping=tau_r: 'semi_implicit' ; + =namelist:timestepping=tau_u: 'semi_implicit' ; =semi-implicit: 'semi_implicit' ; - =namelist:timestepping=outer_iterations: 'semi_implicit', 'jules' ; - =namelist:timestepping=inner_iterations: 'semi_implicit' ; -value-titles=Semi-implicit, Runge-Kutta, No-timestepping, JULES -values='semi_implicit', 'rk', 'no_timestepping', 'jules' + =namelist:timestepping=outer_iterations: 'semi_implicit', 'jules', 'tr_bdf2'; + =namelist:timestepping=inner_iterations_si: 'semi_implicit' ; + =namelist:timestepping=inner_iterations_tr: 'tr_bdf2' ; + =namelist:timestepping=inner_iterations_bdf2: 'tr_bdf2' ; +value-titles=Semi-implicit, TR-BDF2, Runge-Kutta, No-timestepping, JULES +values='semi_implicit', 'tr_bdf2', 'rk', 'no_timestepping', 'jules' [namelist:timestepping=outer_iterations] compulsory=true -description=?????? +description=Number of outer iterations to perform in Quasi-Newton schemes. fail-if=this < 1 ; -help=Number of outer (advection) iterations to do +help=The Semi-Implicit and TR-BDF2 timestepping schemes involve two nested + =loops. The outer loop includes transport and fast physics terms. This + =parameter controls the number of iterations to perform for the outer loop. !kind=default range=1: -sort-key=Panel-A07 +sort-key=Panel-A03a type=integer [namelist:timestepping=runge_kutta_method] compulsory=true -description=????? +description=Choice of Runge-Kutta method !enumeration=true fail-if= -help=????? - =????? +help=Several Runge-Kutta methods are available for either timestepping the whole + =dynamical core, or for use with the Method-of-Lines transport scheme. + =The Strong Stability Preserving (SSP) methods are explicit multi-stage + =methods which combine the values over multiple stages as a weighted linear + =sum to provide stability. ns=namelist/Job/Timestepping/runge kutta -sort-key=Panel-A01 -value-titles=Foward Euler, - =Stong Stability Preserving: Order-2, - =Stong Stability Preserving: Order-3, - =Stong Stability Preserving: Order-4, - =Stong Stability Preserving: Order-5 +sort-key=Panel-A05 +value-titles=Forward Euler, + =Strong Stability Preserving: Order-2, + =Strong Stability Preserving: Order-3, + =Strong Stability Preserving: Order-4, + =Strong Stability Preserving: Order-5 values='forward_euler', 'ssp2', 'ssp3', 'ssp4', 'ssp5' [namelist:timestepping=spinup_alpha] compulsory=true -description=?????? -help=If true then spin up alpha over the spin up period +description=Whether to enforce alpha=1 during the spin up period +help=If true the Semi-Implicit off-centering parameter is set to 1 (fully + =implicit) during the spin up period, to improve stability. !kind=default -sort-key=Panel-A11 +sort-key=Panel-A07 trigger=namelist:timestepping=spinup_period: .true.; type=logical [namelist:timestepping=tau_r] compulsory=true -description=?????? +description=Relaxation parameter for density in the Semi-Implicit scheme fail-if=this < 0.0 -help=Relaxation parameter for density in semi-implicit method +help=The relaxation parameters give the implicitness weighting of semi-implicit + =terms (e.g. transport or forcing terms) in the left-hand side of the linear + =solver. When using more than one Quasi-Newton inner iteration, it is + =recommended to set tau_r=1, as the density transport term is not updated + =between inner iterations. !kind=default ns=namelist/Job/Timestepping/semi-implicit range=0.0: -sort-key=Panel-A06 +sort-key=Panel-A06b type=real [namelist:timestepping=tau_t] compulsory=true -description=?????? +description=Relaxation parameter for potential temperature in the Semi-Implicit scheme fail-if=this < 0.0 -help=Relaxation parameter for potential temperature in semi-implicit method +help=The relaxation parameters give the implicitness weighting of semi-implicit + =terms (e.g. transport or forcing terms) in the left-hand side of the linear + =solver. When using more than one Quasi-Newton inner iteration, it is + =recommended to set tau_t=1, as the potential temperature transport term is + =not updated between inner iterations. !kind=default ns=namelist/Job/Timestepping/semi-implicit range=0.0: -sort-key=Panel-A05 +sort-key=Panel-A06c type=real [namelist:timestepping=tau_u] compulsory=true -description=?????? +description=Relaxation parameter for velocity in the Semi-Implicit scheme fail-if=this < 0.0 -help=Relaxation parameter for velocity in semi-implicit method +help=The relaxation parameters give the implicitness weighting of semi-implicit + =terms (e.g. transport or forcing terms) in the left-hand side of the linear + =solver. It is recommended to set tau_u=alpha, the off-centering parameter. !kind=default ns=namelist/Job/Timestepping/semi-implicit range=0.0: -sort-key=Panel-A04 +sort-key=Panel-A06a type=real #============================================================================== diff --git a/science/gungho/rose-meta/lfric-gungho/versions.py b/science/gungho/rose-meta/lfric-gungho/versions.py index d56bbea4d2..7f02187d5e 100644 --- a/science/gungho/rose-meta/lfric-gungho/versions.py +++ b/science/gungho/rose-meta/lfric-gungho/versions.py @@ -529,3 +529,46 @@ def upgrade(self, config, meta_config=None): self.add_setting(config, [nml, var], val) return config, self.reports + +class vn32_t714(MacroUpgrade): + """Upgrade macro for ticket #714 by Thomas Bendall.""" + + BEFORE_TAG = "vn3.2_t776" + AFTER_TAG = "vn3.2_t714" + + def upgrade(self, config, meta_config=None): + # Commands From: rose-meta/lfric-gungho + # Semi-Implicit setting changes ---------------------------------------- + # Get number of outer/inner iterations from the existing namelist + outer = self.get_setting_value( + config, ["namelist:timestepping", "outer_iterations"] + ) + inner = self.get_setting_value( + config, ["namelist:timestepping", "inner_iterations"] + ) + # Determine inner iterations settings + if outer in [None, "''", ""]: + inner_array = "''" + else: + inner_array = f"{inner}" + for _ in range(int(outer) - 1): + inner_array += f",{inner}" + self.add_setting( + config, + ["namelist:timestepping", "inner_iterations_si"], + inner_array, + ) + # Remove old setting + self.remove_setting( + config, ["namelist:timestepping", "inner_iterations"] + ) + # TR-BDF2 settings ----------------------------------------------------- + # This is easier because it's new and we can prescribe what to do + self.add_setting( + config, ["namelist:timestepping", "inner_iterations_tr"], "2,1" + ) + self.add_setting( + config, ["namelist:timestepping", "inner_iterations_bdf2"], "1,1" + ) + + return config, self.reports diff --git a/science/gungho/source/algorithm/core_dynamics/rhs_alg_mod.x90 b/science/gungho/source/algorithm/core_dynamics/rhs_alg_mod.x90 index 5cfe728530..bee1552788 100644 --- a/science/gungho/source/algorithm/core_dynamics/rhs_alg_mod.x90 +++ b/science/gungho/source/algorithm/core_dynamics/rhs_alg_mod.x90 @@ -93,9 +93,11 @@ contains !> @param[in] dlayer_rhs Use damping layer in rhs yes/no !> @param[in] compute_rhs_t_d Compute rhs for the potential temperature !! and density + !> @param[in] stepper Enumerator for timestepper !> @param[in] model_clock Time in the model subroutine rhs_alg( config, rhs, alpha_dt, base_state, state, moist_dyn, & - compute_eos, compute_rhs_t_d, dlayer_rhs, model_clock ) + compute_eos, compute_rhs_t_d, dlayer_rhs, stepper, & + model_clock ) implicit none @@ -109,6 +111,7 @@ contains type(field_type), intent(inout) :: rhs(bundle_size) logical(kind=l_def), intent(in) :: compute_eos logical(kind=l_def), intent(in) :: compute_rhs_t_d + integer(kind=i_def), intent(in) :: stepper class(model_clock_type), intent(in) :: model_clock type(config_type), intent(in) :: config @@ -116,19 +119,22 @@ contains .and. (rhs(igh_u)%get_element_order_v() == 0) & .and. (.not. si_momentum_equation) & .and. (eos_method == eos_method_sampled) ) then - call rhs_default_alg( config, rhs, alpha_dt, base_state, state, moist_dyn, & - compute_eos, compute_rhs_t_d, dlayer_rhs, model_clock ) + call rhs_default_alg( config, rhs, alpha_dt, base_state, state, & + moist_dyn, compute_eos, compute_rhs_t_d, & + dlayer_rhs, stepper, model_clock ) else - call rhs_general_alg( config, rhs, alpha_dt, base_state, state, moist_dyn, & - compute_eos, compute_rhs_t_d, dlayer_rhs, model_clock ) + call rhs_general_alg( config, rhs, alpha_dt, base_state, state, & + moist_dyn, compute_eos, compute_rhs_t_d, & + dlayer_rhs, stepper, model_clock ) end if end subroutine rhs_alg !> @brief rhs algorithm for the default setup ( lowest order elements, !! explicit momentum advection, global and sampled eos) - subroutine rhs_default_alg( config, rhs, alpha_dt, base_state, state, moist_dyn, & - compute_eos, compute_rhs_t_d, dlayer_rhs, model_clock ) + subroutine rhs_default_alg( config, rhs, alpha_dt, base_state, state, & + moist_dyn, compute_eos, compute_rhs_t_d, & + dlayer_rhs, stepper, model_clock ) implicit none @@ -142,6 +148,7 @@ contains type(field_type), intent(inout) :: rhs(bundle_size) logical(kind=l_def), intent(in) :: compute_eos logical(kind=l_def), intent(in) :: compute_rhs_t_d + integer(kind=i_def), intent(in) :: stepper class(model_clock_type), intent(in) :: model_clock type(config_type), intent(in) :: config @@ -179,7 +186,7 @@ contains ! Using modified velocity mass matrix if needing damping layer if (dlayer_rhs) then - mm_vel => get_w2_mass_matrix(w2_damping_layer_matrix, mesh_id, model_clock) + mm_vel => get_w2_mass_matrix(w2_damping_layer_matrix, stepper, mesh_id, model_clock) else mm_vel => get_mass_matrix_fv(W2, mesh_id) end if @@ -245,8 +252,9 @@ contains end subroutine rhs_default_alg !> @brief rhs algorithm for any generic setup - subroutine rhs_general_alg( config, rhs, alpha_dt, base_state, state, moist_dyn, & - compute_eos, compute_rhs_t_d, dlayer_rhs, model_clock ) + subroutine rhs_general_alg( config, rhs, alpha_dt, base_state, state, & + moist_dyn, compute_eos, compute_rhs_t_d, & + dlayer_rhs, stepper, model_clock ) implicit none @@ -260,6 +268,7 @@ contains type(field_type), intent(inout) :: rhs(bundle_size) logical(kind=l_def), intent(in) :: compute_eos logical(kind=l_def), intent(in) :: compute_rhs_t_d + integer(kind=i_def), intent(in) :: stepper class(model_clock_type), intent(in) :: model_clock type(config_type), intent(in) :: config @@ -312,7 +321,7 @@ contains ! Using modified velocity mass matrix if needing damping layer if (dlayer_rhs) then - mm_vel => get_w2_mass_matrix(w2_damping_layer_matrix, mesh_id, model_clock) + mm_vel => get_w2_mass_matrix(w2_damping_layer_matrix, stepper, mesh_id, model_clock) else mm_vel => get_mass_matrix_fe(w2, mesh%get_id()) end if diff --git a/science/gungho/source/algorithm/diagnostics/si_diagnostics_mod.x90 b/science/gungho/source/algorithm/diagnostics/si_diagnostics_mod.x90 index 7e865b2bba..1653ec01b4 100644 --- a/science/gungho/source/algorithm/diagnostics/si_diagnostics_mod.x90 +++ b/science/gungho/source/algorithm/diagnostics/si_diagnostics_mod.x90 @@ -157,6 +157,11 @@ contains call dtheta_adv%write_field() end if + ! NB: these increments are the difference between the state after slow + ! physics and before the *last* fast physics. They include advection, but + ! also increments from forcing terms (Coriolis, pressure gradient) and + ! the evolution of the model through previous outer loops. + ! For TR-BDF2, all of the TR step is included in these increments du_adv_flag = init_diag(du_adv, 'advection__du_adv') dv_adv_flag = init_diag(dv_adv, 'advection__dv_adv') dw_adv_flag = init_diag(dw_adv, 'advection__dw_adv') @@ -198,6 +203,8 @@ contains ! Solver increments ! Difference between solver value (X-dX_cld) and X_star+dX_fast_physics + ! NB: these increments only include the increment from the *final* call + ! to the solver if (init_diag(dtheta_solv, 'solver__dtheta_solv')) then call derived_fields%get_field('theta_star', theta_star) call invoke(X_minus_Y(dtheta_solv,state(igh_t),theta_star), & diff --git a/science/gungho/source/algorithm/runtime_constants/dycore_constants_mod.x90 b/science/gungho/source/algorithm/runtime_constants/dycore_constants_mod.x90 index 59b518bbd9..4bc0e370d7 100644 --- a/science/gungho/source/algorithm/runtime_constants/dycore_constants_mod.x90 +++ b/science/gungho/source/algorithm/runtime_constants/dycore_constants_mod.x90 @@ -57,9 +57,15 @@ module dycore_constants_mod type(inventory_by_mesh_type) :: geopotential_inventory type(inventory_by_mesh_type) :: coriolis_inventory type(inventory_by_mesh_type) :: vert_coriolis_inventory - type(inventory_by_mesh_type), target :: mm_w2_dl_inventory - type(inventory_by_mesh_type), target :: mm_w2_si_inventory - type(inventory_by_mesh_type), target :: mm_w2_dl_lagged_inventory + type(inventory_by_mesh_type), target :: mm_w2_dl_siqn_inventory + type(inventory_by_mesh_type), target :: mm_w2_si_siqn_inventory + type(inventory_by_mesh_type), target :: mm_w2_dl_lagged_siqn_inventory + type(inventory_by_mesh_type), target :: mm_w2_dl_tr_inventory + type(inventory_by_mesh_type), target :: mm_w2_si_tr_inventory + type(inventory_by_mesh_type), target :: mm_w2_dl_lagged_tr_inventory + type(inventory_by_mesh_type), target :: mm_w2_dl_bdf2_inventory + type(inventory_by_mesh_type), target :: mm_w2_si_bdf2_inventory + type(inventory_by_mesh_type), target :: mm_w2_dl_lagged_bdf2_inventory ! ========================================================================== ! ! Public enumerated types for selecting W2 mass matrices @@ -70,7 +76,18 @@ module dycore_constants_mod integer(kind=i_def), parameter, public :: w2_si_matrix = 891 ! Timestepping enumerators - integer(kind=i_def), parameter, public :: stepper_siqn = 222 + integer(kind=i_def), parameter, public :: stepper_siqn = 2 + integer(kind=i_def), parameter, public :: stepper_tr = 3 + integer(kind=i_def), parameter, public :: stepper_bdf2 = 4 + integer(kind=i_def), parameter, public :: stepper_rk = 1 + + ! TR-BDF2 gamma values + ! gamma = 1 - 0.5*sqrt(2) + real(kind=r_def), parameter, public :: gamma = 0.2928932188134524_r_def + real(kind=r_def), parameter, public :: gamma2 = & + 0.5_r_def*(1.0_r_def - 2.0_r_def*gamma)/(1.0_r_def - gamma) + real(kind=r_def), parameter, public :: gamma3 = & + 0.5_r_def*(1.0_r_def - gamma)/gamma ! ========================================================================== ! ! Public functions for accessing the module contents @@ -256,13 +273,16 @@ contains !> @brief Returns a pointer to a generalised W2 mass matrix operator !> @param[in] mesh_id The ID of the mesh to get the object for + !> @param[in] w2_op_name The enumerated name of the W2 mass matrix operator + !> @param[in] stepper_name The enumerator of the time stepper being used !> @param[in] model_clock The model clock object !! @TODO #490: !! NB: This is currently an optional argument as it is not possible !! to pass it from all parts of the model (in particular from the !! apply method in the mixed operator) !> @return A generalised W2 mass matrix operator for the dynamical core - function get_w2_mass_matrix(w2_op_name, mesh_id, model_clock) result(dynamics_mm_w2) + function get_w2_mass_matrix(w2_op_name, stepper_name, mesh_id, model_clock) & + result(dynamics_mm_w2) use compute_dl_matrix_kernel_mod, only: compute_dl_matrix_kernel_type use damping_layer_config_mod, only: dl_base, dl_str @@ -280,12 +300,16 @@ contains implicit none integer(kind=i_def), intent(in) :: w2_op_name + integer(kind=i_def), intent(in) :: stepper_name integer(kind=i_def), intent(in) :: mesh_id class(model_clock_type), optional, intent(in) :: model_clock type(mesh_type), pointer :: mesh logical(kind=l_def) :: constant_exists type(function_space_type), pointer :: fs type(inventory_by_mesh_type), pointer :: inventory + type(inventory_by_mesh_type), pointer :: dl_inventory + type(inventory_by_mesh_type), pointer :: dl_lagged_inventory + type(inventory_by_mesh_type), pointer :: si_inventory type(operator_type), pointer :: dynamics_mm_w2 type(operator_type), target :: mm_w2_tmp type(operator_type), target :: mm_w2_tmp2 @@ -298,19 +322,38 @@ contains type(quadrature_xyoz_type), pointer :: qr real(kind=r_def) :: const real(kind=r_second) :: dt + real(kind=r_second) :: dt_to_use type(operator_type), pointer :: coriolis integer(tik) :: id ! Check inventories are initialised - if (.not. mm_w2_si_inventory%is_initialised()) then - ! Initialise all inventories together - call mm_w2_si_inventory%initialise(name='mm_w2_si') - call mm_w2_dl_inventory%initialise(name='mm_w2_dl') - call mm_w2_dl_lagged_inventory%initialise(name='mm_w2_dl_lagged') - if (present(model_clock)) then - dt_stored = model_clock%get_seconds_per_step() + select case (stepper_name) + case (stepper_siqn) + if (.not. mm_w2_si_siqn_inventory%is_initialised()) then + ! Initialise all inventories together + call mm_w2_si_siqn_inventory%initialise(name='mm_w2_si') + call mm_w2_dl_siqn_inventory%initialise(name='mm_w2_dl') + call mm_w2_dl_lagged_siqn_inventory%initialise(name='mm_w2_dl_lagged') + if (present(model_clock)) then + dt_stored = model_clock%get_seconds_per_step() + end if end if - end if + + case (stepper_tr, stepper_bdf2) + if (.not. mm_w2_si_tr_inventory%is_initialised()) then + ! Initialise all inventories together + call mm_w2_si_tr_inventory%initialise(name='mm_w2_si_tr') + call mm_w2_dl_tr_inventory%initialise(name='mm_w2_dl_tr') + call mm_w2_dl_lagged_tr_inventory%initialise(name='mm_w2_dl_lagged_tr') + call mm_w2_si_bdf2_inventory%initialise(name='mm_w2_si_bdf2') + call mm_w2_dl_bdf2_inventory%initialise(name='mm_w2_dl_bdf2') + call mm_w2_dl_lagged_bdf2_inventory%initialise(name='mm_w2_dl_lagged_bdf2') + if (present(model_clock)) then + dt_stored = model_clock%get_seconds_per_step() + end if + end if + + end select ! Check this constant is still valid -- is the model time step the same? ! Can only do this if model_clock is present @@ -326,16 +369,78 @@ contains end if ! Determine which inventory to point to + select case (stepper_name) + case (stepper_siqn) + dl_inventory => mm_w2_dl_siqn_inventory + dl_lagged_inventory => mm_w2_dl_lagged_siqn_inventory + si_inventory => mm_w2_si_siqn_inventory + case (stepper_tr) + dl_inventory => mm_w2_dl_tr_inventory + dl_lagged_inventory => mm_w2_dl_lagged_tr_inventory + si_inventory => mm_w2_si_tr_inventory + case (stepper_bdf2) + dl_inventory => mm_w2_dl_bdf2_inventory + dl_lagged_inventory => mm_w2_dl_lagged_bdf2_inventory + si_inventory => mm_w2_si_bdf2_inventory + case default + call log_event( & + 'Dynamics W2 mass matrix not implemented for this stepper', & + LOG_LEVEL_ERROR & + ) + end select + select case (w2_op_name) - case(w2_damping_layer_matrix) - inventory => mm_w2_dl_inventory - case(w2_lagged_damping_layer_matrix) - ! @TODO #416: this should be an inventory of r_solver operators - ! as the r_def operators are not used anywhere - inventory => mm_w2_dl_lagged_inventory - case(w2_si_matrix) - inventory => mm_w2_si_inventory + case (w2_damping_layer_matrix) + select case (stepper_name) + case (stepper_siqn) + inventory => mm_w2_dl_siqn_inventory + case (stepper_tr) + inventory => mm_w2_dl_tr_inventory + case (stepper_bdf2) + inventory => mm_w2_dl_bdf2_inventory case default + dynamics_mm_w2 => null() + call log_event( & + 'Dynamics W2 mass matrix does not exist', LOG_LEVEL_ERROR & + ) + stop + end select + + case (w2_lagged_damping_layer_matrix) + ! @TODO #416: this should be an inventory of r_solver operators + ! as the r_def operators are not used anywhere + select case (stepper_name) + case (stepper_siqn) + inventory => mm_w2_dl_lagged_siqn_inventory + case (stepper_tr) + inventory => mm_w2_dl_lagged_tr_inventory + case (stepper_bdf2) + inventory => mm_w2_dl_lagged_bdf2_inventory + case default + dynamics_mm_w2 => null() + call log_event( & + 'Dynamics W2 mass matrix does not exist', LOG_LEVEL_ERROR & + ) + stop + end select + + case (w2_si_matrix) + select case (stepper_name) + case (stepper_siqn) + inventory => mm_w2_si_siqn_inventory + case (stepper_tr) + inventory => mm_w2_si_tr_inventory + case (stepper_bdf2) + inventory => mm_w2_si_bdf2_inventory + case default + dynamics_mm_w2 => null() + call log_event( & + 'Dynamics W2 mass matrix does not exist', LOG_LEVEL_ERROR & + ) + stop + end select + + case default dynamics_mm_w2 => null() call log_event('Dynamics W2 mass matrix does not exist', LOG_LEVEL_ERROR) stop @@ -349,6 +454,13 @@ contains fs => function_space_collection%get_fs(mesh, element_order_h, & element_order_v, W2) + select case (stepper_name) + case (stepper_siqn, stepper_bdf2) + dt_to_use = dt_stored + case (stepper_tr) + dt_to_use = dt_stored * 2.0_r_def * gamma + end select + ! Each matrix is built on a previous matrix. Generally either all three ! matrices are needed, or only the S.I. matrix (the final in the chain) ! Therefore if this is a call to get the S.I. matrix and the others do not @@ -357,14 +469,14 @@ contains ! ====================================================================== ! ! DAMPING LAYER MATRIX ! ====================================================================== ! - if (mm_w2_dl_inventory%paired_object_exists(mesh_id)) then + if (dl_inventory%paired_object_exists(mesh_id)) then ! Get existing matrix - call mm_w2_dl_inventory%get_operator(mesh, mm_w2_dl) + call dl_inventory%get_operator(mesh, mm_w2_dl) else ! We are going to need to create this matrix if (w2_op_name == w2_damping_layer_matrix) then ! This is our target matrix, so store it - call mm_w2_dl_inventory%add_operator(mm_w2_dl, fs, fs, mesh) + call dl_inventory%add_operator(mm_w2_dl, fs, fs, mesh) else ! Assume that this matrix is only used to create another, so ! make a temporary matrix @@ -386,7 +498,7 @@ contains planet_radius, & element_order_h, & element_order_v, & - dt_stored, & + dt_to_use, & qr) ) if ( LPROF ) call stop_timing( id, 'runtime_constants.dycore' ) else @@ -401,16 +513,16 @@ contains ! ====================================================================== ! ! @TODO: logic here is probably not optimal if (w2_op_name /= w2_damping_layer_matrix) then - if (mm_w2_dl_lagged_inventory%paired_object_exists(mesh_id)) then + if (dl_lagged_inventory%paired_object_exists(mesh_id)) then ! Get existing matrix - call mm_w2_dl_lagged_inventory%get_operator(mesh, mm_w2_dl_lagged) + call dl_lagged_inventory%get_operator(mesh, mm_w2_dl_lagged) else ! We are going to need to create this matrix if ( LPROF ) call start_timing( id, 'runtime_constants.dycore' ) if (w2_op_name == w2_lagged_damping_layer_matrix) then ! This is our target matrix, so store it - call mm_w2_dl_lagged_inventory%add_operator(mm_w2_dl_lagged, fs, fs, mesh) + call dl_lagged_inventory%add_operator(mm_w2_dl_lagged, fs, fs, mesh) else ! Assume that this matrix is only used to create another, so ! make a temporary matrix @@ -439,10 +551,19 @@ contains ! ====================================================================== ! if (w2_op_name == w2_si_matrix) then if ( LPROF ) call start_timing( id, 'runtime_constants.dycore' ) - call mm_w2_si_inventory%add_operator(mm_w2_si, fs, fs, mesh) + call si_inventory%add_operator(mm_w2_si, fs, fs, mesh) if ( rotating ) then - const = - tau_u * real(dt_stored, r_def) + select case (stepper_name) + case (stepper_siqn) + const = - tau_u * real(dt_stored, r_def) + case (stepper_tr) + ! tau_u = gamma + const = - gamma * real(dt_stored, r_def) + case (stepper_bdf2) + ! tau_u = gamma2 + const = - gamma2 * real(dt_stored, r_def) + end select else const = 0.0_r_def end if @@ -476,9 +597,15 @@ contains call geopotential_inventory%clear() call vert_coriolis_inventory%clear() call coriolis_inventory%clear() - call mm_w2_dl_inventory%clear() - call mm_w2_dl_lagged_inventory%clear() - call mm_w2_si_inventory%clear() + call mm_w2_dl_siqn_inventory%clear() + call mm_w2_si_siqn_inventory%clear() + call mm_w2_dl_lagged_siqn_inventory%clear() + call mm_w2_dl_tr_inventory%clear() + call mm_w2_si_tr_inventory%clear() + call mm_w2_dl_lagged_tr_inventory%clear() + call mm_w2_dl_bdf2_inventory%clear() + call mm_w2_si_bdf2_inventory%clear() + call mm_w2_dl_lagged_bdf2_inventory%clear() dt_stored = 0.0_r_second end subroutine final_dycore_constants diff --git a/science/gungho/source/algorithm/runtime_constants/solver_constants_mod.x90 b/science/gungho/source/algorithm/runtime_constants/solver_constants_mod.x90 index dd40d13cb1..b15a514aa5 100644 --- a/science/gungho/source/algorithm/runtime_constants/solver_constants_mod.x90 +++ b/science/gungho/source/algorithm/runtime_constants/solver_constants_mod.x90 @@ -63,7 +63,9 @@ module solver_constants_mod type(inventory_by_mesh_type), target :: im3_div_r_solver_inventory ! Special dynamical core r_solver matrices - type(inventory_by_mesh_type), target :: mm_w2_si_r_solver_inventory + type(inventory_by_mesh_type), target :: mm_w2_si_siqn_r_solver_inventory + type(inventory_by_mesh_type), target :: mm_w2_si_tr_r_solver_inventory + type(inventory_by_mesh_type), target :: mm_w2_si_bdf2_r_solver_inventory ! Normalisations type(inventory_by_mesh_type) :: w2_normalisation_inventory @@ -271,14 +273,19 @@ contains !> @brief Returns a pointer to a generalised W2 mass matrix operator !> @param[in] mesh_id The ID of the mesh to get the object for + !> @param[in] w2_op_name The type of W2 mass matrix operator to get + !> @param[in] stepper_name The time stepper being used !> @return The semi-implicit W2 mass matrix operator - function get_w2_mass_matrix_r_solver(w2_op_name, mesh_id) result(mm_r_solver) + function get_w2_mass_matrix_r_solver(w2_op_name, stepper_name, mesh_id) & + result(mm_r_solver) - use dycore_constants_mod, only: get_w2_mass_matrix, w2_si_matrix + use dycore_constants_mod, only: get_w2_mass_matrix, w2_si_matrix, & + stepper_siqn, stepper_tr, stepper_bdf2 implicit none integer(kind=i_def), intent(in) :: w2_op_name + integer(kind=i_def), intent(in) :: stepper_name integer(kind=i_def), intent(in) :: mesh_id type(mesh_type), pointer :: mesh logical(kind=l_def) :: constant_exists @@ -286,21 +293,43 @@ contains type(operator_type), pointer :: mm_r_def type(r_solver_operator_type), pointer :: mm_r_solver type(inventory_by_mesh_type), pointer :: inventory + character(len=str_def) :: name integer(tik) :: id ! Determine which inventory to point to select case (w2_op_name) - case(w2_si_matrix) - inventory => mm_w2_si_r_solver_inventory + case (w2_si_matrix) + select case (stepper_name) + case (stepper_siqn) + name = 'mm_w2_si_siqn_r_solver' + inventory => mm_w2_si_siqn_r_solver_inventory + case (stepper_tr) + name = 'mm_w2_si_tr_r_solver' + inventory => mm_w2_si_tr_r_solver_inventory + case (stepper_bdf2) + name = 'mm_w2_si_bdf2_r_solver' + inventory => mm_w2_si_bdf2_r_solver_inventory case default mm_r_solver => null() - call log_event('r_solver dynamics W2 mass matrix does not exist', LOG_LEVEL_ERROR) + call log_event( & + 'r_solver dynamics W2 mass matrix does not exist for this stepper', & + LOG_LEVEL_ERROR & + ) stop + end select + + case default + mm_r_solver => null() + call log_event( & + 'r_solver dynamics W2 mass matrix does not exist for this W2 ' // & + 'enumerator', LOG_LEVEL_ERROR & + ) + stop end select ! Initialise inventory if this is the first time getting this constant - if (.not. mm_w2_si_r_solver_inventory%is_initialised()) then - call mm_w2_si_r_solver_inventory%initialise(name='mm_w2_si_r_solver') + if (.not. inventory%is_initialised()) then + call inventory%initialise(name=name) end if mesh => mesh_collection%get_mesh(mesh_id) @@ -308,7 +337,7 @@ contains if (.not. constant_exists) then ! Create constant if it doesn't already exist - mm_r_def => get_w2_mass_matrix(w2_op_name, mesh_id) + mm_r_def => get_w2_mass_matrix(w2_op_name, stepper_name, mesh_id) if ( LPROF ) call start_timing( id, 'runtime_constants.solver' ) @@ -532,7 +561,9 @@ contains call w2_normalisation_inventory%clear() call mm_w2_r_solver_inventory%clear() - call mm_w2_si_r_solver_inventory%clear() + call mm_w2_si_siqn_r_solver_inventory%clear() + call mm_w2_si_tr_r_solver_inventory%clear() + call mm_w2_si_bdf2_r_solver_inventory%clear() call mm_wtheta_r_solver_inventory%clear() call mm_w3_inv_r_solver_inventory%clear() call detj_at_w3_r_solver_inventory%clear() diff --git a/science/gungho/source/algorithm/solver/lam_rhs_alg_mod.x90 b/science/gungho/source/algorithm/solver/lam_rhs_alg_mod.x90 index 0b3ae266d9..4d9e28cc9d 100644 --- a/science/gungho/source/algorithm/solver/lam_rhs_alg_mod.x90 +++ b/science/gungho/source/algorithm/solver/lam_rhs_alg_mod.x90 @@ -19,8 +19,12 @@ module lam_rhs_alg_mod use derived_config_mod, only: bundle_size use si_operators_alg_mod, only: get_rho_at_u use sci_fem_constants_mod, only: get_im3_div_fe - use dycore_constants_mod, only: get_w2_mass_matrix, & - w2_si_matrix + use dycore_constants_mod, only: get_w2_mass_matrix, & + w2_si_matrix, & + stepper_siqn, & + stepper_tr, & + stepper_bdf2, & + gamma use limited_area_constants_mod, only: get_mask_fe use fs_continuity_mod, only: W2, W3, Wtheta use field_mod, only: field_type @@ -87,8 +91,9 @@ contains !> @param[in] model_clock Time in the model !> @param[in] finest_mesh_name Name of the finest mesh !> @param[in] tau_r Relaxation parameter for density in semi-implicit method + !> @param[in] stepper Enumerator for timestepper being used subroutine calc_rhs_lbc( rhs, lbc_fields, model_clock, finest_mesh_name, & - tau_r ) + tau_r, stepper ) implicit none @@ -97,6 +102,7 @@ contains class(model_clock_type), intent(in) :: model_clock character(len=str_def), intent(in) :: finest_mesh_name real(r_def), intent(in) :: tau_r + integer(i_def), intent(in) :: stepper type(field_type), pointer :: boundary_u => null() type(operator_type), pointer :: mm_vel => null(), & @@ -121,9 +127,16 @@ contains if ( LPROF ) call start_timing( id, 'dynamics.rhs_lbc' ) mesh_id = rhs(igh_u)%get_mesh_id() - dt = real(model_clock%get_seconds_per_step(), r_def) - tau_r_dt = tau_r*dt + + select case (stepper) + case (stepper_siqn) + tau_r_dt = tau_r*dt + case (stepper_tr) + tau_r_dt = 2.0_r_def*gamma*dt + case (stepper_bdf2) + tau_r_dt = dt + end select ! Get the LBC data call lbc_fields%get_field('boundary_u_diff', boundary_u) @@ -137,7 +150,7 @@ contains div => get_im3_div_fe(mesh_id) ! m2 (or dl) + tau_u*dt*coriolis (lagged if required) - mm_vel => get_w2_mass_matrix(w2_si_matrix, mesh_id, model_clock) + mm_vel => get_w2_mass_matrix(w2_si_matrix, stepper, mesh_id, model_clock) ! Obtain operators from SI operators r_solver_rho_at_u => get_rho_at_u() diff --git a/science/gungho/source/algorithm/solver/mixed_operator_alg_mod.x90 b/science/gungho/source/algorithm/solver/mixed_operator_alg_mod.x90 index a022c1c623..e6d571d634 100644 --- a/science/gungho/source/algorithm/solver/mixed_operator_alg_mod.x90 +++ b/science/gungho/source/algorithm/solver/mixed_operator_alg_mod.x90 @@ -229,7 +229,7 @@ contains mt_lumped_inv => get_normalisation_r_solver(Wtheta, mesh%get_id()) m2_diag => get_normalisation_r_solver(W2, mesh%get_id()) ! mm_vel = m2 (or dl) + tau_u*dt*coriolis - mm_vel => get_w2_mass_matrix_r_solver(w2_si_matrix, mesh%get_id()) + mm_vel => get_w2_mass_matrix_r_solver(w2_si_matrix, self%stepper, mesh%get_id()) ! Obtain operators from SI operators p2theta => get_p2theta() diff --git a/science/gungho/source/algorithm/solver/semi_implicit_solver_alg_mod.x90 b/science/gungho/source/algorithm/solver/semi_implicit_solver_alg_mod.x90 index 86d8dbda18..f2f189b4fc 100644 --- a/science/gungho/source/algorithm/solver/semi_implicit_solver_alg_mod.x90 +++ b/science/gungho/source/algorithm/solver/semi_implicit_solver_alg_mod.x90 @@ -75,7 +75,9 @@ module semi_implicit_solver_alg_mod ! IO use timing_mod, only: start_timing, stop_timing, & tik, LPROF - use dycore_constants_mod, only: stepper_siqn + use dycore_constants_mod, only: stepper_siqn, & + stepper_tr, & + stepper_bdf2 implicit none @@ -491,14 +493,18 @@ contains if (.not. allocated(solver_sets)) then if (stepper == stepper_siqn) then allocate(solver_sets(1)) + else if (stepper == stepper_tr .or. stepper == stepper_bdf2) then + allocate(solver_sets(2)) else call log_event("semi_implicit_solver_alg_init: unknown stepper", & LOG_LEVEL_ERROR) end if end if - if (stepper == stepper_siqn) then + if (stepper == stepper_siqn .or. stepper == stepper_tr) then stepper_idx = 1 + else if (stepper == stepper_bdf2) then + stepper_idx = 2 else call log_event("semi_implicit_solver_alg_init: unknown stepper", & LOG_LEVEL_ERROR) @@ -648,8 +654,10 @@ contains mesh => state(igh_p)%get_mesh() ! Set stepper_idx - if (stepper == stepper_siqn) then + if (stepper == stepper_siqn .or. stepper == stepper_tr) then stepper_idx = 1 + else if (stepper == stepper_bdf2) then + stepper_idx = 2 else call log_event("semi_implicit_solver_alg_step: unknown stepper", & LOG_LEVEL_ERROR) diff --git a/science/gungho/source/algorithm/solver/si_operators_alg_mod.x90 b/science/gungho/source/algorithm/solver/si_operators_alg_mod.x90 index 5105ff9769..107b854ce7 100644 --- a/science/gungho/source/algorithm/solver/si_operators_alg_mod.x90 +++ b/science/gungho/source/algorithm/solver/si_operators_alg_mod.x90 @@ -48,7 +48,8 @@ module si_operators_alg_mod use mixed_solver_config_mod, only: eliminate_variables, & eliminate_variables_analytic use moist_dyn_mod, only: num_moist_factors, gas_law, total_mass - use dycore_constants_mod, only: stepper_siqn + use dycore_constants_mod, only: stepper_siqn, stepper_tr, stepper_bdf2, & + gamma, gamma2 ! Configuration modules use base_mesh_config_mod, only: prime_mesh_name @@ -404,13 +405,32 @@ contains ! the timestep length obtained from the clock dt = real(clock%get_seconds_per_step(), r_def) cp = config%planet%cp() - tau_u = config%timestepping%tau_u() - tau_r = config%timestepping%tau_r() - tau_t = config%timestepping%tau_t() - tau_u_dt_cp = real(tau_u*dt*cp, r_solver) - tau_t_dt = real(tau_t*dt, r_solver) - tau_r_dt = real(tau_r*dt, r_solver) - tau_tu_dt2_cp = real(tau_t*tau_u*dt**2*cp, r_solver) + + select case (stepper) + case (stepper_siqn) + tau_u = config%timestepping%tau_u() + tau_r = config%timestepping%tau_r() + tau_t = config%timestepping%tau_t() + tau_u_dt_cp = real(tau_u*dt*cp, r_solver) + tau_t_dt = real(tau_t*dt, r_solver) + tau_r_dt = real(tau_r*dt, r_solver) + tau_tu_dt2_cp = real(tau_t*tau_u*dt**2*cp, r_solver) + case (stepper_tr) + ! tau_u = gamma, tau_r = 2*gamma, tau_t = 2*gamma + tau_u_dt_cp = real(gamma*dt*cp, r_solver) + tau_t_dt = 2.0_r_solver*real(gamma*dt, r_solver) + tau_r_dt = 2.0_r_solver*real(gamma*dt, r_solver) + tau_tu_dt2_cp = 2.0_r_solver*real(gamma**2*dt**2*cp, r_solver) + case (stepper_bdf2) + ! tau_u = gamma2, tau_r = 1, tau_t = 1 + tau_u_dt_cp = real(gamma2*dt*cp, r_solver) + tau_t_dt = real(dt, r_solver) + tau_r_dt = real(dt, r_solver) + tau_tu_dt2_cp = real(gamma2*dt**2*cp, r_solver) + case default + call log_event( "Unknown stepper enumerator in create_si_operators", & + LOG_LEVEL_ERROR ) + end select call tau_u_dt_cp_inventory%add_real(tau_u_dt_cp, stepper) call tau_t_dt_inventory%add_real(tau_t_dt, stepper) @@ -934,7 +954,7 @@ contains ! mass matrix should include damping layer and lagged-orography terms ! if these options are not true, this matrix won't contain the terms m2 => get_w2_mass_matrix( & - w2_lagged_damping_layer_matrix, mesh%get_id(), model_clock & + w2_lagged_damping_layer_matrix, stepper, mesh%get_id(), model_clock & ) call m2_r_solver%initialise( w2_fs, w2_fs ) diff --git a/science/gungho/source/algorithm/timestepping/rk_alg_timestep_mod.x90 b/science/gungho/source/algorithm/timestepping/rk_alg_timestep_mod.x90 index f8da8925fc..f8fdecc23c 100644 --- a/science/gungho/source/algorithm/timestepping/rk_alg_timestep_mod.x90 +++ b/science/gungho/source/algorithm/timestepping/rk_alg_timestep_mod.x90 @@ -47,7 +47,8 @@ module rk_alg_timestep_mod copy_bundle, & set_bundle_scalar use dycore_constants_mod, only: get_geopotential, & - get_coriolis + get_coriolis, & + stepper_rk ! PsyKAl PSYClone kernels use sci_enforce_bc_kernel_mod, only: enforce_bc_kernel_type @@ -389,7 +390,8 @@ contains call gungho_transport_control_alg( self%config, & self%rhs_prediction(:,stage), self%state, & self%state(igh_u), self%state(igh_u), & - mr, mr, model_clock, outer, cheap_update ) + mr, mr, model_clock, outer, stepper_rk, & + cheap_update ) ! Compute new rhs if ( rotating ) then coriolis => get_coriolis(mesh_id) diff --git a/science/gungho/source/algorithm/timestepping/semi_implicit_timestep_alg_mod.X90 b/science/gungho/source/algorithm/timestepping/semi_implicit_timestep_alg_mod.X90 index d2a38cf3f1..17c70b6cd3 100644 --- a/science/gungho/source/algorithm/timestepping/semi_implicit_timestep_alg_mod.X90 +++ b/science/gungho/source/algorithm/timestepping/semi_implicit_timestep_alg_mod.X90 @@ -58,8 +58,8 @@ module semi_implicit_timestep_alg_mod lbc_option_um2lfric_file use mixed_solver_config_mod, only: guess_np1, reference_reset_time use stochastic_physics_config_mod, only : use_spt, use_skeb, stph_spectral_dim - use timestepping_config_mod, only: alpha, & - outer_iterations, inner_iterations, & + use timestepping_config_mod, only: alpha, & + outer_iterations, inner_iterations_si, & spinup_alpha use transport_config_mod, only: cheap_update, & transport_ageofair @@ -131,6 +131,11 @@ module semi_implicit_timestep_alg_mod use lam_rhs_alg_mod, only: calc_rhs_lbc, & apply_mask_rhs + ! Common routines + use timestepping_common_mod, only: conditional_collection_copy, & + get_stph_values, put_stph_values + + ! Field mappings use calc_phys_predictors_alg_mod, & only: calc_phys_predictors_alg @@ -199,7 +204,6 @@ module semi_implicit_timestep_alg_mod procedure, public :: finalise => semi_implicit_alg_final procedure, nopass :: run_init procedure, nopass :: run_step - procedure, nopass :: conditional_collection_copy end type semi_implicit_timestep_type @@ -364,13 +368,13 @@ contains #ifdef UM_PHYSICS if (use_spt) then ! Get spectral coefficients and power law from the model database - call get_stph_values(modeldb, spt_array_names, spt_spectral_coeffc, & - spt_spectral_coeffs, spt_power_law) + call get_stph_values(modeldb, spt_array_names, stph_spectral_dim, & + spt_spectral_coeffc, spt_spectral_coeffs, spt_power_law) end if if (use_skeb) then ! Get spectral coefficients and power law from the model database - call get_stph_values(modeldb, skeb_array_names, skeb_spectral_coeffc, & - skeb_spectral_coeffs, skeb_power_law) + call get_stph_values(modeldb, skeb_array_names, stph_spectral_dim, & + skeb_spectral_coeffc, skeb_spectral_coeffs, skeb_power_law) end if #endif @@ -894,7 +898,8 @@ contains call rhs_alg( self%config, self%rhs_n, varbeta*cast_dt, & self%state_after_slow, self%state_n, moist_dyn, & compute_eos=.false., compute_rhs_t_d=.true., & - dlayer_rhs=.false., model_clock=model_clock ) + dlayer_rhs=.false., stepper=stepper_siqn, & + model_clock=model_clock ) call copy_bundle(self%state_after_slow, self%advected_state, bundle_size) ! Set the moisture to be transported to point to the moisture after slow physics @@ -928,8 +933,10 @@ contains call gungho_transport_control_alg( self%config, & self%rhs_adv, self%advected_state, self%state(igh_u), & self%state_n(igh_u), mr, mr_to_adv, model_clock, & - outer, cheap_update, self%adv_inc_prev, self%wind_prev, & - self%state_after_slow(igh_d), self%total_dry_flux, & + outer, stepper_siqn, cheap_update, & + self%adv_inc_prev, self%wind_prev, & + self%state_after_slow(igh_d), self%state_after_slow(igh_d), & + self%total_dry_flux, & adv_tracer_all_outer, adv_tracer_all_outer_after_slow, & adv_tracer_last_outer, adv_tracer_last_outer_after_slow, & con_tracer_all_outer, con_tracer_all_outer_after_slow, & @@ -974,7 +981,8 @@ contains call rhs_alg( self%config, self%rhs_np1, -varalpha*cast_dt, & self%state, self%state, moist_dyn, & compute_eos=.true., compute_rhs_t_d=.true., & - dlayer_rhs=dlayer_on, model_clock=model_clock ) + dlayer_rhs=dlayer_on, stepper=stepper_siqn, & + model_clock=model_clock ) else if (self%use_moisture) call copy_bundle(self%mr_after_slow, mr, nummr) end if @@ -1050,7 +1058,7 @@ contains !============================================================================ ! Start the Inner (nonlinear, coriolis) loop !============================================================================ - inner_dynamics_loop: do inner = 1,inner_iterations + inner_dynamics_loop: do inner = 1, inner_iterations_si(outer) write( log_scratch_space, '(A,2I3)' ) 'loop indices (o, i): ', & outer, inner call log_event( log_scratch_space, LOG_LEVEL_INFO ) @@ -1062,7 +1070,8 @@ contains call rhs_alg( self%config, self%rhs_np1, -varalpha*cast_dt, & self%state, self%state, moist_dyn, & compute_eos=.true., compute_rhs_t_d=.false., & - dlayer_rhs=dlayer_on, model_clock=model_clock ) + dlayer_rhs=dlayer_on, stepper=stepper_siqn, & + model_clock=model_clock ) end if !-------------------------------------------------------------------- ! Compute the LAM LBCs and RHS @@ -1070,7 +1079,7 @@ contains if ( limited_area .and. inner == 1 .and. outer == 1 ) then call lam_solver_lbc(self%state(igh_u), lbc_fields, prime_mesh_name) call calc_rhs_lbc(self%rhs_lbc, lbc_fields, model_clock, prime_mesh_name, & - tau_r) + tau_r, stepper_siqn) end if !-------------------------------------------------------------------- @@ -1099,10 +1108,12 @@ contains end if - write_moisture_diag = write_conservation_diag .and. & - outer == outer_iterations .and. & - inner == inner_iterations .and. & - self%use_moisture + write_moisture_diag = ( & + write_conservation_diag .and. & + outer == outer_iterations .and. & + inner == inner_iterations_si(outer_iterations) .and. & + self%use_moisture & + ) if (write_moisture_diag) then ! Calculate transported density call self%state(igh_d)%copy_field_properties(rho_star) @@ -1139,13 +1150,13 @@ contains ! LAM Overwrite and Blend LBCs !-------------------------------------------------------------------- if ( limited_area ) then - if ( ( blend_frequency == blend_frequency_inner) & - .or. & - ( blend_frequency == blend_frequency_outer & - .and. inner == inner_iterations ) & - .or. & - ( blend_frequency == blend_frequency_final & - .and. inner == inner_iterations & + if ( ( blend_frequency == blend_frequency_inner) & + .or. & + ( blend_frequency == blend_frequency_outer & + .and. inner == inner_iterations_si(outer) ) & + .or. & + ( blend_frequency == blend_frequency_final & + .and. inner == inner_iterations_si(outer_iterations) & .and. outer == outer_iterations ) ) then call lam_blend_lbc(self%state(igh_u), self%state(igh_p), & @@ -1175,6 +1186,11 @@ contains ) end if + if (write_moisture_diag) then + call moisture_conservation_alg(self%config, self%state(igh_d), mr, & + 'After solve') + end if + else ! when use_wavedynamics=false, just add physics increments here call invoke(X_plus_Y(self%state(igh_t), self%state_after_slow(igh_t), self%dtheta), & @@ -1333,138 +1349,4 @@ contains return end subroutine semi_implicit_alg_final - !============================================================================! - !> @brief Make a deep copy of a subset of a field collection, based on a - !> list of fields provided by another collection - !> @param[out] generic_fields_copied New collection written with fields - !> saved at some point in time - !> @param[in] generic_fields_to_copy collection we want to save fields from - !> @param[in] field_list list of fields to be saved - subroutine conditional_collection_copy(generic_fields_copied, & - generic_fields_to_copy, & - field_list) - - use field_collection_mod, only: field_collection_type - use field_collection_iterator_mod, only: field_collection_iterator_type - - implicit none - - type(field_collection_type), intent(out) :: generic_fields_copied - type(field_collection_type), intent(in) :: generic_fields_to_copy - type(field_collection_type), intent(in) :: field_list - - ! Iterator for field collection - type(field_collection_iterator_type) :: iterator - - ! One of the single fields out of the generic_fields_to_copy collection - class( field_parent_type ), pointer :: abstract_field_ptr - type(field_type), pointer :: single_generic_field - - ! The saved version of single_generic_field - type(field_type) :: copied_generic_field - - logical(kind=l_def) :: l_copy - - nullify(abstract_field_ptr) - nullify(single_generic_field) - - call generic_fields_copied%initialise(name='fields_copied') - - if ( generic_fields_to_copy%get_length() > 0 ) then - - call iterator%initialise(generic_fields_to_copy) - - do - if ( .not.iterator%has_next() ) exit - - abstract_field_ptr => iterator%next() - select type(abstract_field_ptr) - type is (field_type) - single_generic_field => abstract_field_ptr - end select - - l_copy = field_list%field_exists(single_generic_field%get_name()) - - if ( l_copy ) then - - ! We copy the field we want to save into a new field - call single_generic_field%copy_field_properties(copied_generic_field) - call invoke( setval_X(copied_generic_field, single_generic_field) ) - - ! We add it to the field collection passed in - call generic_fields_copied%add_field(copied_generic_field) - - end if ! ( l_copy ) - - end do - - end if - - end subroutine conditional_collection_copy - - !> Get some stochastic physics variables out of modeldb - !> @param[in] modeldb Holds the model state - !> @param[in] array_names List of names that reference values - !> @param[out] spectral_coeffc Spectral coefficients - !> @param[out] spectral_coeffs Spectral coefficients - !> @param[out] power_law Power law - - subroutine get_stph_values(modeldb, array_names, spectral_coeffc, & - spectral_coeffs, power_law) - - type(modeldb_type), intent(in) :: modeldb - character(*), intent(in) :: array_names(:) - - real(r_def), allocatable, intent(out) :: spectral_coeffc(:) - real(r_def), allocatable, intent(out) :: spectral_coeffs(:) - real(r_def), allocatable, intent(out) :: power_law(:) - - type(io_value_type), pointer :: stph_io_value_type - - allocate(spectral_coeffc(stph_spectral_dim)) - allocate(spectral_coeffs(stph_spectral_dim)) - allocate(power_law(stph_spectral_dim)) - - stph_io_value_type => get_io_value(modeldb%values, array_names(1)) - spectral_coeffc = stph_io_value_type%data - stph_io_value_type => get_io_value(modeldb%values, array_names(2)) - spectral_coeffs = stph_io_value_type%data - stph_io_value_type => get_io_value(modeldb%values, array_names(3)) - power_law = stph_io_value_type%data - end subroutine get_stph_values - - !> Put some stochastic physics values into modeldb and deallocate - !> holding variables - !> @param[in] modeldb Holds the model state - !> @param[in] array_names List of names that reference values - !> @param[in,out] spectral_coeffc Spectral coefficients - !> @param[in,out] spectral_coeffs Spectral coefficients - !> @param[in,out] power_law Power law - - subroutine put_stph_values(modeldb, array_names, spectral_coeffc, & - spectral_coeffs, power_law) - - type(modeldb_type), intent(in) :: modeldb - character(*), intent(in) :: array_names(:) - - real(r_def), allocatable, intent(inout) :: spectral_coeffc(:) - real(r_def), allocatable, intent(inout) :: spectral_coeffs(:) - real(r_def), allocatable, intent(inout) :: power_law(:) - - type(io_value_type), pointer :: stph_io_value_type - - stph_io_value_type => get_io_value(modeldb%values, array_names(1)) - stph_io_value_type%data = spectral_coeffc - stph_io_value_type => get_io_value(modeldb%values, array_names(2)) - stph_io_value_type%data = spectral_coeffs - stph_io_value_type => get_io_value(modeldb%values, array_names(3)) - stph_io_value_type%data = power_law - - deallocate(spectral_coeffc) - deallocate(spectral_coeffs) - deallocate(power_law) - - end subroutine put_stph_values - - end module semi_implicit_timestep_alg_mod diff --git a/science/gungho/source/algorithm/timestepping/timestepping_common_mod.x90 b/science/gungho/source/algorithm/timestepping/timestepping_common_mod.x90 new file mode 100644 index 0000000000..31ee2d70aa --- /dev/null +++ b/science/gungho/source/algorithm/timestepping/timestepping_common_mod.x90 @@ -0,0 +1,154 @@ +!------------------------------------------------------------------------------- +! (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 timestepping_common_mod + + use constants_mod, only: l_def, r_def, i_def + use driver_modeldb_mod, only: modeldb_type + use field_parent_mod, only: field_parent_type + use field_mod, only: field_type + use field_collection_mod, only: field_collection_type + use field_collection_iterator_mod, only: field_collection_iterator_type + use io_value_mod, only: io_value_type, get_io_value + + implicit none + private + + public :: conditional_collection_copy + public :: get_stph_values + public :: put_stph_values + +contains + + !> @brief Make a deep copy of a subset of a field collection, based on a + !! list of fields provided by another collection + !> @param[out] generic_fields_copied New collection written with fields + !! saved at some point in time + !> @param[in] generic_fields_to_copy collection we want to save fields from + !> @param[in] field_list list of fields to be saved + subroutine conditional_collection_copy(generic_fields_copied, & + generic_fields_to_copy, & + field_list) + + + implicit none + + type(field_collection_type), intent(out) :: generic_fields_copied + type(field_collection_type), intent(in) :: generic_fields_to_copy + type(field_collection_type), intent(in) :: field_list + + ! Iterator for field collection + type(field_collection_iterator_type) :: iterator + + ! One of the single fields out of the generic_fields_to_copy collection + class(field_parent_type), pointer :: abstract_field_ptr + type(field_type), pointer :: single_generic_field + + ! The saved version of single_generic_field + type(field_type) :: copied_generic_field + + logical(kind=l_def) :: l_copy + + nullify(abstract_field_ptr) + nullify(single_generic_field) + + call generic_fields_copied%initialise(name='fields_copied') + + if ( generic_fields_to_copy%get_length() > 0 ) then + + call iterator%initialise(generic_fields_to_copy) + + do + if ( .not.iterator%has_next() ) exit + + abstract_field_ptr => iterator%next() + select type(abstract_field_ptr) + type is (field_type) + single_generic_field => abstract_field_ptr + end select + + l_copy = field_list%field_exists(single_generic_field%get_name()) + + if ( l_copy ) then + + ! We copy the field we want to save into a new field + call single_generic_field%copy_field_properties(copied_generic_field) + call invoke( setval_X(copied_generic_field, single_generic_field) ) + + ! We add it to the field collection passed in + call generic_fields_copied%add_field(copied_generic_field) + + end if ! ( l_copy ) + + end do + + end if + + end subroutine conditional_collection_copy + + !> Get some stochastic physics variables out of modeldb + !> @param[in] modeldb Holds the model state + !> @param[in] array_names List of names that reference values + !> @param[out] spectral_coeffc Spectral coefficients + !> @param[out] spectral_coeffs Spectral coefficients + !> @param[out] power_law Power law + subroutine get_stph_values(modeldb, array_names, stph_spectral_dim, & + spectral_coeffc, spectral_coeffs, power_law) + + type(modeldb_type), intent(in) :: modeldb + character(*), intent(in) :: array_names(:) + integer(i_def), intent(in) :: stph_spectral_dim + real(r_def), allocatable, intent(out) :: spectral_coeffc(:) + real(r_def), allocatable, intent(out) :: spectral_coeffs(:) + real(r_def), allocatable, intent(out) :: power_law(:) + + type(io_value_type), pointer :: stph_io_value_type + + allocate(spectral_coeffc(stph_spectral_dim)) + allocate(spectral_coeffs(stph_spectral_dim)) + allocate(power_law(stph_spectral_dim)) + + stph_io_value_type => get_io_value(modeldb%values, array_names(1)) + spectral_coeffc = stph_io_value_type%data + stph_io_value_type => get_io_value(modeldb%values, array_names(2)) + spectral_coeffs = stph_io_value_type%data + stph_io_value_type => get_io_value(modeldb%values, array_names(3)) + power_law = stph_io_value_type%data + end subroutine get_stph_values + + !> Put some stochastic physics values into modeldb and deallocate + !> holding variables + !> @param[in] modeldb Holds the model state + !> @param[in] array_names List of names that reference values + !> @param[in,out] spectral_coeffc Spectral coefficients + !> @param[in,out] spectral_coeffs Spectral coefficients + !> @param[in,out] power_law Power law + subroutine put_stph_values(modeldb, array_names, spectral_coeffc, & + spectral_coeffs, power_law) + + type(modeldb_type), intent(in) :: modeldb + character(*), intent(in) :: array_names(:) + + real(r_def), allocatable, intent(inout) :: spectral_coeffc(:) + real(r_def), allocatable, intent(inout) :: spectral_coeffs(:) + real(r_def), allocatable, intent(inout) :: power_law(:) + + type(io_value_type), pointer :: stph_io_value_type + + stph_io_value_type => get_io_value(modeldb%values, array_names(1)) + stph_io_value_type%data = spectral_coeffc + stph_io_value_type => get_io_value(modeldb%values, array_names(2)) + stph_io_value_type%data = spectral_coeffs + stph_io_value_type => get_io_value(modeldb%values, array_names(3)) + stph_io_value_type%data = power_law + + deallocate(spectral_coeffc) + deallocate(spectral_coeffs) + deallocate(power_law) + + end subroutine put_stph_values + +end module timestepping_common_mod \ No newline at end of file diff --git a/science/gungho/source/algorithm/timestepping/tr_bdf2_timestep_alg_mod.X90 b/science/gungho/source/algorithm/timestepping/tr_bdf2_timestep_alg_mod.X90 new file mode 100644 index 0000000000..8b56c33cd5 --- /dev/null +++ b/science/gungho/source/algorithm/timestepping/tr_bdf2_timestep_alg_mod.X90 @@ -0,0 +1,1645 @@ +!------------------------------------------------------------------------------- +! (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 A three time-level iterative timestepper for the 3D nonlinear +!! compressible Euler equations, using a Quasi-Newton method with a +!! Trapezoidal Backward-Difference (TR-BDF2) time discretisation. +module tr_bdf2_timestep_alg_mod + + ! Infrastructure + use config_mod, only: config_type + use constants_mod, only: i_def, r_def, l_def, str_def, r_tran + use driver_modeldb_mod, only: modeldb_type + use extrusion_mod, only: TWOD + use field_mod, only: field_type + use field_array_mod, only: field_array_type + use field_collection_mod, only: field_collection_type + use fs_continuity_mod, only: Wtheta, W2 + use io_value_mod, only: io_value_type, & + get_io_value + + use log_mod, only: log_event, log_scratch_space, & + LOG_LEVEL_INFO + use mesh_mod, only: mesh_type + use mesh_collection_mod, only: mesh_collection + use model_clock_mod, only: model_clock_type + use operator_mod, only: operator_type + use r_tran_field_mod, only: r_tran_field_type + use timing_mod, only: start_timing, stop_timing, tik, LPROF + + ! Field bundle built-ins + use derived_config_mod, only: bundle_size + use sci_field_bundle_builtins_mod, & + only: clone_bundle, & + bundle_axpy, & + bundle_axpby, & + add_bundle, & + copy_bundle, & + set_bundle_scalar + + ! Apps routines + use cld_incs_mod, only: cld_incs_init, cld_incs_output + use field_indices_mod, only: igh_u, igh_t, igh_d, igh_p + use moist_dyn_mod, only: num_moist_factors, gas_law + use mr_indices_mod, only: nummr, imr_v, imr_cl, imr_ci, & + imr_s, imr_r, imr_g + use si_diagnostics_mod, only: output_diags_for_si + use timestepping_common_mod, only: conditional_collection_copy, & + get_stph_values, put_stph_values + use timestep_method_mod, only: timestep_method_type + + ! Algorithms + use sci_mass_matrix_solver_alg_mod, & + only: mass_matrix_solver_alg + use ageofair_alg_mod, only: ageofair_update + use calc_phys_predictors_alg_mod, & + only: calc_phys_predictors_alg + use derive_exner_from_eos_alg_mod, & + only: derive_exner_from_eos + use fast_physics_alg_mod, only: fast_physics + use gungho_transport_control_alg_mod, & + only: gungho_transport_control_alg_init, & + gungho_transport_control_alg + use lam_rhs_alg_mod, only: calc_rhs_lbc, & + apply_mask_rhs + + use limited_area_lbc_alg_mod, only: lam_solver_lbc, & + lam_blend_lbc + use map_physics_fields_alg_mod, only: map_physics_fields_alg + use mixing_alg_mod, only: mixing_alg + use moist_dyn_factors_alg_mod, only: moist_dyn_factors_alg + use moisture_conservation_alg_mod, & + only: moisture_conservation_alg + use rhs_alg_mod, only: rhs_alg + use predictors_alg_mod, only: predictors_alg + use semi_implicit_solver_alg_mod, & + only: semi_implicit_solver_alg_init, & + semi_implicit_solver_alg_step, & + semi_implicit_solver_alg_final + use si_operators_alg_mod, only: create_si_operators, & + compute_si_operators_common, & + compute_helmholtz_operators, & + final_si_operators + use slow_physics_alg_mod, only: slow_physics + use smagorinsky_alg_mod, only: smagorinsky_alg + use solver_moist_correction_alg_mod, & + only: solver_moist_correction_alg + use update_prognostic_scalars_alg_mod, & + only: update_prognostic_scalars_alg +#ifdef UM_PHYSICS + use cld_alg_mod, only: cld_alg + use aerosol_ukca_alg_mod, only: aerosol_ukca_alg + use casim_activate_alg_mod, only: casim_activate_alg + use lsp_precfrac_checks_kernel_mod, & + only: lsp_precfrac_checks_kernel_type + use stph_main_alg_mod, only: spt_array_names, & + spt_array_count, & + skeb_array_names, & + skeb_array_count +#endif + + ! Kernels + use sci_enforce_bc_kernel_mod, only: enforce_bc_kernel_type + use dg_matrix_vector_kernel_mod, & + only: dg_matrix_vector_kernel_type + use dg_inc_matrix_vector_kernel_mod, & + only: dg_inc_matrix_vector_kernel_type + use matrix_vector_kernel_mod, only: matrix_vector_kernel_type + + ! Pointers + use sci_fem_constants_mod, only: get_mass_matrix_fe, & + get_mass_matrix_fv + use dycore_constants_mod, only: stepper_tr, stepper_bdf2, & + gamma, gamma2, gamma3 + + ! Configuration options + use aerosol_config_mod, only: glomap_mode_dust_and_clim, & + glomap_mode_ukca + use boundaries_config_mod, only: blend_frequency_inner, & + blend_frequency_outer, & + blend_frequency_final + use formulation_config_mod, only: moisture_formulation_dry + use initialization_config_mod, only: init_option_checkpoint_dump, & + lbc_option_um2lfric_file + use physics_config_mod, only: smagorinsky_placement_outer + use section_choice_config_mod, only: cloud_um, aerosol_um, cloud_none, & + aerosol_none + use stochastic_physics_config_mod, & + only: stochastic_physics_is_loaded + + implicit none + + private + + ! State object for the TR-BDF2 time-stepping method + type, extends(timestep_method_type), public :: tr_bdf2_timestep_type + private + + type(config_type), pointer :: config + logical(kind=l_def) :: use_moisture + logical(kind=l_def) :: output_cld_incs + type(r_tran_field_type) :: total_dry_flux ! dry density flux stored + ! stored over whole step + type(field_type) :: dtheta, dtheta_cld ! increment to theta + type(field_type) :: du ! increment to u + type(field_type) :: wind_prev ! u from prev iteration + ! used for cheap transport update + + type(field_type), allocatable :: state(:) ! Latest estimate of time n+1 + type(field_type), allocatable :: state_n(:) ! Fields at time n + type(field_type), allocatable :: state_m(:) ! Latest estimate of midpoint + type(field_type), allocatable :: state_q(:) ! Used to calculate state_bdf + type(field_type), allocatable :: state_bdf(:) ! Initial state for BDF2 step + type(field_type), allocatable :: state_dag(:) ! State after slow physics + type(field_type), allocatable :: state_to_adv(:) ! State to transport (both steps) + type(field_type), allocatable :: state_aft_adv(:) ! State after transport (both steps) + type(field_type), allocatable :: mr_n(:) ! Mixing ratios at time n + type(field_type), allocatable :: mr_aft_adv(:) ! Mixing ratios after transport (both) + type(field_type), allocatable :: mr_dag(:) ! Mixing ratios after slow physics + type(field_type), allocatable :: mr_bdf(:) ! Mixing ratios after BDF2 step + type(field_type), allocatable :: rhs_n(:) ! Explicit RHS (TR) + type(field_type), allocatable :: rhs_np1(:) ! Implicit RHS (BDF2) + type(field_type), allocatable :: rhs_adv(:) ! Transport RHS (both) + type(field_type), allocatable :: rhs_bdf(:) ! Explicit RHS (BDF2) + type(field_type), allocatable :: rhs_m(:) ! Implicit RHS (TR) + type(field_type), allocatable :: adv_inc_prev(:) ! Transport increment from + ! prev iteration (for cheap update) + type(field_type), allocatable :: rhs_phys(:) ! Physics RHS (both steps) + type(field_type), allocatable :: rhs_lbc(:) ! LBC RHS (both steps) + + contains + private + + procedure, public :: step => tr_bdf2_alg_step + procedure, public :: finalise => tr_bdf2_alg_final + procedure, nopass :: run_init + procedure, nopass :: run_step + + end type tr_bdf2_timestep_type + + ! Constructor for type + interface tr_bdf2_timestep_type + module procedure tr_bdf2_alg_init + end interface tr_bdf2_timestep_type + +contains + + !> Extracts data from modeldb to prepare for initialising object + !> @param[in] modeldb Holds the model state + function tr_bdf2_alg_init(modeldb) result(self) + implicit none + + type(modeldb_type), target, intent(in) :: modeldb + + type(tr_bdf2_timestep_type) :: self + type(field_collection_type), pointer :: prognostic_fields + type(field_collection_type), pointer :: moisture_fields + type(field_type), pointer :: u + type(field_type), pointer :: rho + type(field_type), pointer :: theta + type(field_type), pointer :: exner + type(field_array_type), pointer :: mr_array + type(field_type), pointer :: mr(:) + class(model_clock_type), pointer :: model_clock + + ! Get pointer to clock to use downstream + model_clock => modeldb%clock + + ! Get pointers to field collections for use downstream + prognostic_fields => modeldb%fields%get_field_collection("prognostic_fields") + + ! Get pointers to fields in the prognostic/diagnostic field collections + ! for use downstream + call prognostic_fields%get_field('theta', theta) + call prognostic_fields%get_field('u', u) + call prognostic_fields%get_field('rho', rho) + call prognostic_fields%get_field('exner', exner) + + moisture_fields => modeldb%fields%get_field_collection("moisture_fields") + call moisture_fields%get_field("mr", mr_array) + mr => mr_array%bundle + + ! Run the initialisation process + call run_init( & + self, modeldb%config, u, rho, theta, exner, mr, & + prognostic_fields, moisture_fields, model_clock & + ) + + end function tr_bdf2_alg_init + + !> Extracts data from input objects to prepare for running timestep + !> @param[in] modeldb The gungho model data object + subroutine tr_bdf2_alg_step(self, modeldb) + + implicit none + + class(tr_bdf2_timestep_type), intent(inout) :: self + type(modeldb_type), target, intent(in) :: modeldb + + type(mesh_type), pointer :: mesh + type(mesh_type), pointer :: twod_mesh + + class(model_clock_type), pointer :: model_clock + type(io_value_type), pointer :: temp_corr_io_value + + type(field_collection_type), pointer :: prognostic_fields + type(field_collection_type), pointer :: moisture_fields + type(field_type), pointer :: u + type(field_type), pointer :: rho + type(field_type), pointer :: theta + type(field_type), pointer :: exner + type(field_array_type), pointer :: mr_array + type(field_type), pointer :: mr(:) + type(field_array_type), pointer :: moist_dyn_array + type(field_type), pointer :: moist_dyn(:) + + type(field_collection_type), pointer :: adv_tracer_all_outer + type(field_collection_type), pointer :: adv_tracer_last_outer + type(field_collection_type), pointer :: con_tracer_all_outer + type(field_collection_type), pointer :: con_tracer_last_outer + type(field_collection_type), pointer :: derived_fields + type(field_collection_type), pointer :: radiation_fields + type(field_collection_type), pointer :: microphysics_fields + type(field_collection_type), pointer :: electric_fields + type(field_collection_type), pointer :: orography_fields + type(field_collection_type), pointer :: turbulence_fields + type(field_collection_type), pointer :: convection_fields + type(field_collection_type), pointer :: cloud_fields + type(field_collection_type), pointer :: surface_fields + type(field_collection_type), pointer :: soil_fields + type(field_collection_type), pointer :: snow_fields + type(field_collection_type), pointer :: chemistry_fields + type(field_collection_type), pointer :: aerosol_fields + type(field_collection_type), pointer :: stph_fields + type(field_collection_type), pointer :: lbc_fields + + ! Arrays for passing modeldb data to stochastic physics scheme + real(kind=r_def), allocatable :: spt_spectral_coeffc(:) + real(kind=r_def), allocatable :: spt_spectral_coeffs(:) + real(kind=r_def), allocatable :: spt_power_law(:) + real(kind=r_def), allocatable :: skeb_spectral_coeffc(:) + real(kind=r_def), allocatable :: skeb_spectral_coeffs(:) + real(kind=r_def), allocatable :: skeb_power_law(:) + + real(kind=r_def) :: dt + real(kind=r_def) :: dtemp_encorr +#ifdef UM_PHYSICS + integer(kind=i_def) :: stph_spectral_dim + logical(kind=l_def) :: use_spt + logical(kind=l_def) :: use_skeb +#endif + + ! Get pointers to field collections for use downstream + prognostic_fields => modeldb%fields%get_field_collection("prognostic_fields") + model_clock => modeldb%clock + + ! Get pointers to fields for use downstream + call prognostic_fields%get_field('theta', theta) + call prognostic_fields%get_field('u', u) + call prognostic_fields%get_field('rho', rho) + call prognostic_fields%get_field('exner', exner) + + ! Get timestep parameters from clock + dt = real(model_clock%get_seconds_per_step(), r_def) + + moisture_fields => modeldb%fields%get_field_collection("moisture_fields") + lbc_fields => modeldb%fields%get_field_collection("lbc_fields") + radiation_fields => modeldb%fields%get_field_collection("radiation_fields") + call moisture_fields%get_field("mr", mr_array) + mr => mr_array%bundle + call moisture_fields%get_field("moist_dyn", moist_dyn_array) + moist_dyn => moist_dyn_array%bundle + + ! Get the mesh from one of the fields + mesh => theta%get_mesh() + twod_mesh => mesh_collection%get_mesh(mesh, TWOD) + + adv_tracer_all_outer => modeldb%fields%get_field_collection("adv_tracer_all_outer") + adv_tracer_last_outer => modeldb%fields%get_field_collection("adv_tracer_last_outer") + con_tracer_all_outer => modeldb%fields%get_field_collection("con_tracer_all_outer") + con_tracer_last_outer => modeldb%fields%get_field_collection("con_tracer_last_outer") + derived_fields => modeldb%fields%get_field_collection("derived_fields") + microphysics_fields => modeldb%fields%get_field_collection("microphysics_fields") + turbulence_fields => modeldb%fields%get_field_collection("turbulence_fields") + convection_fields => modeldb%fields%get_field_collection("convection_fields") + cloud_fields => modeldb%fields%get_field_collection("cloud_fields") + surface_fields => modeldb%fields%get_field_collection("surface_fields") + soil_fields => modeldb%fields%get_field_collection("soil_fields") + snow_fields => modeldb%fields%get_field_collection("snow_fields") + chemistry_fields => modeldb%fields%get_field_collection("chemistry_fields") + aerosol_fields => modeldb%fields%get_field_collection("aerosol_fields") + stph_fields => modeldb%fields%get_field_collection("stph_fields") + electric_fields => modeldb%fields%get_field_collection("electric_fields") + orography_fields => modeldb%fields%get_field_collection("orography_fields") + + temp_corr_io_value => get_io_value( modeldb%values, 'temperature_correction_io_value') + ! Get temperature increment for energy correction + dtemp_encorr = dt * temp_corr_io_value%data(1) + +#ifdef UM_PHYSICS + if (stochastic_physics_is_loaded()) then + use_spt = self%config%stochastic_physics%use_spt() + use_skeb = self%config%stochastic_physics%use_skeb() + else + use_spt = .false. + use_skeb = .false. + end if + + if (use_spt) then + ! Get spectral coefficients and power law from the model database + stph_spectral_dim = self%config%stochastic_physics%stph_spectral_dim() + call get_stph_values( & + modeldb, spt_array_names, stph_spectral_dim, & + spt_spectral_coeffc, spt_spectral_coeffs, spt_power_law & + ) + end if + if (use_skeb) then + ! Get spectral coefficients and power law from the model database + call get_stph_values( & + modeldb, skeb_array_names, stph_spectral_dim, & + skeb_spectral_coeffc, skeb_spectral_coeffs, skeb_power_law & + ) + end if +#endif + + ! Run the timestep + call run_step( & + self, modeldb, u, rho, theta, exner, mr, moist_dyn, & + adv_tracer_all_outer, adv_tracer_last_outer, & + con_tracer_all_outer, con_tracer_last_outer, & + prognostic_fields, moisture_fields, & + derived_fields, radiation_fields, microphysics_fields, & + electric_fields, orography_fields, turbulence_fields, & + convection_fields, cloud_fields, surface_fields, soil_fields, & + snow_fields, chemistry_fields, aerosol_fields, stph_fields, & + lbc_fields, & + model_clock, dtemp_encorr, & + spt_spectral_coeffc, spt_spectral_coeffs, spt_power_law, & + skeb_spectral_coeffc, skeb_spectral_coeffs, skeb_power_law, & + mesh, twod_mesh & + ) + +#ifdef UM_PHYSICS + if (use_spt) then + ! Put spectral coefficients and power law back into model database + call put_stph_values( & + modeldb, spt_array_names, spt_spectral_coeffc, & + spt_spectral_coeffs, spt_power_law & + ) + end if + + if (use_skeb) then + ! Put spectral coefficients and power law back into model database + call put_stph_values( & + modeldb, skeb_array_names, skeb_spectral_coeffc, & + skeb_spectral_coeffs, skeb_power_law & + ) + end if +#endif + + end subroutine tr_bdf2_alg_step + + !> @details Initialisation procedure for the timestepping algorithm + !> Initialises various internal fields + !> @param[in] config Application namelist configuration object + !> @param[in,out] u 3D wind field + !> @param[in,out] rho Density + !> @param[in,out] theta Potential temperature + !> @param[in,out] exner Exner pressure + !> @param[in,out] mr Mixing ratios + !> @param[in,out] prognostic_fields Prognostic field collection + !> @param[in,out] moisture_fields Moisture field collection + !> @param[in] model_clock Time in the model. + subroutine run_init( & + self, config, u, rho, theta, exner, mr, & + prognostic_fields, moisture_fields, model_clock & + ) + + implicit none + + type(tr_bdf2_timestep_type), intent(inout) :: self + type(config_type), target, intent(in) :: config + type(field_type), intent(in) :: u, rho, theta, exner + type(field_type), optional, intent(in) :: mr(:) + type(field_collection_type ), intent(inout) :: prognostic_fields + type(field_collection_type ), intent(inout) :: moisture_fields + class(model_clock_type), intent(in) :: model_clock + + ! Mesh + type(mesh_type), pointer :: mesh + ! Reference fields + type(field_type), pointer :: rho_ref, theta_ref, exner_ref + ! Reference moist dynamics factors + type(field_array_type), pointer :: moist_dyn_ref_array + type(field_type), pointer :: moist_dyn_ref(:) + + ! Timestep + real(kind=r_def) :: dt + + ! Reset frequency of semi-implicit operators + integer(kind=i_def) :: reference_reset_freq + + ! Configuration options retrieved from the config object + logical(kind=l_def) :: use_wavedynamics + integer(kind=i_def) :: moisture_formulation + logical(kind=l_def) :: write_diag + logical(kind=l_def) :: use_xios_io + real(kind=r_def) :: reference_reset_time + + self%config => config + + use_wavedynamics = config%formulation%use_wavedynamics() + moisture_formulation = config%formulation%moisture_formulation() + write_diag = config%io%write_diag() + use_xios_io = config%io%use_xios_io() + reference_reset_time = config%mixed_solver%reference_reset_time() + + ! Get the mesh from one of the fields + mesh => theta%get_mesh() + + !------------------------------------------------------------------------- ! + ! Allocate internal state field arrays + !------------------------------------------------------------------------- ! + allocate(self%state(bundle_size)) + allocate(self%state_n(bundle_size)) + allocate(self%state_m(bundle_size)) + allocate(self%state_q(bundle_size)) + allocate(self%state_bdf(bundle_size)) + allocate(self%state_dag(bundle_size)) + allocate(self%state_to_adv(bundle_size)) + allocate(self%state_aft_adv(bundle_size)) + allocate(self%rhs_n(bundle_size)) + allocate(self%rhs_m(bundle_size)) + allocate(self%rhs_bdf(bundle_size)) + allocate(self%rhs_np1(bundle_size)) + allocate(self%rhs_adv(bundle_size)) + allocate(self%adv_inc_prev(bundle_size)) + allocate(self%rhs_phys(bundle_size)) + allocate(self%rhs_lbc(bundle_size)) + allocate(self%mr_n(nummr)) + allocate(self%mr_bdf(nummr)) + allocate(self%mr_dag(nummr)) + allocate(self%mr_aft_adv(nummr)) + + self%use_moisture = ( moisture_formulation /= moisture_formulation_dry ) + self%output_cld_incs = ( & + self%use_moisture .and. write_diag .and. use_xios_io & + ) + + !------------------------------------------------------------------------- ! + ! Initialise internal state field objects + !------------------------------------------------------------------------- ! + + call u%copy_field_properties(self%state(igh_u)) + call theta%copy_field_properties(self%state(igh_t)) + call rho%copy_field_properties(self%state(igh_d)) + call exner%copy_field_properties(self%state(igh_p)) + + call clone_bundle(self%state, self%state_n, bundle_size) + call clone_bundle(self%state, self%state_m, bundle_size) + call clone_bundle(self%state, self%state_q, bundle_size) + call clone_bundle(self%state, self%state_dag, bundle_size) + call clone_bundle(self%state, self%state_bdf, bundle_size) + call clone_bundle(self%state, self%state_to_adv, bundle_size) + call clone_bundle(self%state, self%state_aft_adv, bundle_size) + call clone_bundle(self%state, self%rhs_n, bundle_size) + call clone_bundle(self%state, self%rhs_m, bundle_size) + call clone_bundle(self%state, self%rhs_bdf, bundle_size) + call clone_bundle(self%state, self%rhs_np1, bundle_size) + call clone_bundle(self%state, self%rhs_adv, bundle_size) + call clone_bundle(self%state, self%rhs_phys, bundle_size) + call clone_bundle(self%state, self%rhs_lbc, bundle_size) + call clone_bundle(self%state, self%adv_inc_prev, bundle_size) + + call theta%copy_field_properties(self%dtheta) + call theta%copy_field_properties(self%dtheta_cld) + call invoke(setval_c(self%dtheta_cld, 0.0_r_def)) + call u%copy_field_properties(self%du) + call u%copy_field_properties(self%wind_prev) + + call self%total_dry_flux%initialise(u%get_function_space()) + + call clone_bundle(mr, self%mr_n, nummr) + call clone_bundle(mr, self%mr_dag, nummr) + call clone_bundle(mr, self%mr_bdf, nummr) + if (self%use_moisture) then + call clone_bundle(mr, self%mr_aft_adv, nummr) + else + call set_bundle_scalar(0.0_r_def, self%mr_n, nummr) + call set_bundle_scalar(0.0_r_def, self%mr_dag, nummr) + call set_bundle_scalar(0.0_r_def, self%mr_bdf, nummr) + end if + + ! ------------------------------------------------------------------------ ! + ! Initialise the physics increments to 0 + ! ------------------------------------------------------------------------ ! + call set_bundle_scalar(0.0_r_def, self%rhs_phys, bundle_size) + + ! ------------------------------------------------------------------------ ! + ! Operators for si solves + ! ------------------------------------------------------------------------ ! + call create_si_operators(mesh, stepper_tr, self%config, model_clock) + call create_si_operators(mesh, stepper_bdf2, self%config, model_clock) + + ! If using checkpointed reference state, then calculate semi-implicit + ! operators using the checkpointed reference state + dt = real(model_clock%get_seconds_per_step(), r_def) + reference_reset_freq = nint(reference_reset_time / dt, i_def) + if (mod(model_clock%get_first_step()-1, reference_reset_freq) /= 0) then + call moisture_fields%get_field("moist_dyn_ref", moist_dyn_ref_array) + moist_dyn_ref => moist_dyn_ref_array%bundle + call prognostic_fields%get_field('theta_ref', theta_ref) + call prognostic_fields%get_field('rho_ref', rho_ref) + call prognostic_fields%get_field('exner_ref', exner_ref) + + call compute_si_operators_common( & + self%config, theta_ref, rho_ref, exner_ref, moist_dyn_ref & + ) + call compute_helmholtz_operators(model_clock, stepper_tr) + call compute_helmholtz_operators(model_clock, stepper_bdf2) + end if + + if (use_wavedynamics) then + call gungho_transport_control_alg_init(mesh) + + ! Construct semi-implicit solver + call semi_implicit_solver_alg_init(self%state, stepper_tr) + call semi_implicit_solver_alg_init(self%state, stepper_bdf2) + end if + + call log_event( & + "tr_bdf2_timestep: initialised timestepping algorithm", LOG_LEVEL_INFO & + ) + + end subroutine run_init + + !> @details The control algorithm for the TR-BDF2 timestepper. This is a three + !! time-level scheme that consists of two phases: the Trapezoidal + !! (TR) step and the Backward-Difference (BDF2) step, each of which + !! involves an implicit method that is solved with a Quasi-Newton + !! method. + !! The TR step runs from time n*dt to (n+2*gamma)*dt, while + !! the BDF2 step runs from (n+2*gamma)*dt to (n+1)*dt. + !> @param[in] modeldb Holds the model state + !> @param[in,out] u 3D wind field + !> @param[in,out] rho Density + !> @param[in,out] theta Potential temperature + !> @param[in,out] exner Exner pressure + !> @param[in,out] mr Mixing ratios + !> @param[in,out] moist_dyn Factors for moist dynamics + !> @param[in,out] adv_tracer_all_outer Group of fields to be advected + !> @param[in,out] adv_tracer_last_outer Group of fields to be advected + !> @param[in,out] con_tracer_all_outer Second group of fields to be advected + !> @param[in,out] con_tracer_last_outer Second group of fields to be advected + !> @param[in,out] prognostic_fields Prognostic field collection + !> @param[in,out] moisture_fields Moisture field collection + !> @param[in,out] derived_fields Group of derived fields + !> @param[in,out] radiation_fields Fields for radiation scheme + !> @param[in,out] microphysics_fields Fields for mphys scheme + !> @param[in,out] electric_fields Fields for electric (lighting) scheme + !> @param[in] orography_fields Fields for orog drag scheme + !> @param[in,out] turbulence_fields Fields for turbulence scheme + !> @param[in,out] convection_fields Fields for convection scheme + !> @param[in,out] cloud_fields Fields for cloud scheme + !> @param[in,out] surface_fields Fields for surface scheme + !> @param[in,out] soil_fields Fields for soil hydrology scheme + !> @param[in,out] snow_fields Fields for snow scheme + !> @param[in,out] chemistry_fields Fields for chemistry scheme + !> @param[in,out] aerosol_fields Fields for aerosol scheme + !> @param[in,out] stph_fields Fields for stohcastic physics schemes + !> @param[in,out] lbc_fields Fields for lateral boundaries + !> @param[in] model_clock Time in the model. + !> @param[in] dtemp_encorr Temperature increment for energy + !! correction + !> @param[in,out] spt_spectral_coeffc Real Spectral coefficients + !> @param[in,out] spt_spectral_coeffs Imaginary Spectral coefficients + !> @param[in,out] spt_power_law Spectral power law + !> @param[in,out] skeb_spectral_coeffc Real Spectral coefficients + !> @param[in,out] skeb_spectral_coeffs Imaginary Spectral coefficients + !> @param[in,out] skeb_power_law Spectral power law + !> @param[in] mesh The current mesh + !> @param[in] twod_mesh The current 2d mesh + subroutine run_step( & + self, modeldb, u, rho, theta, exner, mr, moist_dyn, & + adv_tracer_all_outer, adv_tracer_last_outer, & + con_tracer_all_outer, con_tracer_last_outer, & + prognostic_fields, moisture_fields, derived_fields, & + radiation_fields, microphysics_fields, electric_fields, & + orography_fields, turbulence_fields, convection_fields, & + cloud_fields, surface_fields, soil_fields, snow_fields, & + chemistry_fields, aerosol_fields, stph_fields, lbc_fields, & + model_clock, dtemp_encorr, & + spt_spectral_coeffc, spt_spectral_coeffs, spt_power_law, & + skeb_spectral_coeffc, skeb_spectral_coeffs, skeb_power_law, & + mesh, twod_mesh & + ) + + implicit none + + ! Arguments + type(tr_bdf2_timestep_type), target, intent(inout) :: self + type(modeldb_type), target, intent(in) :: modeldb + + type(field_type), intent(inout) :: u, rho, theta, exner + type(field_type), intent(inout) :: mr(nummr) + type(field_type), intent(inout) :: moist_dyn(num_moist_factors) + + type(field_collection_type), intent(inout) :: adv_tracer_all_outer + type(field_collection_type), intent(inout) :: adv_tracer_last_outer + type(field_collection_type), intent(inout) :: con_tracer_all_outer + type(field_collection_type), intent(inout) :: con_tracer_last_outer + type(field_collection_type), intent(inout) :: prognostic_fields + type(field_collection_type), intent(inout) :: moisture_fields + type(field_collection_type), intent(inout) :: derived_fields + type(field_collection_type), intent(inout) :: radiation_fields + type(field_collection_type), intent(inout) :: microphysics_fields + type(field_collection_type), intent(inout) :: electric_fields + type(field_collection_type), intent(in) :: orography_fields + type(field_collection_type), intent(inout) :: turbulence_fields + type(field_collection_type), intent(inout) :: convection_fields + type(field_collection_type), intent(inout) :: cloud_fields + type(field_collection_type), intent(inout) :: surface_fields + type(field_collection_type), intent(inout) :: soil_fields + type(field_collection_type), intent(inout) :: snow_fields + type(field_collection_type), intent(inout) :: chemistry_fields + type(field_collection_type), intent(inout) :: aerosol_fields + type(field_collection_type), intent(inout) :: stph_fields + type(field_collection_type), intent(inout) :: lbc_fields + + class(model_clock_type), intent(in) :: model_clock + real(kind=r_def), intent(in) :: dtemp_encorr + real(kind=r_def), intent(inout) :: spt_spectral_coeffc(:) + real(kind=r_def), intent(inout) :: spt_spectral_coeffs(:) + real(kind=r_def), intent(inout) :: spt_power_law(:) + real(kind=r_def), intent(inout) :: skeb_spectral_coeffc(:) + real(kind=r_def), intent(inout) :: skeb_spectral_coeffs(:) + real(kind=r_def), intent(inout) :: skeb_power_law(:) + type(mesh_type), pointer, intent(in) :: mesh + type(mesh_type), pointer, intent(in) :: twod_mesh + + ! Internal variables + ! Reference fields + type(field_type), pointer :: rho_ref + type(field_type), pointer :: theta_ref + type(field_type), pointer :: exner_ref + type(field_type), pointer :: moist_dyn_ref(:) + type(field_array_type), pointer :: moist_dyn_ref_array + + ! Moisture field to transport + type(field_type), pointer :: mr_to_adv(:) + type(field_type) :: dry_flux_solver + type(r_tran_field_type) :: dry_flux_solver_r_tran + type(field_type) :: rho_star + + type(field_type) :: dcfl_tot, dcff_tot, dbcf_tot + type(field_type) :: dcfl_adv, dcff_adv, dbcf_adv + character(str_def), parameter :: sec_tot='processed' + character(str_def), parameter :: suffix_tot='tot' + character(str_def), parameter :: sec_adv='advection' + character(str_def), parameter :: suffix_adv='adv' + + type(field_type), pointer :: ageofair + type(operator_type), pointer :: mm_wt + type(operator_type), pointer :: mm_vel + +#ifdef UM_PHYSICS + type(field_type), pointer :: precfrac + integer(kind=i_def) :: glomap_mode +#endif + + integer(kind=i_def) :: outer, inner, reference_reset_freq + logical(kind=l_def) :: write_moisture_diag, do_moist_correction + type(field_collection_type) :: adv_tracer_all_outer_dag + type(field_collection_type) :: adv_tracer_last_outer_dag + type(field_collection_type) :: con_tracer_all_outer_dag + type(field_collection_type) :: con_tracer_last_outer_dag + logical(kind=l_def) :: checkpoint_reference_fields + character(len=str_def) :: prime_mesh_name + integer(kind=i_def) :: lbc_option + logical(kind=l_def) :: microphysics_casim + logical(kind=l_def) :: l_mcr_precfrac + logical(kind=l_def) :: murk_lbc + logical(kind=l_def) :: outer_smagorinsky + logical(kind=l_def) :: to_blend_lbc + real(kind=r_def) :: dt + integer(kind=tik) :: id + + ! Configuration options retrieved from the config object + logical(kind=l_def) :: limited_area + integer(kind=i_def) :: blend_frequency + integer(kind=i_def) :: element_order_h + integer(kind=i_def) :: element_order_v + logical(kind=l_def) :: use_physics + logical(kind=l_def) :: dlayer_on + logical(kind=l_def) :: use_wavedynamics + integer(kind=i_def) :: moisture_formulation + logical(kind=l_def) :: exner_from_eos + logical(kind=l_def) :: solver_moisture_conservation + integer(kind=i_def) :: init_option + logical(kind=l_def) :: subroutine_timers + logical(kind=l_def) :: write_conservation_diag + logical(kind=l_def) :: write_diag + logical(kind=l_def) :: use_xios_io + integer(kind=i_def) :: diagnostic_frequency + logical(kind=l_def) :: checkpoint_read + logical(kind=l_def) :: smagorinsky + real(kind=r_def) :: reference_reset_time + integer(kind=i_def) :: smagorinsky_placement + integer(kind=i_def) :: cloud + integer(kind=i_def) :: aerosol + integer(kind=i_def) :: outer_iterations + integer(kind=i_def), allocatable :: inner_iterations_tr(:) + integer(kind=i_def), allocatable :: inner_iterations_bdf2(:) + logical(kind=l_def) :: cheap_update + logical(kind=l_def) :: transport_ageofair + + if (LPROF) call start_timing(id, 'tr_bdf2_timestep_alg') + + ! Get config variables + element_order_h = self%config%finite_element%element_order_h() + element_order_v = self%config%finite_element%element_order_v() + init_option = self%config%initialization%init_option() + subroutine_timers = self%config%io%subroutine_timers() + write_conservation_diag = self%config%io%write_conservation_diag() + write_diag = self%config%io%write_diag() + use_xios_io = self%config%io%use_xios_io() + diagnostic_frequency = self%config%io%diagnostic_frequency() + checkpoint_read = self%config%io%checkpoint_read() + use_physics = self%config%formulation%use_physics() + dlayer_on = self%config%formulation%dlayer_on() + use_wavedynamics = self%config%formulation%use_wavedynamics() + moisture_formulation = self%config%formulation%moisture_formulation() + exner_from_eos = self%config%formulation%exner_from_eos() + if (moisture_formulation /= moisture_formulation_dry) then + solver_moisture_conservation = & + self%config%formulation%solver_moisture_conservation() + else + solver_moisture_conservation = .false. + end if + smagorinsky = self%config%mixing%smagorinsky() + reference_reset_time = self%config%mixed_solver%reference_reset_time() + cheap_update = self%config%transport%cheap_update() + transport_ageofair = self%config%transport%transport_ageofair() + outer_iterations = self%config%timestepping%outer_iterations() + allocate(inner_iterations_tr(outer_iterations)) + allocate(inner_iterations_bdf2(outer_iterations)) + inner_iterations_tr(:) = self%config%timestepping%inner_iterations_tr() + inner_iterations_bdf2(:) = self%config%timestepping%inner_iterations_bdf2() + limited_area = self%config%boundaries%limited_area() + if (limited_area) then + blend_frequency = self%config%boundaries%blend_frequency() + end if + if (use_physics) then + smagorinsky_placement = self%config%physics%smagorinsky_placement() + cloud = self%config%section_choice%cloud() + aerosol = self%config%section_choice%aerosol() + else + smagorinsky_placement = smagorinsky_placement_outer + cloud = cloud_none + aerosol = aerosol_none + end if + lbc_option = 0 + dt = real(model_clock%get_seconds_per_step(), r_def) + + if (limited_area .and. use_wavedynamics) then + + prime_mesh_name = self%config%base_mesh%prime_mesh_name() + lbc_option = self%config%initialization%lbc_option() + + if (lbc_option == lbc_option_um2lfric_file) then + murk_lbc = self%config%aerosol%murk_lbc() + end if + end if + if (lbc_option == lbc_option_um2lfric_file & + .or. (use_physics .and. cloud == cloud_um)) then + microphysics_casim = self%config%microphysics%microphysics_casim() + l_mcr_precfrac = self%config%microphysics%l_mcr_precfrac() + end if + + if (element_order_h == 0 .and. element_order_v == 0) then + ! Lowest order so use finite volume constants + mm_wt => get_mass_matrix_fv(Wtheta, mesh%get_id()) + mm_vel => get_mass_matrix_fv(W2, mesh%get_id()) + else + mm_wt => get_mass_matrix_fe(Wtheta, mesh%get_id()) + mm_vel => get_mass_matrix_fe(W2, mesh%get_id()) + end if + + !--------------------------------------------------------------------------- + ! Copy prognostic field data to state arrays + !--------------------------------------------------------------------------- + call invoke( & + name="copy_init_fields_to_state", & + setval_X(self%state(igh_u), u ), & + setval_X(self%state(igh_t), theta), & + setval_X(self%state(igh_d), rho), & + setval_X(self%state(igh_p), exner), & + setval_c(self%total_dry_flux, 0.0_r_tran) & + ) + + call copy_bundle(self%state, self%state_n, bundle_size) + call copy_bundle(self%state, self%state_m, bundle_size) + call copy_bundle(self%state, self%state_dag, bundle_size) + if (self%use_moisture) then + call copy_bundle(mr, self%mr_n, nummr) + call copy_bundle(mr, self%mr_dag, nummr) + end if + + call rho_star%initialise(rho%get_function_space()) + call dry_flux_solver%initialise(u%get_function_space()) + + ! ======================================================================== ! + ! SLOW PHYSICS + ! ======================================================================== ! + + if (use_physics) then + if (self%output_cld_incs) then + call cld_incs_init( & + cloud_fields, dcfl_tot, dcff_tot, dbcf_tot, sec_tot, suffix_tot & + ) + end if + + call slow_physics( & + modeldb, self%du, self%dtheta, self%mr_dag, & + self%state_n(igh_t), self%state_n(igh_u), self%state_n(igh_d), & + self%state_n(igh_p), moist_dyn, self%mr_n, & + derived_fields, radiation_fields, microphysics_fields, & + electric_fields, orography_fields, turbulence_fields, & + convection_fields, cloud_fields, surface_fields, soil_fields, & + snow_fields, chemistry_fields, aerosol_fields, model_clock, & + dt, dtemp_encorr, mesh, twod_mesh & + ) + + call invoke( & + name="update_from_slow_physics", & + inc_X_plus_Y(self%state_dag(igh_t), self%dtheta), & + inc_X_plus_Y(self%state_dag(igh_u), self%du) & + ) + ! mr_dag is already updated in slow_physics + + if (self%output_cld_incs) then + call cld_incs_init( & + cloud_fields, dcfl_adv, dcff_adv, dbcf_adv, sec_adv, suffix_adv & + ) + end if + end if ! use_physics + + call conditional_collection_copy( & + adv_tracer_all_outer_dag, generic_fields_to_copy=adv_tracer_all_outer, & + field_list=adv_tracer_all_outer & + ) + call conditional_collection_copy( & + adv_tracer_last_outer_dag, & + generic_fields_to_copy=adv_tracer_last_outer, & + field_list=adv_tracer_last_outer & + ) + call conditional_collection_copy( & + con_tracer_all_outer_dag, generic_fields_to_copy=con_tracer_all_outer, & + field_list=con_tracer_all_outer & + ) + call conditional_collection_copy( & + con_tracer_last_outer_dag, & + generic_fields_to_copy=con_tracer_last_outer, & + field_list=con_tracer_last_outer & + ) + + ! ======================================================================== ! + ! DYNAMICS -- TRAPEZOIDAL STEP + ! ======================================================================== ! + + ! X^m + gamma*dt*F[X^m] = T[X^dag - gamma*dt*F[X^n]] + + ! ------------------------------------------------------------------------ ! + ! Compute the semi-implicit operators + ! ------------------------------------------------------------------------ ! + ! Reset the reference state in the semi-implicit operators using the latest + ! value of the state. This occurs every n timesteps, where n is calculated + ! as reference_reset_time divided by dt. + ! The reference_reset_time is specified in the configuration namelist. + ! Note that this reset can only occur at most once per timestep. + reference_reset_freq = nint(reference_reset_time / dt, i_def) + + if (mod(model_clock%get_step()-1, reference_reset_freq) == 0) then + ! Compute semi-implicit operators with current model state + call compute_si_operators_common( & + self%config, self%state(igh_t), self%state(igh_d), & + self%state(igh_p), moist_dyn & + ) + call compute_helmholtz_operators(model_clock, stepper_tr) + call compute_helmholtz_operators(model_clock, stepper_bdf2) + + checkpoint_reference_fields = ( & + mod(model_clock%get_first_step()-1, reference_reset_freq) /= 0 .or. & + mod(model_clock%get_last_step(), reference_reset_freq) /= 0 & + ) + + if (checkpoint_read .or. init_option == init_option_checkpoint_dump) then + ! If the first timestep of this run IS an operator calc timestep, but + ! the first timestep of the next run IS NOT, then checkpoint_flag + ! must be false to allow model to start running, as the operator + ! prognostics will not be in the initial dump + if (mod(model_clock%get_first_step()-1, reference_reset_freq) == 0 & + .and. mod(model_clock%get_last_step(), reference_reset_freq) /= 0) then + checkpoint_reference_fields = .false. + end if + end if + + ! Copy over model state for checkpointing if required + if (checkpoint_reference_fields) then + call prognostic_fields%get_field('theta_ref', theta_ref) + call prognostic_fields%get_field('rho_ref', rho_ref) + call prognostic_fields%get_field('exner_ref', exner_ref) + call moisture_fields%get_field("moist_dyn_ref", moist_dyn_ref_array) + moist_dyn_ref => moist_dyn_ref_array%bundle + call copy_bundle(moist_dyn, moist_dyn_ref, num_moist_factors) + call invoke( & + setval_X(theta_ref, self%state(igh_t)), & + setval_X(rho_ref, self%state(igh_d)), & + setval_X(exner_ref, self%state(igh_p)) & + ) + end if + end if + + ! ------------------------------------------------------------------------ ! + ! Forcings: time-level n + ! ------------------------------------------------------------------------ ! + + ! rhs_n = M[X^dag - gamma*dt*F[X^n]] + call rhs_alg( & + self%config, self%rhs_n, gamma*dt, self%state_dag, self%state_n, & + moist_dyn, compute_eos=.false., compute_rhs_t_d=.true., & + dlayer_rhs=.false., stepper=stepper_tr, model_clock=model_clock & + ) + + ! ======================================================================== ! + ! Outer dynamics loop + ! ======================================================================== ! + + ! Transported variables are set to those after slow physics + call copy_bundle(self%state_dag, self%state_to_adv, bundle_size) + mr_to_adv => self%mr_dag + + ! Set predictors for transport + call predictors_alg( & + self%state_to_adv, self%state_n(igh_u), self%rhs_n(igh_u), & + gamma, model_clock & + ) + + outer_tr_loop: do outer = 1, outer_iterations + + if (use_wavedynamics) then + + call invoke( setval_c(dry_flux_solver, 0.0_r_def) ) + + ! -------------------------------------------------------------------- ! + ! Transport + ! -------------------------------------------------------------------- ! + ! Transport state_dag & mr_dag: put results in rhs_adv & mr + ! Transporting wind is 0.5*(u^n + u^m) + call gungho_transport_control_alg( & + self%config, self%rhs_adv, & + self%state_to_adv, self%state_m(igh_u), self%state_n(igh_u), & + mr, mr_to_adv, model_clock, outer, stepper_tr, & + cheap_update=.false., & + rho_d_n=self%state_dag(igh_d), & + rho_d_n_start=self%state_dag(igh_d), & + total_dry_flux=self%total_dry_flux & + ) + + if (outer == outer_iterations) then + ! Store the transported state for the BDF2 step + ! Need mass matrix solve on u: + call mass_matrix_solver_alg(self%du, self%rhs_adv(igh_u)) + call invoke( & + X_plus_Y( & + self%state_aft_adv(igh_u), self%du, self%state_to_adv(igh_u) & + ), & + X_plus_Y( & + self%state_aft_adv(igh_t), self%rhs_adv(igh_t), & + self%state_to_adv(igh_t) & + ), & + X_plus_Y( & + self%state_aft_adv(igh_d), self%rhs_adv(igh_d), & + self%state_to_adv(igh_d) & + ), & + X_plus_Y( & + self%state_aft_adv(igh_p), self%rhs_adv(igh_p), & + self%state_to_adv(igh_p) & + ) & + ) + end if + + ! Convert theta increment to weak form + call invoke( & + setval_X(self%dtheta, self%rhs_adv(igh_t)), & + setval_c(self%rhs_adv(igh_t), 0.0_r_def), & + dg_inc_matrix_vector_kernel_type( & + self%rhs_adv(igh_t), self%dtheta, mm_wt & + ) & + ) + + else ! No wavedynamics + if (self%use_moisture) call copy_bundle(self%mr_dag, mr, nummr) + call set_bundle_scalar(0.0_r_def, self%rhs_adv, bundle_size) + call copy_bundle(self%state_dag, self%state_aft_adv, bundle_size) + end if + + ! ====================================================================== ! + ! Inner dynamics loop + ! ====================================================================== ! + if (use_wavedynamics) then + + inner_tr_loop: do inner = 1, inner_iterations_tr(outer) + write( log_scratch_space, '(A,2I3)' ) & + 'TR loop indices (o, i): ', outer, inner + call log_event( log_scratch_space, LOG_LEVEL_INFO ) + + ! -------------------------------------------------------------------- + ! Forcings: time-level m + ! -------------------------------------------------------------------- + call rhs_alg( & + self%config, self%rhs_m, -gamma*dt, self%state_m, self%state_m, & + moist_dyn, compute_eos=.true., compute_rhs_t_d=(inner==1), & + dlayer_rhs=dlayer_on, stepper=stepper_tr, & + model_clock=model_clock & + ) + + !--------------------------------------------------------------------- + ! LAM LBCs and RHS + !--------------------------------------------------------------------- + if (limited_area .and. inner == 1 .and. outer == 1) then + call lam_solver_lbc( & + self%state_m(igh_u), lbc_fields, prime_mesh_name & + ) + call calc_rhs_lbc( & + self%rhs_lbc, lbc_fields, model_clock, prime_mesh_name, & + 1.0_r_def, stepper_tr & + ) + end if + + !--------------------------------------------------------------------- + ! Compute the residuals + !--------------------------------------------------------------------- + ! Add on advective terms: rhs = rhs_n - rhs_m + rhs_adv + ! (reuse rhs_m for rhs) + call bundle_axpy( & + -1.0_r_def, self%rhs_m, self%rhs_n, self%rhs_m, bundle_size & + ) + call add_bundle(self%rhs_m, self%rhs_adv, self%rhs_m, bundle_size) + + if (limited_area) then + if (inner == 1 .and. outer == 1) then + ! Add on the RHS for LBCs + call add_bundle(self%rhs_m, self%rhs_lbc, self%rhs_m, bundle_size) + end if + ! Apply masks to RHS + call apply_mask_rhs(self%rhs_m, prime_mesh_name) + end if + + ! Accelerators for inner loop convergence + if (inner > 1) then + call invoke( setval_c(self%rhs_m(igh_d), 0.0_r_def), & + setval_c(self%rhs_m(igh_t), 0.0_r_def) ) + end if + + write_moisture_diag = ( & + self%use_moisture .and. write_conservation_diag & + .and. outer == outer_iterations & + .and. inner == inner_iterations_tr(outer_iterations) & + ) + if (write_moisture_diag) then + ! Calculate transported density + call self%state(igh_d)%copy_field_properties(rho_star) + call invoke( & + X_plus_Y(rho_star, self%state_dag(igh_d), self%rhs_adv(igh_d)) & + ) + call moisture_conservation_alg( & + self%config, rho_star, mr, 'Before TR solve' & + ) + end if + + !--------------------------------------------------------------------- + ! Solve system: A*inc = rhs_m, and increment state by inc + !--------------------------------------------------------------------- + call semi_implicit_solver_alg_step( & + self%state_m, self%rhs_m, & + dry_flux_solver, moist_dyn(gas_law), mr, & + first_iteration=(inner==1), stepper=stepper_tr & + ) + + ! If not already done update factors for moist dynamics + if (self%use_moisture) then + call moist_dyn_factors_alg(moist_dyn, mr) + end if + + if (exner_from_eos) then + call derive_exner_from_eos(self%state_m, moist_dyn(gas_law)) + end if + end do inner_tr_loop + + end if ! use_wavedynamics + end do outer_tr_loop + + ! ======================================================================== ! + ! DYNAMICS -- BACKWARD-DIFFERENCE STEP + ! ======================================================================== ! + + ! X^{n+1} + gamma2*dt*F[X^{n+1}] = T[(1-gamma3)*X^q + gamma3*X^m] + ! where X^q = X_dag + X_aft_adv - X_to_adv + + ! First guess of X^{n+1} is extrapolated from X^m and X^n + call bundle_axpby( & + (1.0_r_def - 0.5_r_def/gamma), self%state_n, & + 0.5_r_def/gamma, self%state_m, & + self%state, bundle_size & + ) + + ! ------------------------------------------------------------------------ ! + ! Compute initial BDF state + ! ------------------------------------------------------------------------ ! + + ! X^q = X_dag + X_aft_adv - X_to_adv + call bundle_axpy( & + -1.0_r_def, self%state_to_adv, self%state_aft_adv, self%state_q, & + bundle_size & + ) + call add_bundle(self%state_q, self%state_dag, self%state_q, bundle_size) + + ! X_bdf = (1-gamma3)*X^q + gamma3*X^m + call bundle_axpby( & + (1.0_r_def - gamma3), self%state_q, gamma3, self%state_m, & + self%state_bdf, bundle_size & + ) + + do_moist_correction = (solver_moisture_conservation .and. self%use_moisture) + if (do_moist_correction) then + call dry_flux_solver_r_tran%initialise(u%get_function_space()) + call invoke( & + inc_a_times_X(gamma3, dry_flux_solver), & + real_to_real_X(dry_flux_solver_r_tran, dry_flux_solver), & + inc_X_plus_Y(self%total_dry_flux, dry_flux_solver_r_tran) & + ) + call solver_moist_correction_alg( & + self%config, mr, self%state_bdf(igh_d), self%state_aft_adv(igh_d), & + dry_flux_solver & + ) + end if + + if (write_moisture_diag) then + call moisture_conservation_alg( & + self%config, self%state_bdf(igh_d), mr, 'After TR solve' & + ) + end if + + ! Note that for mr, the value at the m-level is equal to that at the q-level + ! so we can just use mr as mr_bdf + call copy_bundle(mr, self%mr_bdf, nummr) + mr_to_adv => self%mr_bdf + + ! Compute BDF2 RHS, by multiplying by mass matrices + ! The pressure contribution is set to zero + call invoke( & + name="compute_rhs_bdf_from_state_bdf", & + setval_c(self%rhs_bdf(igh_u), 0.0_r_def), & + matrix_vector_kernel_type( & + self%rhs_bdf(igh_u), self%state_bdf(igh_u), mm_vel & + ), & + enforce_bc_kernel_type(self%rhs_bdf(igh_u)), & + dg_matrix_vector_kernel_type( & + self%rhs_bdf(igh_t), self%state_bdf(igh_t), mm_wt & + ), & + setval_X(self%rhs_bdf(igh_d), self%state_bdf(igh_d)), & + setval_c(self%rhs_bdf(igh_p), 0.0_r_def) & + ) + + ! ======================================================================== ! + ! Outer transport loop + ! ======================================================================== ! + + outer_bdf_loop: do outer = 1, outer_iterations + + if (use_wavedynamics) then + + call invoke( setval_c(dry_flux_solver, 0.0_r_def) ) + + ! -------------------------------------------------------------------- ! + ! Transport + ! -------------------------------------------------------------------- ! + ! Transport state_bdf & mr_bdf: put results in rhs_adv & mr + ! Transporting wind is u^{n+1} + ! Here we also transport tracers over the whole timestep using + ! a transporting wind of 0.5*(u^n + u^{n+1}) + call gungho_transport_control_alg( & + self%config, self%rhs_adv, & + self%state_bdf, self%state(igh_u), self%state_n(igh_u), & + mr, mr_to_adv, model_clock, outer, stepper_bdf2, & + cheap_update, self%adv_inc_prev, self%wind_prev, & + self%state_bdf(igh_d), self%state_dag(igh_d), & + self%total_dry_flux, & + adv_tracer_all_outer, adv_tracer_all_outer_dag, & + adv_tracer_last_outer, adv_tracer_last_outer_dag, & + con_tracer_all_outer, con_tracer_all_outer_dag, & + con_tracer_last_outer, con_tracer_last_outer_dag & + ) + + ! Convert theta increment to weak form + call invoke( & + setval_X(self%dtheta, self%rhs_adv(igh_t)), & + setval_c(self%rhs_adv(igh_t), 0.0_r_def), & + dg_inc_matrix_vector_kernel_type( & + self%rhs_adv(igh_t), self%dtheta, mm_wt & + ) & + ) + + ! ---------------------------------------------------------------------- + ! Forcings: time-level n+1 + ! ---------------------------------------------------------------------- + ! Used for fast physics predictors, so calculated here + call rhs_alg( & + self%config, self%rhs_np1, -gamma2*dt, self%state, self%state, & + moist_dyn, compute_eos=.true., compute_rhs_t_d=.true., & + dlayer_rhs=dlayer_on, stepper=stepper_bdf2, & + model_clock=model_clock & + ) + else ! No wavedynamics + call set_bundle_scalar(0.0_r_def, self%rhs_adv, bundle_size) + if (self%use_moisture) call copy_bundle(self%mr_dag, mr, nummr) + end if + + ! Update the moisture after transport (mr_aft_adv) for diagnostics + if (self%use_moisture .and. outer == outer_iterations) then + call copy_bundle(mr, self%mr_aft_adv, nummr) + end if + + ! ====================================================================== ! + ! FAST PHYSICS + ! ====================================================================== ! + + if (use_physics) then + !----------------------------------------------------------------------- + ! Predictors + !----------------------------------------------------------------------- + call calc_phys_predictors_alg( & + derived_fields, self%rhs_np1, self%rhs_adv, self%rhs_bdf, & + self%state, self%state_dag, lbc_fields, model_clock & + ) + + if (outer == outer_iterations .and. self%output_cld_incs) then + call cld_incs_output( & + cloud_fields, dcfl_adv, dcff_adv, dbcf_adv, sec_adv, suffix_adv & + ) + end if + + call fast_physics( & + self%config, self%du, self%dtheta, mr, self%state_n(igh_t), & + self%state_n(igh_d), self%state_n(igh_u), self%state_n(igh_p), & + self%mr_n, derived_fields, radiation_fields, microphysics_fields, & + orography_fields, turbulence_fields, convection_fields, & + cloud_fields, surface_fields, soil_fields, snow_fields, & + chemistry_fields, aerosol_fields, stph_fields, & + spt_spectral_coeffc, spt_spectral_coeffs, spt_power_law, & + skeb_spectral_coeffc, skeb_spectral_coeffs, skeb_power_law, & + outer, model_clock, dt & + ) + + outer_smagorinsky = ( & + smagorinsky .and. & + smagorinsky_placement == smagorinsky_placement_outer & + ) + if (outer_smagorinsky) then + call smagorinsky_alg( & + self%config, self%dtheta, self%du, mr, & + self%state(igh_t), self%state(igh_u), derived_fields, & + cloud_fields, self%state(igh_d), self%state(igh_p), & + dt, model_clock & + ) + end if + + if (use_wavedynamics) then + ! copy incs into rhs_phys, including multiplication by mass matrix + ! need to reset rhs_phys to 0 because matrix_vector_kernel_type + ! increments the field rather than over-writing it + call set_bundle_scalar(0.0_r_def, self%rhs_phys, bundle_size) + call invoke( & + name="update_rhs_phys_from_fast_physics", & + dg_inc_matrix_vector_kernel_type( & + self%rhs_phys(igh_t), self%dtheta, mm_wt & + ), & + matrix_vector_kernel_type( & + self%rhs_phys(igh_u), self%du, mm_vel), & + enforce_bc_kernel_type(self%rhs_phys(igh_u)) & + ) + end if + end if ! use_physics + + ! ====================================================================== ! + ! Inner dynamics loop + ! ====================================================================== ! + if (use_wavedynamics) then + + inner_bdf_loop: do inner = 1, inner_iterations_bdf2(outer) + write( log_scratch_space, '(A,2I3)' ) & + 'BDF loop indices (o, i): ', outer, inner + call log_event( log_scratch_space, LOG_LEVEL_INFO ) + + ! -------------------------------------------------------------------- + ! Forcings: time-level n+1 + ! -------------------------------------------------------------------- + if (inner > 1) then + call rhs_alg( & + self%config, self%rhs_np1, -gamma2*dt, self%state, self%state, & + moist_dyn, compute_eos=.true., compute_rhs_t_d=.false., & + dlayer_rhs=dlayer_on, stepper=stepper_bdf2, & + model_clock=model_clock & + ) + end if + + !--------------------------------------------------------------------- + ! LAM LBCs and RHS + !--------------------------------------------------------------------- + if (limited_area .and. inner == 1 .and. outer == 1) then + call lam_solver_lbc( & + self%state(igh_u), lbc_fields, prime_mesh_name & + ) + call calc_rhs_lbc( & + self%rhs_lbc, lbc_fields, model_clock, prime_mesh_name, & + 1.0_r_def, stepper_bdf2 & + ) + end if + + !--------------------------------------------------------------------- + ! Compute the residuals + !--------------------------------------------------------------------- + ! Add on advective terms: rhs = rhs_bdf - rhs_np1 + rhs_adv + rhs_phys + ! (reuse rhs_np1 for rhs) + call bundle_axpy( & + -1.0_r_def, self%rhs_np1, self%rhs_bdf, self%rhs_np1, & + bundle_size & + ) + call add_bundle(self%rhs_np1, self%rhs_adv, self%rhs_np1, bundle_size) + call add_bundle(self%rhs_np1, self%rhs_phys, self%rhs_np1, bundle_size) + + if (limited_area) then + if (inner == 1 .and. outer == 1) then + ! Add on the RHS for LBCs + call add_bundle( & + self%rhs_np1, self%rhs_lbc, self%rhs_np1, bundle_size & + ) + end if + ! Apply masks to RHS + call apply_mask_rhs(self%rhs_np1, prime_mesh_name) + end if + + ! Accelerators for inner loop convergence + if (inner > 1) then + call invoke( setval_c(self%rhs_np1(igh_d), 0.0_r_def), & + setval_c(self%rhs_np1(igh_t), 0.0_r_def) ) + end if + + write_moisture_diag = ( & + self%use_moisture .and. write_conservation_diag & + .and. outer == outer_iterations & + .and. inner == inner_iterations_bdf2(outer_iterations) & + ) + if (write_moisture_diag) then + ! Calculate transported density + call self%state(igh_d)%copy_field_properties(rho_star) + call invoke( & + X_plus_Y(rho_star, self%state_bdf(igh_d), self%rhs_adv(igh_d)) & + ) + call moisture_conservation_alg( & + self%config, rho_star, mr, 'Before BDF solve' & + ) + end if + + !--------------------------------------------------------------------- + ! Solve system: A*inc = rhs_np1, and increment state by inc + !--------------------------------------------------------------------- + call semi_implicit_solver_alg_step( & + self%state, self%rhs_np1, & + dry_flux_solver, moist_dyn(gas_law), mr, & + first_iteration=(inner==1), stepper=stepper_bdf2 & + ) + + ! If not already done update factors for moist dynamics + if (self%use_moisture) then + call moist_dyn_factors_alg(moist_dyn, mr) + end if + + if (exner_from_eos) then + call derive_exner_from_eos(self%state, moist_dyn(gas_law)) + end if + + !--------------------------------------------------------------------- + ! LAM Overwrite and Blend LBCs + !--------------------------------------------------------------------- + if (limited_area) then + to_blend_lbc = ( & + blend_frequency == blend_frequency_inner .or. ( & + blend_frequency == blend_frequency_outer & + .and. inner == inner_iterations_bdf2(outer_iterations) & + ) .or. ( & + blend_frequency == blend_frequency_final & + .and. inner == inner_iterations_bdf2(outer_iterations) & + .and. outer == outer_iterations & + ) & + ) + if (to_blend_lbc) then + call lam_blend_lbc( & + self%state(igh_u), self%state(igh_p), self%state(igh_d), & + self%state(igh_t), mr, lbc_fields, microphysics_fields, & + aerosol_fields, lbc_option, microphysics_casim, murk_lbc, & + moisture_formulation, prime_mesh_name & + ) + end if + end if + end do inner_bdf_loop + + do_moist_correction = ( & + solver_moisture_conservation .and. self%use_moisture & + .and. outer == outer_iterations & + ) + if (do_moist_correction) then + call invoke( & + X_plus_Y(rho_star, self%rhs_adv(igh_d), self%state_bdf(igh_d)) & + ) + call solver_moist_correction_alg( & + self%config, mr, self%state(igh_d), rho_star, dry_flux_solver & + ) + end if + + if (write_moisture_diag) then + call moisture_conservation_alg( & + self%config, self%state(igh_d), mr, 'After BDF solve' & + ) + end if + + else ! when use_wavedynamics=false, just add physics increments here + + call invoke( & + X_plus_Y(self%state(igh_t), self%state_dag(igh_t), self%dtheta), & + X_plus_Y(self%state(igh_u), self%state_dag(igh_u), self%du) & + ) + end if ! use_wavedynamics + end do outer_bdf_loop + + ! ======================================================================== ! + ! END-OF-STEP PHYSICS + ! ======================================================================== ! + + call adv_tracer_all_outer_dag%clear() + call adv_tracer_last_outer_dag%clear() + call con_tracer_all_outer_dag%clear() + call con_tracer_last_outer_dag%clear() + + if (transport_ageofair) then + call con_tracer_last_outer%get_field('ageofair', ageofair) + call ageofair_update(ageofair, model_clock) + end if + + !--------------------------------------------------------------------------- + ! Apply mixing + !--------------------------------------------------------------------------- + call mixing_alg( & + self%config, mr, self%state(igh_t), self%state(igh_u), derived_fields, & + cloud_fields, self%state(igh_d), self%state(igh_p), dt, model_clock & + ) + + ! -------------------------------------------------------------------------- + ! Call cloud scheme to generate cloud and latent heating after pressure + ! changes are applied from the solver. + ! -------------------------------------------------------------------------- +#ifdef UM_PHYSICS + if (use_physics .and. cloud == cloud_um) then + call cld_alg( & + self%config, self%dtheta_cld, mr, self%state(igh_t), & + self%state(igh_p), self%state(igh_d), derived_fields, & + turbulence_fields, cloud_fields, convection_fields, & + self%state_n(igh_t), self%mr_n, & + model_clock%get_step(), dt, .false. & + ) + call invoke(inc_X_plus_Y(self%state(igh_t), self%dtheta_cld)) + + if (l_mcr_precfrac) then + call microphysics_fields%get_field('precfrac', precfrac) + call invoke( & + lsp_precfrac_checks_kernel_type( & + exner, mr(imr_v), mr(imr_cl), mr(imr_ci), & + mr(imr_s), mr(imr_r), mr(imr_g), precfrac & + ) & + ) + end if + + if (microphysics_casim) then + call casim_activate_alg( & + self%config, self%state(igh_t), mr, derived_fields, cloud_fields, & + microphysics_fields, convection_fields, initialise=.false. & + ) + end if ! microphysics_casim + end if +#endif + + ! Update derived variables for time level n+1 + if (self%use_moisture) then + call moist_dyn_factors_alg(moist_dyn, mr) + end if + if (use_physics) then + call map_physics_fields_alg( & + self%config, self%state(igh_u), self%state(igh_p), & + self%state(igh_d), self%state(igh_t), moist_dyn, derived_fields & + ) + end if + + !---------------------------------------------------------------------------- + ! Call UKCA for GLOMAP-mode prognostic aerosol updates + !--------------------------------------------------------------------------- +#ifdef UM_PHYSICS + if (aerosol == aerosol_um) then + glomap_mode = self%config%aerosol%glomap_mode() + end if + if (aerosol == aerosol_um .and. & + (glomap_mode == glomap_mode_ukca .or. & + glomap_mode == glomap_mode_dust_and_clim)) then + call aerosol_ukca_alg( & + self%config, modeldb%values, chemistry_fields, aerosol_fields, & + radiation_fields, derived_fields, microphysics_fields, & + turbulence_fields, convection_fields, cloud_fields, surface_fields, & + soil_fields, self%state, mr, model_clock & + ) + end if +#endif + + ! Write diagnostic output + if (use_physics .and. self%output_cld_incs) then + call cld_incs_output( & + cloud_fields, dcfl_tot, dcff_tot, dbcf_tot, sec_tot, suffix_tot & + ) + end if + if (write_diag .and. use_xios_io .and. & + mod(model_clock%get_step(), diagnostic_frequency) == 0) then + call output_diags_for_si( & + self%state, self%state_n, self%state_dag, & + mr, self%mr_n, self%mr_dag, self%mr_aft_adv, & + derived_fields, self%du, self%dtheta, self%dtheta_cld & + ) + end if + + !--------------------------------------------------------------------------- + ! Update fields held in the driver layer + !--------------------------------------------------------------------------- + call invoke( setval_X(u, self%state(igh_u)), & + setval_X(theta, self%state(igh_t)), & + setval_X(rho, self%state(igh_d)), & + setval_X(exner, self%state(igh_p)) ) + + deallocate(inner_iterations_tr) + deallocate(inner_iterations_bdf2) + + if (LPROF) call stop_timing(id, 'tr_bdf2_timestep_alg') + + end subroutine run_step + + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !> Release all claimed resources once completed. + subroutine tr_bdf2_alg_final(self) + + implicit none + + class(tr_bdf2_timestep_type), intent(inout) :: self + + call semi_implicit_solver_alg_final() + call final_si_operators() + + if (allocated(self%state)) deallocate(self%state) + if (allocated(self%state_n)) deallocate(self%state_n) + if (allocated(self%state_m)) deallocate(self%state_m) + if (allocated(self%state_bdf)) deallocate(self%state_bdf) + if (allocated(self%state_q)) deallocate(self%state_q) + if (allocated(self%state_aft_adv)) deallocate(self%state_aft_adv) + if (allocated(self%state_dag)) deallocate(self%state_dag) + if (allocated(self%state_to_adv)) deallocate(self%state_to_adv) + if (allocated(self%rhs_n)) deallocate(self%rhs_n) + if (allocated(self%rhs_m)) deallocate(self%rhs_m) + if (allocated(self%rhs_bdf)) deallocate(self%rhs_bdf) + if (allocated(self%rhs_np1)) deallocate(self%rhs_np1) + if (allocated(self%rhs_adv)) deallocate(self%rhs_adv) + if (allocated(self%rhs_phys)) deallocate(self%rhs_phys) + if (allocated(self%rhs_lbc)) deallocate(self%rhs_lbc) + if (allocated(self%adv_inc_prev)) deallocate(self%adv_inc_prev) + if (allocated(self%mr_n)) deallocate(self%mr_n) + if (allocated(self%mr_dag)) deallocate(self%mr_dag) + if (allocated(self%mr_bdf)) deallocate(self%mr_bdf) + if (allocated(self%mr_aft_adv)) deallocate(self%mr_aft_adv) + + call self%dtheta%field_final() + call self%du%field_final() + call self%total_dry_flux%field_final() + + return + end subroutine tr_bdf2_alg_final + +end module tr_bdf2_timestep_alg_mod diff --git a/science/gungho/source/algorithm/transport/control/consistent_wind_alg_mod.X90 b/science/gungho/source/algorithm/transport/control/consistent_wind_alg_mod.X90 index 573727030f..edab78a724 100644 --- a/science/gungho/source/algorithm/transport/control/consistent_wind_alg_mod.X90 +++ b/science/gungho/source/algorithm/transport/control/consistent_wind_alg_mod.X90 @@ -19,6 +19,7 @@ module consistent_wind_alg_mod get_height_fv, & get_face_selector_ew, & get_face_selector_ns + use dycore_constants_mod, only: stepper_siqn, stepper_tr use consistent_wind_kernel_mod, only: consistent_wind_kernel_type use fs_continuity_mod, only: Wtheta, W2v, W2h use model_clock_mod, only: model_clock_type @@ -59,6 +60,7 @@ contains !> @param[in] wind_np1 Wind at the end of the step !> @param[in] model_clock Model time information !> @param[in] outer Outer iteration index + !> @param[in] stepper Enumerator of timestepper !> @param[in] cheap_update_step Whether a cheap transport update is used subroutine consistent_wind_alg(config, & transport_controller, & @@ -67,12 +69,14 @@ contains wind_np1, & model_clock, & outer, & + stepper, & cheap_update_step) implicit none type(config_type), intent(in) :: config integer(kind=i_def), intent(in) :: outer + integer(kind=i_def), intent(in) :: stepper logical(kind=l_def), intent(in) :: cheap_update_step class(model_clock_type), intent(in) :: model_clock type(field_type), intent(in) :: rho_d_latest, wind_n, wind_np1 @@ -123,8 +127,14 @@ contains call wind_adv%initialise( wind_n%get_function_space() ) face_selector_ew => get_face_selector_ew( wind_n%get_mesh() ) face_selector_ns => get_face_selector_ns( wind_n%get_mesh() ) - call invoke( aX_plus_bY( wind_adv, 0.5_r_def, wind_n, 0.5_r_def, wind_np1 ), & - split_w2_field_kernel_type( wind_h, wind_v, wind_adv, & + + if (stepper == stepper_siqn .or. stepper == stepper_tr) then + call invoke( aX_plus_bY( wind_adv, 0.5_r_def, wind_n, & + 0.5_r_def, wind_np1 ) ) + else + call invoke( setval_X( wind_adv, wind_np1) ) + end if + call invoke( split_w2_field_kernel_type( wind_h, wind_v, wind_adv, & face_selector_ew, & face_selector_ns ) ) @@ -160,7 +170,8 @@ contains wind_adv_ptr => wind_adv call transport_controller%finalise() call transport_controller%initialise( & - config, model_clock, rho_d_latest, wind_adv, wind_adv_ptr, outer, & + config, model_clock, stepper, & + rho_d_latest, wind_adv, wind_adv_ptr, outer, & cheap_update_step & ) end subroutine consistent_wind_alg diff --git a/science/gungho/source/algorithm/transport/control/gungho_transport_control_alg_mod.X90 b/science/gungho/source/algorithm/transport/control/gungho_transport_control_alg_mod.X90 index b602a895f6..64e733cb7f 100644 --- a/science/gungho/source/algorithm/transport/control/gungho_transport_control_alg_mod.X90 +++ b/science/gungho/source/algorithm/transport/control/gungho_transport_control_alg_mod.X90 @@ -8,35 +8,40 @@ module gungho_transport_control_alg_mod use config_mod, only: config_type - use constants_mod, only: i_def, r_def, l_def, r_tran, & + use constants_mod, only: i_def, r_def, l_def, r_tran, & PRECISION_R_TRAN + use dycore_constants_mod, only: stepper_siqn, stepper_tr, & + stepper_bdf2 use extrusion_mod, only: SHIFTED use field_mod, only: field_type use flux_precomputations_alg_mod, only: flux_precomputations_type - use formulation_config_mod, only: moisture_formulation, & - moisture_formulation_dry, & + use formulation_config_mod, only: moisture_formulation, & + moisture_formulation_dry, & use_multires_coupling use local_mesh_mod, only: local_mesh_type - use log_mod, only: log_event, & - log_scratch_space, & - LOG_LEVEL_DEBUG, & - LOG_LEVEL_ERROR, & + use log_mod, only: log_event, & + log_scratch_space, & + LOG_LEVEL_DEBUG, & + LOG_LEVEL_ERROR, & LOG_LEVEL_ALWAYS use mesh_collection_mod, only: mesh_collection use mesh_mod, only: mesh_type - use multires_coupling_config_mod, only: coarse_aerosol_transport, & + use multires_coupling_config_mod, only: coarse_aerosol_transport, & aerosol_mesh_name use r_tran_field_mod, only: r_tran_field_type - use transport_config_mod, only: transport_ageofair, & - consistent_metric + use transport_config_mod, only: transport_ageofair, & + consistent_metric, & + si_outer_transport, & + si_outer_transport_advective, & + si_outer_transport_horizontal_sl use transport_controller_mod, only: transport_controller_type use transport_metadata_mod, only: transport_metadata_type - use transport_metadata_collection_mod, only: transport_metadata_collection_type, & - transport_metadata_collection - use check_configuration_mod, only: check_any_shifted, & - check_transport_name, & + use transport_metadata_collection_mod, only: transport_metadata_collection, & + transport_metadata_collection_type + use check_configuration_mod, only: check_any_shifted, & + check_transport_name, & check_any_eqn_consistent - use timing_mod, only: start_timing, stop_timing, & + use timing_mod, only: start_timing, stop_timing, & tik, LPROF implicit none @@ -149,6 +154,7 @@ contains !> @param[in] mr_in Moisture fields before transport !> @param[in] model_clock Time within the model !> @param[in] outer Outer (advection) iteration number + !> @param[in] stepper Enumerator for timestepper !> @param[in] cheap_update Logical flag for cheap transport update !> @param[in] prev_inc (Optional) Advective increment after 1st outer !! iteration transport used for cheap transport update @@ -156,6 +162,8 @@ contains !! previous outer iteration !> @param[in] rho_d_n (Optional) dry density to be transported, !! without the predictor factor + !> @param[in] rho_d_n_start (Optional) dry density at start of timestep, + !! for use with TR-BDF2 timestepper !> @param[in] total_dry_flux (Optional) the mass flux used to transport !! the dry density !> @param[in,out] adv_tracer_all_outer @@ -178,9 +186,9 @@ contains subroutine gungho_transport_control_alg(config, & advection_inc, advected_fields, & wind_np1, wind_n, mr_out, mr_in, & - model_clock, outer, & + model_clock, outer, stepper, & cheap_update, prev_inc, wind_prev, & - rho_d_n, & + rho_d_n, rho_d_n_start, & total_dry_flux, & adv_tracer_all_outer, & adv_tracer_all_outer_after_slow, & @@ -229,10 +237,12 @@ contains type(field_type), intent(in) :: mr_in(nummr) class(model_clock_type), intent(in) :: model_clock integer(kind=i_def), intent(in) :: outer + integer(kind=i_def), intent(in) :: stepper logical(kind=l_def), intent(in) :: cheap_update type(field_type), intent(in), optional :: prev_inc(bundle_size) type(field_type), intent(in), optional :: wind_prev type(field_type), target, intent(in), optional :: rho_d_n + type(field_type), intent(in), optional :: rho_d_n_start type(r_tran_field_type), intent(in), optional :: total_dry_flux type(field_collection_type), intent(inout), optional :: adv_tracer_all_outer type(field_collection_type), intent(in), optional :: adv_tracer_all_outer_after_slow @@ -254,6 +264,7 @@ contains type(field_type) :: rho_d_np1 type(field_type) :: aerosol_wind_n, aerosol_wind_np1 type(field_type) :: aerosol_reference_rho + type(r_tran_field_type) :: tr_and_bdf2_flux type(field_type), pointer :: rho_d_n_ptr type(field_type), target :: rho_d_n_trgt type(field_type), pointer :: rho_d_latest_ptr @@ -297,6 +308,12 @@ contains ! Determine aspects relating to cheap update steps ------------------------- ! Check whether it is a cheap update step cheap_update_step = (cheap_update .and. outer > 1) + if (cheap_update .and. .not. stepper == stepper_siqn) then + call log_event( & + 'Cheap transport update not implemented for TR-BDF2 stepper', & + LOG_LEVEL_ERROR & + ) + end if call wind_for_adv%initialise( wind_n%get_function_space() ) if (cheap_update .AND. outer > 2 .AND. present(wind_prev) ) then @@ -317,8 +334,9 @@ contains end if ! Initialise the main transport controller --------------------------------- - call transport_controller%initialise(config, & - model_clock, rho_d_latest_ptr, wind_for_adv, wind_np1, outer, & + call transport_controller%initialise( & + config, model_clock, stepper, & + rho_d_latest_ptr, wind_for_adv, wind_np1, outer, & cheap_update_step & ) @@ -334,6 +352,7 @@ contains wind_np1, & model_clock, & outer, & + stepper, & cheap_update_step) ! ------------------------------------------------------------------------ ! @@ -351,10 +370,26 @@ contains ! Increment the whole time step's dry flux, which is to be used in the ! transport of tracers in the final outer step, if using the cheap update - if (cheap_update) then + ! or the TR-BDF2 timestepper + if (cheap_update .or. ( & + stepper == stepper_bdf2 .and. outer == outer_iterations) & + ) then flux_precomputations => transport_controller%get_flux_precomputations() flux_this_outer => flux_precomputations%get_total_ref_flux() call invoke( inc_X_plus_Y(total_dry_flux, flux_this_outer) ) + + ! If in BDF2 stage of TR-BDF2, but not last outer, need to store flux + else if (stepper == stepper_bdf2 .and. outer < outer_iterations) then + flux_precomputations => transport_controller%get_flux_precomputations() + flux_this_outer => flux_precomputations%get_total_ref_flux() + call tr_and_bdf2_flux%initialise(total_dry_flux%get_function_space()) + call invoke( X_plus_Y(tr_and_bdf2_flux, total_dry_flux, flux_this_outer) ) + + ! Store dry flux in final outer iteration of TR + else if (stepper == stepper_tr .and. outer == outer_iterations) then + flux_precomputations => transport_controller%get_flux_precomputations() + flux_this_outer => flux_precomputations%get_total_ref_flux() + call invoke( setval_X(total_dry_flux, flux_this_outer) ) end if ! Check negative reference fields at this point @@ -398,9 +433,50 @@ contains mr_out, mr_in, nummr_to_transport, & transport_controller, transport_metadata & ) - ! ---------------------------------------------------------------------- ! - ! Transport tracers which are active in fast physics, and therefore need - ! transporting on every outer iteration + end if + + ! ------------------------------------------------------------------------ ! + ! Transport potential temperature + ! (must be after moisture but before last_outer tracers due to cheap update) + ! Note: the theta increment is pointwise + call log_event( "Transporting potential temperature...", LOG_LEVEL_DEBUG) + transport_metadata => & + transport_metadata_collection%get_transport_metadata('potential_temperature') + + call theta_transport_alg( & + fields_np1(igh_t), advection_inc(igh_t), advected_fields(igh_t), & + mr_out, mr_in, transport_controller, transport_metadata & + ) + + ! ------------------------------------------------------------------------ ! + ! Transport tracers which are active in fast physics, and therefore need + ! transporting on every outer iteration + ! Not transported in TR step of TR-BDF2 + if ( moisture_formulation /= moisture_formulation_dry ) then + + if (stepper == stepper_bdf2) then + ! Reset transport controller -- transport is done over whole timestep + ! Set stepper to SIQN to use time-averaged wind + call transport_controller%finalise() + call transport_controller%initialise( & + config, model_clock, stepper_siqn, & + rho_d_n_start, wind_n, wind_np1, outer=outer, & + cheap_update_step=.false. & + ) + if (outer == outer_iterations) then + ! Dry flux is flux built up over whole timestep + call transport_controller%initialise_flux_precomputations(total_dry_flux) + else if (.not. ( & + si_outer_transport == si_outer_transport_advective & + .or. si_outer_transport == si_outer_transport_horizontal_sl & + )) then + ! Dry flux is the stored TR flux plus the latest BDF flux + call transport_controller%initialise_flux_precomputations(tr_and_bdf2_flux) + end if + ! Otherwise, not doing conservative tracer transport so don't initialise + ! flux precomputations + end if + if (present(adv_tracer_all_outer) & .and. check_transport_name('adv_tracer') ) then call log_event( & @@ -429,19 +505,6 @@ contains end if end if - ! ------------------------------------------------------------------------ ! - ! Transport potential temperature - ! (must be after moisture but before last_outer tracers due to cheap update) - ! Note: the theta increment is pointwise - call log_event( "Transporting potential temperature...", LOG_LEVEL_DEBUG) - transport_metadata => & - transport_metadata_collection%get_transport_metadata('potential_temperature') - - call theta_transport_alg( & - fields_np1(igh_t), advection_inc(igh_t), advected_fields(igh_t), & - mr_out, mr_in, transport_controller, transport_metadata & - ) - ! ======================================================================== ! ! TRANSPORT OF PROGNOSTICS: LAST OUTER LOOP ! ======================================================================== ! @@ -467,12 +530,13 @@ contains wind_np1, & model_clock, & outer, & + stepper_siqn, & cheap_update_step) else call transport_controller%finalise() call transport_controller%initialise( & - config, & - model_clock, rho_d_n_ptr, wind_n, wind_np1, & + config, model_clock, stepper_siqn, & + rho_d_n_ptr, wind_n, wind_np1, & outer=outer, cheap_update_step=.false. & ) end if @@ -496,8 +560,8 @@ contains ! Set up new transport controller for coarser mesh call aerosol_transport_controller%initialise( & - config, & - model_clock, aerosol_reference_rho, aerosol_wind_n, & + config, model_clock, stepper_siqn, & + aerosol_reference_rho, aerosol_wind_n, & aerosol_wind_np1, outer, cheap_update_step=.false. & ) ! Copy dry density flux for the whole timestep into flux precomputations diff --git a/science/gungho/source/algorithm/transport/control/transport_controller_mod.x90 b/science/gungho/source/algorithm/transport/control/transport_controller_mod.x90 index 819cd599db..38442f7ee1 100644 --- a/science/gungho/source/algorithm/transport/control/transport_controller_mod.x90 +++ b/science/gungho/source/algorithm/transport/control/transport_controller_mod.x90 @@ -42,6 +42,8 @@ module transport_controller_mod use sci_geometric_constants_mod, only: get_face_selector_ew, & get_face_selector_ns use transport_constants_mod, only: get_detj_at_w3_r_tran + use dycore_constants_mod, only: stepper_siqn, stepper_tr, & + stepper_bdf2, stepper_rk, gamma ! Transport objects use flux_precomputations_alg_mod, only: flux_precomputations_type @@ -141,6 +143,7 @@ contains !! - initialising precomputation objects !> @param[in] config Application namelist configuration object !> @param[in] model_clock Tracks the time within the model + !> @param[in] stepper Enumerator for timestepper !> @param[in] ref_field_rdef The reference density to use for conservative !! transport of tracers. This argument must be !! specified to perform conservative transport @@ -158,7 +161,7 @@ contains !! the transporting wind is the *difference* !! in wind from the previous step, rather than !! the full transport wind. - subroutine initialise(self, config, model_clock, ref_field_rdef, & + subroutine initialise(self, config, model_clock, stepper, ref_field_rdef, & wind_n_rdef, wind_np1_rdef, outer, cheap_update_step) use split_transport_utils_mod, only: get_dry_config, & @@ -178,6 +181,7 @@ contains class(transport_controller_type), intent(inout) :: self class(model_clock_type), target, intent(in) :: model_clock + integer(kind=i_def), intent(in) :: stepper type(field_type), intent(in) :: ref_field_rdef type(field_type), intent(in) :: wind_n_rdef type(field_type), optional, intent(in) :: wind_np1_rdef @@ -231,7 +235,20 @@ contains self%cheap_update_step = cheap_update_step end if - dt_rtran = real(model_clock%get_seconds_per_step(), r_tran) + ! Determine time increment for this step, based on the stepper + select case (stepper) + case (stepper_siqn, stepper_rk) + dt_rtran = real(model_clock%get_seconds_per_step(), r_tran) + case (stepper_tr) + dt_rtran = real(model_clock%get_seconds_per_step(), r_tran) + dt_rtran = 2.0_r_tran*real(gamma, r_tran)*dt_rtran + case (stepper_bdf2) + dt_rtran = real(model_clock%get_seconds_per_step(), r_tran) + dt_rtran = (1.0_r_tran - 2.0_r_tran*real(gamma, r_tran))*dt_rtran + case default + call log_event( "Unknown stepper passed to transport_controller " // & + "initialise method", LOG_LEVEL_ERROR ) + end select self%reference_splitting = splitting(get_dry_config()) @@ -274,12 +291,23 @@ contains self%wind_n_rtran), & inc_a_times_X(0.5_r_tran, self%transporting_wind) ) - ! standard semi-implicit transport wind: average of wind_n and wind_np1 else if (present(wind_np1_rdef)) then - call invoke( X_plus_Y(self%transporting_wind, self%wind_np1_rtran, & - self%wind_n_rtran), & - inc_a_times_X(0.5_r_tran, self%transporting_wind) ) - + select case (stepper) + case (stepper_rk) + ! Use wind_n as transporting wind + call invoke( setval_X(self%transporting_wind, self%wind_n_rtran) ) + case (stepper_siqn, stepper_tr) + ! standard semi-implicit transport wind: average of wind_n and wind_np1 + call invoke( X_plus_Y(self%transporting_wind, self%wind_np1_rtran, & + self%wind_n_rtran), & + inc_a_times_X(0.5_r_tran, self%transporting_wind) ) + case (stepper_bdf2) + ! Use wind_np1 as transporting wind + call invoke( setval_X(self%transporting_wind, self%wind_np1_rtran) ) + case default + call log_event( "Unknown stepper passed to transport_controller " // & + "initialise method", LOG_LEVEL_ERROR ) + end select else call invoke( setval_X(self%transporting_wind, self%wind_n_rtran) ) end if diff --git a/science/gungho/source/configuration/check_configuration_mod.F90 b/science/gungho/source/configuration/check_configuration_mod.F90 index c97c6911b3..1a07ff871b 100644 --- a/science/gungho/source/configuration/check_configuration_mod.F90 +++ b/science/gungho/source/configuration/check_configuration_mod.F90 @@ -142,11 +142,13 @@ subroutine check_configuration(modeldb) scaling_factor use timestepping_config_mod, only: method, & method_semi_implicit, & + method_tr_bdf2, & dt, & alpha, & outer_iterations, & - inner_iterations - + inner_iterations_si, & + inner_iterations_tr, & + inner_iterations_bdf2 use mesh_mod, only: geometry_spherical, & geometry_planar, & topology_periodic, & @@ -269,13 +271,57 @@ subroutine check_configuration(modeldb) outer_iterations call log_event( log_scratch_space, LOG_LEVEL_ERROR ) end if - if ( inner_iterations < 1 ) then - write( log_scratch_space, '(A,I4)' ) 'Invalid Choice: inner_iterations must be at least 1:', & - inner_iterations + if ( SIZE(inner_iterations_si) /= outer_iterations ) then + write( log_scratch_space, '(A,I4,A,I4)' ) 'Invalid Options: ' // & + 'inner_iterations_si has size ', SIZE(inner_iterations_si), & + ', but must have the same size as outer_iterations:', & + outer_iterations + call log_event( log_scratch_space, LOG_LEVEL_ERROR ) + end if + if ( minval(inner_iterations_si) < 1 ) then + write( log_scratch_space, '(A,I4)' ) & + 'Invalid Choice: inner_iterations_si must be at least 1:', & + minval(inner_iterations_si) + call log_event( log_scratch_space, LOG_LEVEL_ERROR ) + end if + + else if ( method == method_tr_bdf2 ) then + if ( outer_iterations < 1 ) then + write( log_scratch_space, '(A,I4)' ) & + 'Invalid Choice: outer_iterations must be at least 1:', & + outer_iterations + call log_event( log_scratch_space, LOG_LEVEL_ERROR ) + end if + if ( SIZE(inner_iterations_tr) /= outer_iterations ) then + write( log_scratch_space, '(A,I4,A,I4)' ) 'Invalid Options: ' // & + 'inner_iterations_tr has size ', SIZE(inner_iterations_tr), & + ', but must have the same size as outer_iterations:', & + outer_iterations + call log_event( log_scratch_space, LOG_LEVEL_ERROR ) + end if + if ( SIZE(inner_iterations_bdf2) /= outer_iterations ) then + write( log_scratch_space, '(A,I4,A,I4)' ) 'Invalid Options: ' // & + 'inner_iterations_bdf2 has size ', SIZE(inner_iterations_bdf2), & + ', but must have the same size as outer_iterations:', & + outer_iterations + call log_event( log_scratch_space, LOG_LEVEL_ERROR ) + end if + if ( minval(inner_iterations_tr) < 1 ) then + write( log_scratch_space, '(A,I4)' ) & + 'Invalid Choice: inner_iterations_tr must be at least 1:', & + minval(inner_iterations_tr) + call log_event( log_scratch_space, LOG_LEVEL_ERROR ) + end if + if ( minval(inner_iterations_bdf2) < 1 ) then + write( log_scratch_space, '(A,I4)' ) & + 'Invalid Choice: inner_iterations_bdf2 must be at least 1:', & + minval(inner_iterations_bdf2) call log_event( log_scratch_space, LOG_LEVEL_ERROR ) end if end if + + ! Check the transport namelist any_scheme_mol = check_any_scheme_mol() if (any_scheme_mol) then diff --git a/science/gungho/source/driver/gungho_model_mod.F90 b/science/gungho/source/driver/gungho_model_mod.F90 index 24985b6cb2..1bd07b8ff2 100644 --- a/science/gungho/source/driver/gungho_model_mod.F90 +++ b/science/gungho/source/driver/gungho_model_mod.F90 @@ -90,6 +90,7 @@ module gungho_model_mod use rk_alg_timestep_mod, only : rk_timestep_type use semi_implicit_timestep_alg_mod, & only : semi_implicit_timestep_type + use tr_bdf2_timestep_alg_mod, only : tr_bdf2_timestep_type use setup_orography_alg_mod, only : setup_orography_alg use idealised_config_mod, only : perturb_init, perturb_seed use initial_temperature_config_mod, only : perturb, perturb_random @@ -1162,6 +1163,7 @@ subroutine initialise_model( mesh, modeldb ) use timestepping_config_mod, only: method, & method_semi_implicit, & method_rk, & + method_tr_bdf2, & method_no_timestepping, & method_jules @@ -1212,42 +1214,18 @@ subroutine initialise_model( mesh, modeldb ) case( method_semi_implicit ) ! Semi-Implicit ! Initialise the semi-implicit timestep method allocate( timestep_method, source=semi_implicit_timestep_type(modeldb) ) - ! Add to the model database - call modeldb%values%add_key_value('timestep_method', & - timestep_method) - ! Output initial conditions - if ( write_conservation_diag ) then - call conservation_algorithm( modeldb%config, rho, u, theta, & - mr, exner ) - if ( use_moisture ) then - call moisture_conservation_alg( modeldb%config, rho, mr, & - 'Before timestep' ) - end if - end if + + case( method_tr_bdf2 ) ! TR-BDF2 + ! Initialise the TR-BDF2 timestep method + allocate( timestep_method, source=tr_bdf2_timestep_type(modeldb) ) case( method_rk ) ! RK ! Initialise the Runge-Kutta timestep method allocate( timestep_method, source=rk_timestep_type(modeldb) ) - ! Add to the model database - call modeldb%values%add_key_value('timestep_method', & - timestep_method) - - ! Output initial conditions - if ( write_conservation_diag ) then - call conservation_algorithm( modeldb%config, rho, u, theta, & - mr, exner ) - if ( use_moisture ) then - call moisture_conservation_alg( modeldb%config, rho, mr, & - 'Before timestep' ) - end if - end if case( method_no_timestepping ) ! Initialise a null-timestep method allocate( timestep_method, source=no_timestep_type() ) - ! Add to the model database - call modeldb%values%add_key_value('timestep_method', & - timestep_method) write( log_scratch_space, & '(A, A)' ) 'CAUTION: Running with no timestepping. ' // & ' Prognostic fields not evolved' @@ -1257,9 +1235,6 @@ subroutine initialise_model( mesh, modeldb ) case( method_jules ) ! jules ! Initialise the jules timestep method allocate( timestep_method, source=jules_timestep_type(modeldb) ) - ! Add to the model database - call modeldb%values%add_key_value('timestep_method', & - timestep_method) #endif case default @@ -1267,6 +1242,17 @@ subroutine initialise_model( mesh, modeldb ) "stopping program! ",LOG_LEVEL_ERROR) end select + ! Add to the model database + call modeldb%values%add_key_value('timestep_method', timestep_method) + + ! Output initial conditions + if ( write_conservation_diag ) then + call conservation_algorithm(modeldb%config, rho, u, theta, mr, exner) + if ( use_moisture ) then + call moisture_conservation_alg(modeldb%config, rho, mr, 'Before timestep') + end if + end if + end subroutine initialise_model !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! diff --git a/science/gungho/source/driver/gungho_step_mod.x90 b/science/gungho/source/driver/gungho_step_mod.x90 index 403bc7c8bb..bb1d10eae5 100644 --- a/science/gungho/source/driver/gungho_step_mod.x90 +++ b/science/gungho/source/driver/gungho_step_mod.x90 @@ -45,13 +45,6 @@ module gungho_step_mod use moisture_fluxes_alg_mod, only : moisture_fluxes_alg use timestep_method_mod, only : timestep_method_type, & get_timestep_method_from_collection - use rk_alg_timestep_mod, only : rk_timestep_type - use semi_implicit_timestep_alg_mod, & - only : semi_implicit_timestep_type - use timestepping_config_mod, only : method, & - method_semi_implicit, & - method_rk, & - method_no_timestepping use update_energy_correction_alg_mod, & only : update_energy_correction_alg use compute_total_energy_alg_mod, only : compute_total_energy_alg diff --git a/science/gungho/unit-test/kernel/core_dynamics/compute_dl_matrix_kernel_mod_test.pf b/science/gungho/unit-test/kernel/core_dynamics/compute_dl_matrix_kernel_mod_test.pf index 0164d328db..4c70fcebc6 100644 --- a/science/gungho/unit-test/kernel/core_dynamics/compute_dl_matrix_kernel_mod_test.pf +++ b/science/gungho/unit-test/kernel/core_dynamics/compute_dl_matrix_kernel_mod_test.pf @@ -54,12 +54,15 @@ contains real(r_def), parameter :: null_island(2) = [0.0_r_def, 0.0_r_def] real(r_def), parameter :: equator_latitude = 0.0_r_def - call feign_timestepping_config( & - method=method_semi_implicit, & - dt=1.0_r_second, alpha=0.5_r_def, tau_u=0.5_r_def, & - tau_t=0.5_r_def, tau_r=0.5_r_def, & - outer_iterations=4_i_def, inner_iterations=1_i_def, & - runge_kutta_method=runge_kutta_method_ssp3, & + call feign_timestepping_config( & + method=method_semi_implicit, & + dt=1.0_r_second, alpha=0.5_r_def, tau_u=0.5_r_def, & + tau_t=0.5_r_def, tau_r=0.5_r_def, & + outer_iterations=4_i_def, & + inner_iterations_tr=(/1_i_def/), & + inner_iterations_bdf2=(/1_i_def/), & + inner_iterations_si=(/1_i_def/), & + runge_kutta_method=runge_kutta_method_ssp3, & spinup_period=0.0_r_second, spinup_alpha=.false.) call feign_finite_element_config( & diff --git a/science/gungho/unit-test/kernel/diagnostics/cfl_kernel_mod_test.pf b/science/gungho/unit-test/kernel/diagnostics/cfl_kernel_mod_test.pf index 44b9c23aea..ae0102c417 100644 --- a/science/gungho/unit-test/kernel/diagnostics/cfl_kernel_mod_test.pf +++ b/science/gungho/unit-test/kernel/diagnostics/cfl_kernel_mod_test.pf @@ -52,7 +52,10 @@ contains method=method_semi_implicit, & dt=real(DT, r_second), alpha=0.5_r_def, tau_u=0.5_r_def, & tau_t=0.5_r_def, tau_r=0.5_r_def, & - outer_iterations=1_i_def, inner_iterations=1_i_def, & + outer_iterations=1_i_def, & + inner_iterations_tr=(/1_i_def/), & + inner_iterations_bdf2=(/1_i_def/), & + inner_iterations_si=(/1_i_def/), & runge_kutta_method=runge_kutta_method_ssp3, & spinup_period=0.0_r_second, spinup_alpha=.false.) diff --git a/science/gungho/unit-test/kernel/diagnostics/compute_bottom_mass_from_flux_kernel_mod_test.pf b/science/gungho/unit-test/kernel/diagnostics/compute_bottom_mass_from_flux_kernel_mod_test.pf index 5298e2a5b1..95b10a53e6 100644 --- a/science/gungho/unit-test/kernel/diagnostics/compute_bottom_mass_from_flux_kernel_mod_test.pf +++ b/science/gungho/unit-test/kernel/diagnostics/compute_bottom_mass_from_flux_kernel_mod_test.pf @@ -50,7 +50,10 @@ contains method=method_semi_implicit, & dt=real(dt, r_second), alpha=0.5_r_def, tau_u=0.5_r_def, & tau_t=0.5_r_def, tau_r=0.5_r_def, & - outer_iterations=1_i_def, inner_iterations=1_i_def, & + outer_iterations=1_i_def, & + inner_iterations_tr=(/1_i_def/), & + inner_iterations_bdf2=(/1_i_def/), & + inner_iterations_si=(/1_i_def/), & runge_kutta_method=runge_kutta_method_ssp3, & spinup_period=0.0_r_second, spinup_alpha=.false.) diff --git a/science/gungho/unit-test/kernel/external_forcing/deep_hot_jupiter_kernel_mod_test.pf b/science/gungho/unit-test/kernel/external_forcing/deep_hot_jupiter_kernel_mod_test.pf index b42327791a..ee90f3a11e 100644 --- a/science/gungho/unit-test/kernel/external_forcing/deep_hot_jupiter_kernel_mod_test.pf +++ b/science/gungho/unit-test/kernel/external_forcing/deep_hot_jupiter_kernel_mod_test.pf @@ -72,7 +72,10 @@ contains call feign_timestepping_config( method=method_semi_implicit, & dt=real(dt, r_second), alpha=0.5_r_def, & tau_u=0.5_r_def, tau_t=0.5_r_def, tau_r=0.5_r_def, & - outer_iterations=1_i_def, inner_iterations=1_i_def, & + outer_iterations=1_i_def, & + inner_iterations_tr=(/1_i_def/), & + inner_iterations_bdf2=(/1_i_def/), & + inner_iterations_si=(/1_i_def/), & runge_kutta_method=runge_kutta_method_ssp3, & spinup_period=0.0_r_second, spinup_alpha=.false.) diff --git a/science/gungho/unit-test/kernel/external_forcing/earth_like_kernel_mod_test.pf b/science/gungho/unit-test/kernel/external_forcing/earth_like_kernel_mod_test.pf index 0a6c802be2..a43055f61f 100644 --- a/science/gungho/unit-test/kernel/external_forcing/earth_like_kernel_mod_test.pf +++ b/science/gungho/unit-test/kernel/external_forcing/earth_like_kernel_mod_test.pf @@ -79,7 +79,10 @@ contains call feign_timestepping_config( method=method_semi_implicit, & dt=real(dt, r_second), alpha=0.5_r_def, & tau_u=0.5_r_def, tau_t=0.5_r_def, tau_r=0.5_r_def, & - outer_iterations=1_i_def, inner_iterations=1_i_def, & + outer_iterations=1_i_def, & + inner_iterations_tr=(/1_i_def/), & + inner_iterations_bdf2=(/1_i_def/), & + inner_iterations_si=(/1_i_def/), & runge_kutta_method=runge_kutta_method_ssp3, & spinup_period=0.0_r_second, spinup_alpha=.false.) diff --git a/science/gungho/unit-test/kernel/external_forcing/held_suarez_kernel_mod_test.pf b/science/gungho/unit-test/kernel/external_forcing/held_suarez_kernel_mod_test.pf index 5a38bf829b..15d18338fc 100644 --- a/science/gungho/unit-test/kernel/external_forcing/held_suarez_kernel_mod_test.pf +++ b/science/gungho/unit-test/kernel/external_forcing/held_suarez_kernel_mod_test.pf @@ -74,7 +74,10 @@ contains call feign_timestepping_config(method=method_semi_implicit, & dt=real(dt, r_second), alpha=0.5_r_def, tau_u=0.5_r_def, & tau_t=0.5_r_def, tau_r=0.5_r_def, & - outer_iterations=1_i_def, inner_iterations=1_i_def,& + outer_iterations=1_i_def, & + inner_iterations_tr=(/1_i_def/), & + inner_iterations_bdf2=(/1_i_def/), & + inner_iterations_si=(/1_i_def/), & runge_kutta_method=runge_kutta_method_ssp3, & spinup_period=0.0_r_second, spinup_alpha=.false.) diff --git a/science/gungho/unit-test/kernel/external_forcing/shallow_hot_jupiter_kernel_mod_test.pf b/science/gungho/unit-test/kernel/external_forcing/shallow_hot_jupiter_kernel_mod_test.pf index f3b6f192b7..c2784ab5bc 100644 --- a/science/gungho/unit-test/kernel/external_forcing/shallow_hot_jupiter_kernel_mod_test.pf +++ b/science/gungho/unit-test/kernel/external_forcing/shallow_hot_jupiter_kernel_mod_test.pf @@ -73,7 +73,10 @@ contains call feign_timestepping_config( method=method_semi_implicit, & dt=real(dt, r_second), alpha=0.5_r_def, & tau_u=0.5_r_def, tau_t=0.5_r_def, tau_r=0.5_r_def, & - outer_iterations=1_i_def, inner_iterations=1_i_def, & + outer_iterations=1_i_def, & + inner_iterations_tr=(/1_i_def/), & + inner_iterations_bdf2=(/1_i_def/), & + inner_iterations_si=(/1_i_def/), & runge_kutta_method=runge_kutta_method_ssp3, & spinup_period=0.0_r_second, spinup_alpha=.false.) diff --git a/science/gungho/unit-test/kernel/external_forcing/tidally_locked_earth_kernel_mod_test.pf b/science/gungho/unit-test/kernel/external_forcing/tidally_locked_earth_kernel_mod_test.pf index 495b87b283..36cd4eb877 100644 --- a/science/gungho/unit-test/kernel/external_forcing/tidally_locked_earth_kernel_mod_test.pf +++ b/science/gungho/unit-test/kernel/external_forcing/tidally_locked_earth_kernel_mod_test.pf @@ -74,7 +74,10 @@ contains call feign_timestepping_config( method=method_semi_implicit, & dt=real(dt, r_second), alpha=0.5_r_def, & tau_u=0.5_r_def, tau_t=1.0_r_def, tau_r=0.5_r_def, & - outer_iterations=1_i_def, inner_iterations=1_i_def, & + outer_iterations=1_i_def, & + inner_iterations_tr=(/1_i_def/), & + inner_iterations_bdf2=(/1_i_def/), & + inner_iterations_si=(/1_i_def/), & runge_kutta_method=runge_kutta_method_ssp3, & spinup_period=0.0_r_second, spinup_alpha=.false.) diff --git a/science/linear/integration-test/nwp_gal9/resources/nwp_gal9_configuration.nml b/science/linear/integration-test/nwp_gal9/resources/nwp_gal9_configuration.nml index 06e0f1c287..10349d6beb 100644 --- a/science/linear/integration-test/nwp_gal9/resources/nwp_gal9_configuration.nml +++ b/science/linear/integration-test/nwp_gal9/resources/nwp_gal9_configuration.nml @@ -354,7 +354,7 @@ timestep_start='1', ×tepping alpha=0.55, dt=1800, -inner_iterations=2, +inner_iterations_si=2,2 method='semi_implicit', outer_iterations=2, runge_kutta_method='forward_euler', diff --git a/science/linear/integration-test/semi_implicit/resources/semi_implicit_configuration.nml b/science/linear/integration-test/semi_implicit/resources/semi_implicit_configuration.nml index 75e188f5ec..aaedd554d0 100644 --- a/science/linear/integration-test/semi_implicit/resources/semi_implicit_configuration.nml +++ b/science/linear/integration-test/semi_implicit/resources/semi_implicit_configuration.nml @@ -220,7 +220,7 @@ calendar_origin='2016-01-01 15:00:00', ×tepping alpha=0.5, dt=10.0, -inner_iterations=2, +inner_iterations_si=2,2 method='semi_implicit', outer_iterations=2, spinup_alpha=.false., diff --git a/science/linear/integration-test/tl_test/tl_test_advect_density_field_mod.x90 b/science/linear/integration-test/tl_test/tl_test_advect_density_field_mod.x90 index 4caac57f06..eec9f97fd9 100644 --- a/science/linear/integration-test/tl_test/tl_test_advect_density_field_mod.x90 +++ b/science/linear/integration-test/tl_test/tl_test_advect_density_field_mod.x90 @@ -14,6 +14,7 @@ module tl_test_advect_density_field_mod use sci_assign_field_random_kernel_mod, & only: assign_field_random_kernel_type use constants_mod, only: i_def, r_def, str_def + use dycore_constants_mod, only: stepper_siqn use field_mod, only: field_type use field_collection_mod, only: field_collection_type use function_space_collection_mod, only: function_space_collection @@ -139,7 +140,7 @@ module tl_test_advect_density_field_mod ! Init stuff call gungho_transport_control_alg_init(mesh) call tl_transport_controller%initialise(modeldb%config, & - modeldb%clock, n_rho, ls_rho, n_u, n_u, ls_u, ls_u & + modeldb%clock, stepper_siqn, n_rho, ls_rho, n_u, n_u, ls_u, ls_u & ) transport_metadata => transport_metadata_collection%get_transport_metadata('density') @@ -169,12 +170,13 @@ module tl_test_advect_density_field_mod inc_X_plus_Y( n_u, p_u ), & inc_X_plus_Y( n_rho, p_rho ) ) - call n_transport_controller%initialise(modeldb%config, modeldb%clock, n_rho, n_u) + call n_transport_controller%initialise(modeldb%config, modeldb%clock, & + stepper_siqn, n_rho, n_u) call transport_field( n_rho_np1, n_rho, n_transport_controller, transport_metadata ) call invoke( X_minus_Y( n2_advection_inc, n_rho_np1, n_rho ) ) call tl_transport_controller%initialise(modeldb%config, & - modeldb%clock, n_rho, ls_rho, p_u, p_u, ls_u, ls_u & + modeldb%clock, stepper_siqn, n_rho, ls_rho, p_u, p_u, ls_u, ls_u & ) call tl_transport_field( & p_rho_np1, p_rho, ls_rho, tl_transport_controller, & diff --git a/science/linear/integration-test/tl_test/tl_test_advect_theta_field_mod.x90 b/science/linear/integration-test/tl_test/tl_test_advect_theta_field_mod.x90 index cb0ee50281..b69a683b7a 100644 --- a/science/linear/integration-test/tl_test/tl_test_advect_theta_field_mod.x90 +++ b/science/linear/integration-test/tl_test/tl_test_advect_theta_field_mod.x90 @@ -14,6 +14,7 @@ module tl_test_advect_theta_field_mod use sci_assign_field_random_kernel_mod, & only: assign_field_random_kernel_type use constants_mod, only: i_def, r_def, str_def + use dycore_constants_mod, only: stepper_siqn use field_mod, only: field_type use field_collection_mod, only: field_collection_type use function_space_collection_mod, only: function_space_collection @@ -140,7 +141,8 @@ module tl_test_advect_theta_field_mod ! Init stuff call gungho_transport_control_alg_init(mesh) - call tl_transport_controller%initialise( modeldb%config, modeldb%clock, n_rho, & + call tl_transport_controller%initialise( modeldb%config, modeldb%clock, & + stepper_siqn, n_rho, & ls_rho, n_u, n_u, ls_u, ls_u ) transport_metadata => transport_metadata_collection%get_transport_metadata('potential_temperature') @@ -171,12 +173,13 @@ module tl_test_advect_theta_field_mod inc_X_plus_Y( n_u, p_u ), & inc_X_plus_Y( n_theta, p_theta ) ) - call n_transport_controller%initialise(modeldb%config, modeldb%clock, n_rho, n_u) + call n_transport_controller%initialise(modeldb%config, modeldb%clock, & + stepper_siqn, n_rho, n_u) call transport_field( n_theta_np1, n_theta, n_transport_controller, transport_metadata ) call invoke( X_minus_Y( n2_advection_inc, n_theta_np1, n_theta ) ) call tl_transport_controller%initialise( modeldb%config, & - modeldb%clock, n_rho, ls_rho, p_u, p_u, ls_u, ls_u & + modeldb%clock, stepper_siqn, n_rho, ls_rho, p_u, p_u, ls_u, ls_u & ) call tl_transport_field( & p_theta_np1, p_theta, ls_theta, tl_transport_controller, & diff --git a/science/linear/integration-test/tl_test/tl_test_rhs_alg_mod.x90 b/science/linear/integration-test/tl_test/tl_test_rhs_alg_mod.x90 index ef5ac4be15..8307386add 100644 --- a/science/linear/integration-test/tl_test/tl_test_rhs_alg_mod.x90 +++ b/science/linear/integration-test/tl_test/tl_test_rhs_alg_mod.x90 @@ -10,6 +10,7 @@ module tl_test_rhs_alg_mod use constants_mod, only: i_def, r_def, str_def + use dycore_constants_mod, only: stepper_siqn use driver_modeldb_mod, only: modeldb_type use field_array_mod, only: field_array_type use field_mod, only: field_type @@ -174,7 +175,8 @@ module tl_test_rhs_alg_mod call set_bundle_scalar( 0.0_r_def, n1_rhs, bundle_size ) call rhs_alg( modeldb%config, n1_rhs, dt, ls_state, ls_state, ls_moist_dyn, & compute_eos=.true., compute_rhs_t_d=.true., & - dlayer_rhs=.true., model_clock=modeldb%clock ) + dlayer_rhs=.true., stepper=stepper_siqn, & + model_clock=modeldb%clock ) do n=1,2 gamma_u = gamma_u / 2.0_r_def @@ -202,11 +204,13 @@ module tl_test_rhs_alg_mod call rhs_alg( modeldb%config, n2_rhs, dt, state, state, n_moist_dyn, & compute_eos=.true., compute_rhs_t_d=.true., & - dlayer_rhs=.true., model_clock=modeldb%clock ) + dlayer_rhs=.true., stepper=stepper_siqn, & + model_clock=modeldb%clock ) call tl_rhs_alg( p_rhs, dt, p_state, p_state, p_moist_dyn, & ls_state, ls_moist_dyn, & compute_eos=.true., dlayer_rhs=.true., & + stepper=stepper_siqn, & model_clock=modeldb%clock) ! diff = n2_rhs - n1_rhs diff --git a/science/linear/integration-test/tl_test/tl_test_transport_control_mod.x90 b/science/linear/integration-test/tl_test/tl_test_transport_control_mod.x90 index 076c95a9da..1a2b7cd141 100644 --- a/science/linear/integration-test/tl_test/tl_test_transport_control_mod.x90 +++ b/science/linear/integration-test/tl_test/tl_test_transport_control_mod.x90 @@ -10,6 +10,7 @@ module tl_test_transport_control_mod use sci_assign_field_random_kernel_mod, & only: assign_field_random_kernel_type use constants_mod, only: i_def, r_def, str_def + use dycore_constants_mod, only: stepper_siqn use field_array_mod, only: field_array_type use field_mod, only: field_type use sci_field_bundle_builtins_mod, only: clone_bundle, & @@ -198,6 +199,7 @@ module tl_test_transport_control_mod ls_mr, & modeldb%clock, & outer, & + stepper_siqn, & cheap_update = .false.) gamma_u = 1._r_def @@ -241,6 +243,7 @@ module tl_test_transport_control_mod n_mr_in, & modeldb%clock, & outer, & + stepper_siqn, & cheap_update = .false. ) call tl_transport_control_alg(modeldb%config, & @@ -255,7 +258,8 @@ module tl_test_transport_control_mod ls_state(igh_u), & ls_mr, & modeldb%clock, & - outer ) + outer, & + stepper_siqn ) ! diff = n2_rhs - n1_rhs call minus_bundle( n2_rhs, n1_rhs, diff, bundle_size ) diff --git a/science/linear/source/algorithm/core_dynamics/tl_rhs_alg_mod.x90 b/science/linear/source/algorithm/core_dynamics/tl_rhs_alg_mod.x90 index 2ddbc1022b..9c389182fb 100644 --- a/science/linear/source/algorithm/core_dynamics/tl_rhs_alg_mod.x90 +++ b/science/linear/source/algorithm/core_dynamics/tl_rhs_alg_mod.x90 @@ -84,11 +84,12 @@ contains !! linearisation state !> @param[in] compute_eos Flag to control computation of the equation of !! state + !> @param[in] stepper Enumerator for the timestepper !> @param[in] dlayer_rhs Use damping layer in rhs yes/no !> @param[in] model_clock Time in the model subroutine tl_rhs_alg(rhs, alpha_dt, base_state, state, moist_dyn, & ls_state, ls_moist_dyn, compute_eos, & - dlayer_rhs, model_clock) + dlayer_rhs, stepper, model_clock) implicit none @@ -103,6 +104,7 @@ subroutine tl_rhs_alg(rhs, alpha_dt, base_state, state, moist_dyn, & type(field_type), intent(in) :: ls_moist_dyn(num_moist_factors) type(field_type), intent(inout) :: rhs(bundle_size) logical(kind=l_def), intent(in) :: compute_eos + integer(kind=i_def), intent(in) :: stepper class(model_clock_type), intent(in) :: model_clock type(operator_type), pointer :: mm_vel => null(), & @@ -150,7 +152,7 @@ subroutine tl_rhs_alg(rhs, alpha_dt, base_state, state, moist_dyn, & ! Using modified velocity mass matrix if using damping layer if (dlayer_rhs) then - mm_vel => get_w2_mass_matrix(w2_damping_layer_matrix, mesh%get_id(), model_clock) + mm_vel => get_w2_mass_matrix(w2_damping_layer_matrix, stepper, mesh%get_id(), model_clock) else mm_vel => get_mass_matrix_fe(W2, mesh%get_id()) end if diff --git a/science/linear/source/algorithm/timestepping/tl_rk_alg_timestep_mod.x90 b/science/linear/source/algorithm/timestepping/tl_rk_alg_timestep_mod.x90 index d705b82618..79fcfa486c 100644 --- a/science/linear/source/algorithm/timestepping/tl_rk_alg_timestep_mod.x90 +++ b/science/linear/source/algorithm/timestepping/tl_rk_alg_timestep_mod.x90 @@ -43,7 +43,8 @@ module tl_rk_alg_timestep_mod copy_bundle, & set_bundle_scalar use dycore_constants_mod, only: get_geopotential, & - get_coriolis + get_coriolis, & + stepper_rk ! PsyKAl PSYClone kernels use sci_enforce_bc_kernel_mod, only: enforce_bc_kernel_type @@ -323,7 +324,8 @@ contains ls_state_stage(igh_u,stage), & ls_state_stage(igh_u,stage), & ls_mr, ls_mr, model_clock, & - 1_i_def, cheap_update = .false. ) + 1_i_def, stepper_rk, & + cheap_update = .false. ) ! Compute new rhs if ( rotating ) then @@ -413,7 +415,8 @@ contains ls_state_stage(:,stage), & ls_state_stage(igh_u,stage), & ls_state_stage(igh_u,stage), & - ls_mr, model_clock, 1_i_def ) + ls_mr, model_clock, 1_i_def, & + stepper_rk ) ! Compute new rhs if ( rotating ) then diff --git a/science/linear/source/algorithm/timestepping/tl_si_timestep_alg_mod.x90 b/science/linear/source/algorithm/timestepping/tl_si_timestep_alg_mod.x90 index e281e16e9d..49e9ca5021 100644 --- a/science/linear/source/algorithm/timestepping/tl_si_timestep_alg_mod.x90 +++ b/science/linear/source/algorithm/timestepping/tl_si_timestep_alg_mod.x90 @@ -34,7 +34,7 @@ module tl_si_timestep_alg_mod use mixed_solver_config_mod, only: guess_np1, & reference_reset_time use timestepping_config_mod, only: alpha, & - outer_iterations, inner_iterations, & + outer_iterations, inner_iterations_si,& spinup_alpha use transport_config_mod, only: use_density_predictor use derived_config_mod, only: bundle_size @@ -151,7 +151,8 @@ module tl_si_timestep_alg_mod type( field_type ), allocatable :: ls_rhs_phys(:,:,:) type( field_type ), allocatable :: ls_mr_itns(:,:,:) type( field_type ), allocatable :: ls_moist_dyn_itns(:,:,:) - integer( i_def ) :: ls_outer_iterations, ls_inner_iterations + integer( i_def ), allocatable :: ls_inner_iterations(:) + integer( i_def ) :: ls_outer_iterations type( field_type ) :: theta_fv_inc ! theta fv transport increment to change to weak form @@ -218,12 +219,14 @@ contains ! but ls_outer_iterations and ls_inner_iterations are set to ! 1 so that the arrays are defined. ls_outer_iterations = 1_i_def - ls_inner_iterations = 1_i_def + if (.not. allocated(ls_inner_iterations)) allocate(ls_inner_iterations(1)) + ls_inner_iterations(:) = 1_i_def else ! The linearisation state is evolved, and the intermediate iterations ! are used to calculate the evolution of the perturbation. ls_outer_iterations = outer_iterations - ls_inner_iterations = inner_iterations + if (.not. allocated(ls_inner_iterations)) allocate(ls_inner_iterations(outer_iterations)) + ls_inner_iterations(:) = inner_iterations_si(:) end if if (full_init) then @@ -250,19 +253,19 @@ contains allocate(ls_mr_after_slow(nummr)) allocate(ls_state_itns(bundle_size, & - ls_outer_iterations, ls_inner_iterations )) + ls_outer_iterations, maxval(ls_inner_iterations) )) allocate(ls_rhs_np1(bundle_size, & - ls_outer_iterations, ls_inner_iterations )) + ls_outer_iterations, maxval(ls_inner_iterations) )) allocate(ls_rhs_adv(bundle_size, & - ls_outer_iterations, ls_inner_iterations )) + ls_outer_iterations, maxval(ls_inner_iterations) )) allocate(ls_rhs_phys(bundle_size, & - ls_outer_iterations, ls_inner_iterations )) + ls_outer_iterations, maxval(ls_inner_iterations) )) allocate(ls_mr_inc(nummr, & - ls_outer_iterations, ls_inner_iterations )) + ls_outer_iterations, maxval(ls_inner_iterations) )) allocate(ls_mr_itns(nummr, & - ls_outer_iterations, ls_inner_iterations )) + ls_outer_iterations, maxval(ls_inner_iterations) )) allocate(ls_moist_dyn_itns(num_moist_factors, & - ls_outer_iterations, ls_inner_iterations )) + ls_outer_iterations, maxval(ls_inner_iterations) )) end if !-------------------------------------------------------------------- @@ -295,7 +298,7 @@ contains call clone_bundle(state, ls_rhs_n, bundle_size) do outer = 1, ls_outer_iterations - do inner = 1, ls_inner_iterations + do inner = 1, ls_inner_iterations(outer) call clone_bundle(state, ls_state_itns(:, outer, inner), bundle_size) call clone_bundle(state, ls_rhs_np1(:, outer, inner), bundle_size) call clone_bundle(state, ls_rhs_adv(:, outer, inner), bundle_size) @@ -320,7 +323,7 @@ contains call clone_bundle(ls_mr, ls_mr_after_slow, nummr) do outer = 1,ls_outer_iterations - do inner = 1, ls_inner_iterations + do inner = 1, ls_inner_iterations(outer) call clone_bundle(ls_moist_dyn, ls_moist_dyn_itns(:, outer, inner), & num_moist_factors) call clone_bundle(ls_mr, ls_mr_itns(:, outer, inner), nummr) @@ -510,7 +513,8 @@ contains ls_rhs_n, varbeta*cast_dt, ls_state_after_slow, & ls_state_n, ls_moist_dyn_itns(:,1,1), & compute_eos=.false., compute_rhs_t_d=.true., & - dlayer_rhs=.false., model_clock=modeldb%clock ) + dlayer_rhs=.false., stepper=stepper_siqn, & + model_clock=modeldb%clock ) if ( .not. si_momentum_equation ) then ! Predictor of the wind field (u-beta*dt*rhs) to be advected if using @@ -532,6 +536,7 @@ contains ls_mr_itns(:, outer, 1), & ls_mr_after_slow(:), & modeldb%clock, outer, & + stepper_siqn, & cheap_update = .false. ) ! Convert theta increment to weak form @@ -560,14 +565,14 @@ contains !===================================================================== ! Start the Inner (nonlinear, coriolis) loop !===================================================================== - ls_inner_dynamics_loop: do inner = 1,ls_inner_iterations + ls_inner_dynamics_loop: do inner = 1,ls_inner_iterations(outer) write( log_scratch_space, '(A,2I3)' ) 'loop indices (o, i): ', & outer, inner call log_event( log_scratch_space, LOG_LEVEL_INFO ) ! Calculate the counters for the next iteration - if ( inner == inner_iterations ) then + if ( inner == inner_iterations_si(outer) ) then next_inner = 1 next_outer = outer + 1 else @@ -579,7 +584,7 @@ contains ! - and not the final iteration. So the final loop of the nonlinear is ! not required. if ( next_outer <= outer_iterations .and. & - next_inner <= inner_iterations ) then + next_inner <= inner_iterations_si(outer) ) then !-------------------------------------------------------------------- ! Compute the time-level n+1 dynamics terms @@ -593,6 +598,7 @@ contains compute_eos=.true., & compute_rhs_t_d=.true., & dlayer_rhs=dlayer_on, & + stepper=stepper_siqn, & model_clock=modeldb%clock ) !-------------------------------------------------------------------- @@ -732,7 +738,7 @@ contains !-------------------------------------------------------------------- call tl_rhs_alg( rhs_n, varbeta*cast_dt, state_after_slow, state_n, & moist_dyn, ls_state_n, ls_moist_dyn_itns(:, 1,1), & - .false., .false., model_clock=modeldb%clock ) + .false., .false., stepper_siqn, model_clock=modeldb%clock ) call invoke(setval_c( rhs_np1(igh_u), 0.0_r_def )) @@ -773,7 +779,8 @@ contains ls_state_n(igh_u), & ls_mr_after_slow, & modeldb%clock, & - outer ) + outer, & + stepper_siqn) ! Convert theta increment to weak form call invoke( setval_X(theta_fv_inc, rhs_adv(igh_t)), & @@ -795,6 +802,7 @@ contains ls_moist_dyn_itns( :, ls_outer, 1 ), & .true., & dlayer_on, & + stepper_siqn, & modeldb%clock ) end if @@ -822,7 +830,7 @@ contains !======================================================================= ! Start the Inner (nonlinear, coriolis) loop !======================================================================= - inner_dynamics_loop: do inner = 1,inner_iterations + inner_dynamics_loop: do inner = 1,inner_iterations_si(outer) write( log_scratch_space, '(A,2I3)' ) 'loop indices (o, i): ', & outer, inner call log_event( log_scratch_space, LOG_LEVEL_INFO ) @@ -847,6 +855,7 @@ contains ls_moist_dyn_itns(:, ls_outer, ls_inner), & .true., & dlayer_on, & + stepper_siqn, & modeldb%clock) end if @@ -1024,6 +1033,7 @@ contains if (allocated(ls_mr_inc)) deallocate(ls_mr_inc) if (allocated(ls_mr_itns)) deallocate(ls_mr_itns) if (allocated(ls_moist_dyn_itns)) deallocate(ls_moist_dyn_itns) + if (allocated(ls_inner_iterations)) deallocate(ls_inner_iterations) return end subroutine tl_semi_implicit_alg_final diff --git a/science/linear/source/algorithm/transport/control/tl_transport_control_alg_mod.x90 b/science/linear/source/algorithm/transport/control/tl_transport_control_alg_mod.x90 index f5d68b3d2c..56ea1d00c1 100644 --- a/science/linear/source/algorithm/transport/control/tl_transport_control_alg_mod.x90 +++ b/science/linear/source/algorithm/transport/control/tl_transport_control_alg_mod.x90 @@ -51,12 +51,12 @@ contains !> @param[in] ls_mr_in PASSIVE Moisture fields before transport !> @param[in] model_clock Time within the model !> @param[in] outer Outer (advection) iteration number - !> + !> @param[in] stepper Enumerator for timestepper subroutine tl_transport_control_alg(config, advection_inc, advected_fields, & wind_np1, wind_n, mr_out, mr_in, & ls_advected_fields, & ls_wind_np1, ls_wind_n, ls_mr_in, & - model_clock, outer ) + model_clock, outer, stepper ) use derived_config_mod, only: bundle_size use sci_field_bundle_builtins_mod, only: clone_bundle, set_bundle_scalar @@ -88,6 +88,7 @@ contains type(field_type), intent(in) :: ls_mr_in(nummr) class(model_clock_type), intent(in) :: model_clock integer(kind=i_def), intent(in) :: outer + integer(kind=i_def), intent(in) :: stepper ! Internal variables type(field_type) :: fields_np1(bundle_size) @@ -115,8 +116,8 @@ contains end if ! Initialise the transport controllers ------------------------------------- - call tl_transport_controller%initialise( config, & - model_clock, advected_fields(igh_d), ls_advected_fields(igh_d), & + call tl_transport_controller%initialise( config, model_clock, stepper, & + advected_fields(igh_d), ls_advected_fields(igh_d), & wind_n, wind_np1, ls_wind_n, ls_wind_np1, & outer=outer, cheap_update_step=cheap_update_step ) diff --git a/science/linear/source/algorithm/transport/control/tl_transport_controller_mod.x90 b/science/linear/source/algorithm/transport/control/tl_transport_controller_mod.x90 index 3d50361760..095b6aa634 100644 --- a/science/linear/source/algorithm/transport/control/tl_transport_controller_mod.x90 +++ b/science/linear/source/algorithm/transport/control/tl_transport_controller_mod.x90 @@ -59,6 +59,7 @@ contains !! and perturbed winds and densities. !> @param[in] config Application namelist configuration object !> @param[in] model_clock Tracks the time within the model + !> @param[in] stepper Enumerator for timestepper !> @param[in] ref_field_rdef Perturbed reference density to use for !! conservative transport of tracers. !> @param[in] ls_ref_field_rdef Linear reference density to use for @@ -77,8 +78,9 @@ contains !! the transporting wind is the *difference* !! in wind from the previous step, rather !! than the full transport wind. - subroutine initialise(self, config, model_clock, ref_field_rdef, & - ls_ref_field_rdef, wind_n_rdef, wind_np1_rdef, & + subroutine initialise(self, config, model_clock, stepper, & + ref_field_rdef, ls_ref_field_rdef, & + wind_n_rdef, wind_np1_rdef, & ls_wind_n_rdef, ls_wind_np1_rdef, & outer, cheap_update_step) @@ -94,21 +96,25 @@ contains type(field_type), intent(in) :: ls_wind_np1_rdef type(field_type), intent(in) :: ref_field_rdef type(field_type), intent(in) :: ls_ref_field_rdef + integer(kind=i_def), intent(in) :: stepper integer(kind=i_def), optional, intent(in) :: outer logical(kind=l_def), optional, intent(in) :: cheap_update_step self%config => config - call self%ls_wind_ls_rho_controller%initialise( self%config, & - model_clock, ls_ref_field_rdef, ls_wind_n_rdef, ls_wind_np1_rdef, & + call self%ls_wind_ls_rho_controller%initialise( & + self%config, model_clock, stepper, & + ls_ref_field_rdef, ls_wind_n_rdef, ls_wind_np1_rdef, & outer=outer, cheap_update_step=cheap_update_step ) - call self%ls_wind_pert_rho_controller%initialise( self%config, & - model_clock, ref_field_rdef, ls_wind_n_rdef, ls_wind_np1_rdef, & + call self%ls_wind_pert_rho_controller%initialise( & + self%config, model_clock, stepper, & + ref_field_rdef, ls_wind_n_rdef, ls_wind_np1_rdef, & outer=outer, cheap_update_step=cheap_update_step ) - call self%pert_wind_ls_rho_controller%initialise( self%config, & - model_clock, ls_ref_field_rdef, wind_n_rdef, wind_np1_rdef, & + call self%pert_wind_ls_rho_controller%initialise( & + self%config, model_clock, stepper, & + ls_ref_field_rdef, wind_n_rdef, wind_np1_rdef, & outer=outer, cheap_update_step=cheap_update_step ) end subroutine initialise