Factor timestep out of solver operators - #659
Factor timestep out of solver operators#659Thomas Bendall (tommbendall) wants to merge 10 commits into
Conversation
Update stable for vn3.1
Merge vn3.2 stable
|
Question: should/does this have an impact on |
No it doesn't impact |
|
Looks like this will need a PR in JEDI, I will link it here once ready |
DanStoneMO
left a comment
There was a problem hiding this comment.
Linked JEDI PR is now live at: https://github.com/MetOffice/lfric-jedi/pull/1323
|
I see that you have referenced the change of results: "By refactoring code, this PR changes the KGOs of all tests that use the linear solver. However these changes are only at the bit-level and do not impact science." I assume that the change to results is due to an order of operations change? Thanks. |
Yes that's right, the point at which fields are multiplied by |
| @@ -657,9 +696,11 @@ contains | |||
| ! Set rhs_u = rhs_u + M_2^{diag} * P_{2,theta} * rhs_theta | |||
| call rhs_tmp%initialise( vector_space = rhs_rsol(igh_u)%get_function_space() ) | |||
| p2theta => get_p2theta() | |||
| ! p2theta is assembled unscaled, so apply the scaling tau_u*dt*cp | |||
| call invoke( setval_c(rhs_tmp, 0.0_r_solver), & | |||
| matrix_vector_kernel_type( rhs_tmp, rhs_rsol(igh_t), p2theta ), & | |||
| inc_X_times_Y(rhs_tmp, m2_diag), & | |||
There was a problem hiding this comment.
inc_X_times_Y(rhs_tmp, m2_diag), &
inc_a_times_X(const_u, rhs_tmp), &
could become
inc_aX_times_Y(rhs_rtmp, const_u, m2_diag), &
| setval_c(rhs_tmp, 0.0_r_solver), & | ||
| matrix_vector_kernel_type( rhs_tmp, rhs_rsol(igh_t), q2t_op ), & | ||
| inc_a_times_X( const_u, rhs_tmp ), & | ||
| inc_X_plus_Y( rhs_rsol(igh_u), rhs_tmp ), & |
There was a problem hiding this comment.
inc_a_times_X( const_u, rhs_tmp ), &
inc_X_plus_Y( rhs_rsol(igh_u), rhs_tmp ), &
could become
inc_X_plus_bY(rhs_rsol(igh_u), const_u, rhs_tmp), &
| else | ||
| call invoke( setval_c( grad_p, 0.0_r_solver ), & | ||
| setval_c( y_vec, 0.0_r_solver ), & | ||
| scaled_matrix_vector_kernel_type(grad_p, x_vec, & | ||
| div_star, hb_lumped_inv, & | ||
| u_normalisation), & | ||
| inc_a_times_X( const_u, grad_p ), & |
There was a problem hiding this comment.
I think the multiplication by const_u could be taken inside the scaled_matrix_vector kernel to avoid the extra built in
thomasmelvin
left a comment
There was a problem hiding this comment.
Generally looks good, I've made a few suggestions where I think the code could be optimised a little more (generally removing a built-in of extra temporary field)
| @@ -209,26 +221,28 @@ contains | |||
| scaled_matrix_vector_kernel_type(grad_p, x_vec, & | |||
| div_star, hb_lumped_inv, & | |||
| u_normalisation), & | |||
| inc_a_times_X( const_u, grad_p ), & | |||
There was a problem hiding this comment.
I think the multiplication by const_u could be taken inside the scaled_matrix_vector kernel to avoid the extra built in
| scaled_matrix_vector_kernel_type(u_inc, exner_inc, & | ||
| div_star, u_normalisation, & | ||
| Hb_lumped_inv), & | ||
| inc_a_times_X(const_u, u_inc), & |
There was a problem hiding this comment.
I think the multiplication by const_u could be taken inside the scaled_matrix_vector kernel to avoid the extra built in
| @@ -483,6 +516,7 @@ contains | |||
| call uvw_norm%initialise( vector_space = self%rhs_u%get_function_space() ) | |||
| call invoke( name = "compute_split_increments", & | |||
| X_times_Y(uvw_norm, u_normalisation, Hb_lumped_inv), & | |||
| inc_a_times_X(const_u, uvw_norm), & | |||
There was a problem hiding this comment.
X_times_Y(uvw_norm, u_normalisation, Hb_lumped_inv), &
inc_a_times_X(const_u, uvw_norm),
could become
aX_times_Y(uvw_norm, const_u, u_normalisation, Hb_lumped_inv), &
There was a problem hiding this comment.
So there actually isn't an aX_times_Y built-in! I think this is still the simplest combination. Hopefully a clever compiler will fuse the loops and there will be no impact...
| inc_a_times_X(const_r, r_p), & | ||
| dg_matrix_vector_kernel_type(r_p_theta, self%rhs_u, q32_theta_op), & | ||
| inc_a_times_X(const_t, r_p_theta), & | ||
| inc_X_plus_Y(r_p, r_p_theta), & |
There was a problem hiding this comment.
I'm a bit concerned by the replacement of a single kernel with 5 kernels in terms of performance. I think it would be more efficient to create a new kernel to compute r_p = const_rq32_rho_opself%rhs_u + const_tq32_theta_opself%rhs_u
or more generally to compute
y = aMx + bNx for scalars a, b, matrices M, N and vectors x, y
this would also remove the need to create the r_p_theta vector
| ! per-level function spaces set up in the loop above. | ||
| if ( preconditioner == preconditioner_tridiagonal .or. & | ||
| preconditioner == preconditioner_multigrid ) then | ||
| call tri_precon_inventory%copy_field_array( tri_precon, stepper ) |
There was a problem hiding this comment.
Do all these copy_field_array mean we are creating multiple copies of the fields? i.e. one in the inventory and a local copy? if so it feels it would be better to expose the field from the inventory to the algorithm, something like tri_precon => tri_precon_inventory%get_field_array(stepper)
There was a problem hiding this comment.
I've now done this. One complication is that the elements of the field array are on different meshes. I couldn't think of a nice interface in the inventory to handle that! So instead I'm reinitialising the fields once they've been created.
I think this is not too bad since this routine is only done once at model start-up. I could make this cleaner if I stored the fields in the inventory by mesh and by stepper, but this would require changed interfaces everywhere these fields are used... I think that would be neater but is probably best left to a future PR
| setval_c(Hb_lumped_inv(level), 0.0_r_solver), & | ||
| setval_c(m2_u, 0.0_r_solver), & | ||
| matrix_vector_kernel_type(Hb_lumped_inv(level), ones, m2_r_solver), & | ||
| inc_X_times_Y(Hb_lumped_inv(level), u_normalisation), & | ||
| matrix_vector_kernel_type(m2_u, ones, eliminated_q22(level)), & | ||
| matrix_vector_kernel_type(m2_u, scaled_ones, eliminated_q22(level)), & |
There was a problem hiding this comment.
You could replace the used of scaled ones by
matrix_vector_kernel_type(m2_u, ones, eliminated_q22(level)), &
inc_X_minus_bY(Hb_lumped_inc(level), tau_tu_dt2_cp, m2_u), &
| setval_c(Hb_lumped_inv(level), 0.0_r_solver), & | ||
| setval_c(theta_adv_term, 0.0_r_solver), & | ||
| dg_inc_matrix_vector_kernel_type( & | ||
| theta_adv_term, ones, ptheta2v(level)), & | ||
| theta_adv_term, scaled_ones, ptheta2v(level)), & | ||
| inc_X_times_Y(theta_adv_term, t_normalisation), & |
There was a problem hiding this comment.
Again, you could replace the used of scaled ones by
dg_matrix_vector_kernel_type(theta_adv_term, ones, ptheta2v(level)), &
inc_X_times_bY(theta_adv_term, tau_t_dt, t_normalisation), &
and remove the extra invoke
Thanks very much for the review and the suggested improvements. I have:
I also realised I had forgotten to do one important thing: split the The changes were all made in this commit: b807692 I have also given specific replies about two changes:
|
DrTVockerodtMO
left a comment
There was a problem hiding this comment.
Thanks Tom, adjoint code changes look good. I've suggested only one minor change but in general I approve.
|
Your CLA signature was found on the base branch, but you appear to have modified the CONTRIBUTORS.md file in this PR. Please do not edit the CONTRIBUTORS.md file. If you have already signed the CLA, revert changes to the file and your signature will be picked up. |
PR Summary
Sci/Tech Reviewer: thomasmelvin
Code Reviewer: Lottie Turner (@mo-lottieturner)
This PR performs some refactoring of the
si_operatorsused by Gungho's linear solver. Many of these operators currently include a scaling of the formtau*dt, and this PR pulls those scalings out so that they are applied in-place rather than included in the operators.Motivation: the new TR-BDF2 timestepper being developed in #379 calls the solver multiple times with different values of
tau*dt. Factoringtau*dtout of these operators means that the operators do not need duplicating multiple times for the TR-BDF2 timestepper.However, some of the
si_operatorscan't be straightforwardly refactored and will need duplicating. This PR anticipates this by storing these operators in an appropriate inventory.Linked-To:
Blocks: #379
Overview of Changes
Primary operators —
tau*dtdependencetau*dtm3_rho_starm3_exner_starp3thetarho_at_udiv_startau_u * dt * c_pp2thetatau_u * dt * c_pptheta2tau_t * dtptheta2vtau_t * dtcompound_divtau_r * dteliminated_q22tau_t * tau_u * (dt)^2 * c_peliminated_q32tau_r * dt(fromcompound_div) +tau_t * dteliminated_q2ttau_u * dt * c_pDerived fields —
dtdependencedtdependenceHb_lumped_invtau_u*tau_t*(dt)^2*c_pfrom the buoyancy term;M_ucontributes adt-independent offset, so the dependence is nonlinear. Analytic: inverse oftau_t*tau_u*(dt)^2*c_pfromQ_22.Helmholtz_operatorHb_lumped_inv(nonlinear indt),div_star(tau_u*dt*c_p),compound_divoreliminated_q32(tau_r*dt,tau_t*dt), andm3_exner_star(none). Dominant off-diagonal structureHelm_diagHelmholtz_operator; inherits the same nonlineardtdependence. When normalisation is off, set to 1 (independent ofdt).tri_preconHelmholtz_operatorstencil components, optionally scaled byHelm_diag; inherits the same nonlineardtdependence asHelmholtz_operator.PR review walkthrough
Core refactor: SI operators are assembled unscaled,
tau*dtapplied latersi_operators_alg_mod.x90: The underlying (div_star,p2theta,ptheta2,ptheta2v,compound_div,eliminated_q22, ,eliminated_q2t) are now assembled without thetau*dtfactor baked in. The scaling is applied later, at the point each operator is applied (matrix-vector kernels,mixed_operator/apply_mixed_operator,apply_elim_mixed_lp,opt_apply_variable_hx, etc.), via new scalar arguments (const_u,const_t,const_r,const_div,const_theta).eliminated_q32has been split intoeliminated_q32_rhoandeliminated_q32_thetatau*dtfactors are stored in an inventory, so that there canbe multiple copies of these.
get_tau_u_dt_cp,get_tau_t_dt,get_tau_r_dt,get_tau_tu_dt2_cp). These take astepperargument, since the constants depend on which timestepper scheme is active. Operator-assembly routines (get_tri_precon_*,get_helmholtz_operator_*,get_helm_diag_*,get_hb_lumped_inv_*) andcreate_si_operatorswere similarly threaded withstepperso the correct constants can be looked up from the inventory.dycore_constants_mod.x90: added thestepper_siqnenumerator, used to select which SI-scheme constants apply.Gungho kernels/algorithms
apply_mixed_operator_kernel_mod.F90: kernel metadata and argument list extended to takeconst_u,const_t,const_r; the scaling multiplications that used to be implicit in the operator are now explicit in the kernel body.mixed_operator_alg_mod.x90,mixed_schur_preconditioner_alg_mod.x90,pressure_operator_alg_mod.x90,pressure_precon_alg_mod.x90,semi_implicit_solver_alg_mod.x90: updated to source and pass through the new scaling constants andstepperargument at the call sites that build/apply these operators.apply_elim_mixed_lp_operator_kernel_modandopt_apply_variable_hx_kernel_mod: same pattern — added scaling scalar arguments (const_r;const_div/const_theta) and renamedq32→q32_rhoto reflect the analytic-elimination naming.sample_eliminated_theta_q32_kernel_mod.F90/project_eliminated_theta_q32_kernel_mod.F90: minor follow-on tweaks for the renamed/rescoped operator.Results
By refactoring code, this PR changes the KGOs of all tests that use the linear solver. However these changes are only at the bit-level and do not impact science.
These PDFs contain the plots from the test-suite, compared with those from vn3.2, demonstrating that this change is indeed science-neutral:
results_si_operator_dt_gungho.pdf
results_si_operator_dt_lfric_atm.pdf
Performance
By moving the
tau*dtfactors out of the operators, these need applying during the application of the solver. The following plots show timings for lfric_atm and the solver, from jobs in the lfric_atm weekly suite. Each was run three times to get a sense for variability. Any increase in solver cost seems to be very small, and within the variability of the total runtime.Code Quality Checklist
Testing
trac.log
Test Suite Results - lfric_apps - si_operator_dt/run1
Suite Information
Task Information
✅ succeeded tasks - 1595
Security Considerations
Performance Impact
AI Assistance and Attribution
Copilot was used under human design and governance to perform this refactor and to fix the adjoint patches.
Documentation
PSyclone Approval
Sci/Tech Review
(Please alert the code reviewer via a tag when you have approved the SR)
Code Review