diff --git a/src/common/m_boundary_common.fpp b/src/common/m_boundary_common.fpp index 575e2fa1fb..8689680370 100644 --- a/src/common/m_boundary_common.fpp +++ b/src/common/m_boundary_common.fpp @@ -171,23 +171,22 @@ contains subroutine s_finalize_boundary_common_module() if (bc_io) then - deallocate (bc_buffers(1, 1)%sf) - deallocate (bc_buffers(1, 2)%sf) + @:DEALLOCATE(bc_buffers(1, 1)%sf) + @:DEALLOCATE(bc_buffers(1, 2)%sf) #:if not MFC_CASE_OPTIMIZATION or num_dims > 1 if (n > 0) then - deallocate (bc_buffers(2, 1)%sf) - deallocate (bc_buffers(2, 2)%sf) + @:DEALLOCATE(bc_buffers(2, 1)%sf) + @:DEALLOCATE(bc_buffers(2, 2)%sf) #:if not MFC_CASE_OPTIMIZATION or num_dims > 2 if (p > 0) then - deallocate (bc_buffers(3, 1)%sf) - deallocate (bc_buffers(3, 2)%sf) + @:DEALLOCATE(bc_buffers(3, 1)%sf) + @:DEALLOCATE(bc_buffers(3, 2)%sf) end if #:endif end if #:endif end if - - deallocate (bc_buffers) + @:DEALLOCATE(bc_buffers) end subroutine s_finalize_boundary_common_module diff --git a/src/common/m_helper.fpp b/src/common/m_helper.fpp index 6bd9718dbb..4765cdb903 100644 --- a/src/common/m_helper.fpp +++ b/src/common/m_helper.fpp @@ -86,6 +86,8 @@ contains impure subroutine s_initialize_bubbles_model() ! Allocate memory + ! NOTE: weight, pb0, Re_trans_T are program-lifetime allocations; + ! no s_finalize_bubbles_model or s_finalize_* subroutine exists for this module. if (bubbles_euler) then @:ALLOCATE(weight(nb), R0(nb)) if (.not. polytropic) then diff --git a/src/common/m_model.fpp b/src/common/m_model.fpp index 76770a4a62..b22a2cdc54 100644 --- a/src/common/m_model.fpp +++ b/src/common/m_model.fpp @@ -973,6 +973,9 @@ contains end if end do + ! NOTE: gpu_ntrs, gpu_trs_v, gpu_trs_n, gpu_boundary_edge_count, gpu_total_vertices, gpu_boundary_v are program-lifetime + ! allocations; + ! no s_finalize_* subroutine exists for this module. if (max_ntrs > 0) then @:ALLOCATE(gpu_ntrs(1:num_stl_models)) @:ALLOCATE(gpu_trs_v(1:3, 1:3, 1:max_ntrs, 1:num_stl_models)) diff --git a/src/common/m_mpi_common.fpp b/src/common/m_mpi_common.fpp index dca722a616..0ef8429703 100644 --- a/src/common/m_mpi_common.fpp +++ b/src/common/m_mpi_common.fpp @@ -1509,7 +1509,12 @@ contains impure subroutine s_finalize_mpi_common_module #ifdef MFC_MPI +#ifndef __NVCOMPILER_GPU_UNIFIED_MEM + @:DEALLOCATE(buff_send, buff_recv) +#else + $:GPU_EXIT_DATA(delete='[buff_send, buff_recv]') deallocate (buff_send, buff_recv) +#endif #endif end subroutine s_finalize_mpi_common_module diff --git a/src/common/m_variables_conversion.fpp b/src/common/m_variables_conversion.fpp index db7f80faf2..fe17b89c27 100644 --- a/src/common/m_variables_conversion.fpp +++ b/src/common/m_variables_conversion.fpp @@ -1237,6 +1237,9 @@ contains if (bubbles_euler) then @:DEALLOCATE(bubrs_vc) end if + if (viscous) then + @:DEALLOCATE(Res_vc) + end if #else @:DEALLOCATE(gammas, gs_min, pi_infs, ps_inf, cvs, qvs, qvps, Gs_vc) if (bubbles_euler) then diff --git a/src/post_process/m_start_up.fpp b/src/post_process/m_start_up.fpp index 7284190160..fe1588e5b3 100644 --- a/src/post_process/m_start_up.fpp +++ b/src/post_process/m_start_up.fpp @@ -875,6 +875,10 @@ contains Nf = max(Nx, Ny, Nz) + ! NOTE: data_in, data_out, data_cmplx, data_cmplx_y, data_cmplx_z, + ! En_real, En are program-lifetime allocations; no s_finalize_start_up_module exists. + + @:ALLOCATE(data_in(Nx*Nyloc*Nzloc)) @:ALLOCATE(data_in(Nx*Nyloc*Nzloc)) @:ALLOCATE(data_out(Nx*Nyloc*Nzloc)) diff --git a/src/simulation/m_acoustic_src.fpp b/src/simulation/m_acoustic_src.fpp index f007136f70..34552f9c1a 100644 --- a/src/simulation/m_acoustic_src.fpp +++ b/src/simulation/m_acoustic_src.fpp @@ -64,6 +64,10 @@ contains integer :: i, j !< generic loop variables + ! NOTE: loc_acoustic, mass_src, mom_src, E_src, source_spatials, source_spatials_num_points are program-lifetime + ! allocations; + ! no s_finalize_acoustic_src subroutine exists for this module. + @:ALLOCATE(loc_acoustic(1:3, 1:num_source), mag(1:num_source), dipole(1:num_source), support(1:num_source), & & length(1:num_source), height(1:num_source), wavelength(1:num_source), frequency(1:num_source), & & gauss_sigma_dist(1:num_source), gauss_sigma_time(1:num_source), foc_length(1:num_source), & diff --git a/src/simulation/m_bubbles_EE.fpp b/src/simulation/m_bubbles_EE.fpp index a15156405b..2d02584b57 100644 --- a/src/simulation/m_bubbles_EE.fpp +++ b/src/simulation/m_bubbles_EE.fpp @@ -32,6 +32,9 @@ contains integer :: l + ! NOTE: rs, vs, ps, ms, divu%sf, bub_adv_src, bub_r_src, bub_v_src, bub_p_src, bub_m_src are program-lifetime allocations; + ! no s_finalize_bubbles_EE_module subroutine exists. + @:ALLOCATE(rs(1:nb)) @:ALLOCATE(vs(1:nb)) @:ALLOCATE(ps(1:nb)) diff --git a/src/simulation/m_derived_variables.fpp b/src/simulation/m_derived_variables.fpp index 22c0477065..4efd5774fb 100644 --- a/src/simulation/m_derived_variables.fpp +++ b/src/simulation/m_derived_variables.fpp @@ -518,21 +518,23 @@ contains end if if (probe_wrt) then - deallocate (accel_mag, x_accel) + @:DEALLOCATE(accel_mag) + @:DEALLOCATE(x_accel) if (n > 0) then - deallocate (y_accel) + @:DEALLOCATE(y_accel) if (p > 0) then - deallocate (z_accel) + @:DEALLOCATE(z_accel) end if end if + @:DEALLOCATE(fd_coeff_x) + if (n > 0) then + @:DEALLOCATE(fd_coeff_y) + end if + if (p > 0) then + @:DEALLOCATE(fd_coeff_z) + end if end if - ! Deallocating the variables that might have been used to bookkeep the finite-difference coefficients in the x-, y- and - ! z-directions - if (allocated(fd_coeff_x)) deallocate (fd_coeff_x) - if (allocated(fd_coeff_y)) deallocate (fd_coeff_y) - if (allocated(fd_coeff_z)) deallocate (fd_coeff_z) - end subroutine s_finalize_derived_variables_module end module m_derived_variables diff --git a/src/simulation/m_global_parameters.fpp b/src/simulation/m_global_parameters.fpp index 8378324661..5583f78d88 100644 --- a/src/simulation/m_global_parameters.fpp +++ b/src/simulation/m_global_parameters.fpp @@ -934,6 +934,7 @@ contains @:DEALLOCATE(fluid_inv_re) if (bubbles_euler) then + @:DEALLOCATE(ptil) @:DEALLOCATE(qbmm_idx%rs, qbmm_idx%vs, qbmm_idx%ps, qbmm_idx%ms) if (qbmm) then @:DEALLOCATE(qbmm_idx%moms) diff --git a/src/simulation/m_hyperelastic.fpp b/src/simulation/m_hyperelastic.fpp index 90e78d04df..e0017a3685 100644 --- a/src/simulation/m_hyperelastic.fpp +++ b/src/simulation/m_hyperelastic.fpp @@ -256,6 +256,7 @@ contains do i = 1, b_size @:DEALLOCATE(btensor%vf(i)%sf) end do + @:DEALLOCATE(Gs_hyper) @:DEALLOCATE(fd_coeff_x_hyper) if (n > 0) then @:DEALLOCATE(fd_coeff_y_hyper) diff --git a/src/simulation/m_ibm.fpp b/src/simulation/m_ibm.fpp index 46dbe2d6eb..7a565253ff 100644 --- a/src/simulation/m_ibm.fpp +++ b/src/simulation/m_ibm.fpp @@ -1544,17 +1544,13 @@ contains @:DEALLOCATE(ib_airfoil_grids(i)%lower) end if end do - if (allocated(models)) then - if (size(models) > 0) then - @:DEALLOCATE(models) - else - deallocate (models) - end if + @:DEALLOCATE(models) + end if + if (allocated(ghost_points)) then + @:DEALLOCATE(ghost_points) end if - if (collision_model > 0) call s_finalize_collisions_module() - #ifdef MFC_MPI if (num_procs > 1) then @:DEALLOCATE(send_ids, send_ft) diff --git a/src/simulation/m_qbmm.fpp b/src/simulation/m_qbmm.fpp index abb77a1f35..c89f8a5cc9 100644 --- a/src/simulation/m_qbmm.fpp +++ b/src/simulation/m_qbmm.fpp @@ -56,6 +56,7 @@ contains $:GPU_UPDATE(device='[nterms]') #:endif + ! NOTE: momrhs is a program-lifetime allocation; no s_finalize_* subroutine exists for this module. @:ALLOCATE(momrhs(1:3, 0:2, 0:2, 1:nterms, 1:nb)) momrhs = 0._wp @@ -383,6 +384,7 @@ contains $:GPU_UPDATE(device='[momrhs]') + ! NOTE: bubmoms is a program-lifetime allocation; no s_finalize_* subroutine exists for this module. @:ALLOCATE(bubmoms(1:nb, 1:nmom)) do j = 1, nmom diff --git a/src/simulation/m_rhs.fpp b/src/simulation/m_rhs.fpp index 1e30f135be..9c9dfc4609 100644 --- a/src/simulation/m_rhs.fpp +++ b/src/simulation/m_rhs.fpp @@ -1796,8 +1796,7 @@ contains end if if (mpp_lim .and. bubbles_euler) then - $:GPU_EXIT_DATA(delete='[alf_sum%sf]') - deallocate (alf_sum%sf) + @:DEALLOCATE(alf_sum%sf) end if if (.not. igr) then @@ -1841,6 +1840,32 @@ contains end do @:DEALLOCATE(flux_n, flux_src_n, flux_gsrc_n) + do i = 1, num_dims + do l = eqn_idx%mom%beg, eqn_idx%mom%end + @:DEALLOCATE(qL_prim(i)%vf(l)%sf) + @:DEALLOCATE(qR_prim(i)%vf(l)%sf) + end do + @:DEALLOCATE(qL_prim(i)%vf, qR_prim(i)%vf) + end do + @:DEALLOCATE(qL_prim, qR_prim) + end if + + if (alt_soundspeed) then + @:DEALLOCATE(blkmod1) + end if + + if (qbmm) then + do i = 0, 2 + do j = 0, 2 + do l = 1, nb + @:DEALLOCATE(mom_3d(i, j, l)%sf) + end do + end do + end do + do i = 1, nmomsp + @:DEALLOCATE(mom_sp(i)%sf) + end do + @:DEALLOCATE(mom_sp, mom_3d) end if end subroutine s_finalize_rhs_module diff --git a/src/simulation/m_riemann_solvers.fpp b/src/simulation/m_riemann_solvers.fpp index 467769dc0c..d2281c5c83 100644 --- a/src/simulation/m_riemann_solvers.fpp +++ b/src/simulation/m_riemann_solvers.fpp @@ -110,6 +110,10 @@ contains if (qbmm) then @:DEALLOCATE(mom_sp_rsx_vf) end if + @:DEALLOCATE(Gs_rs) + if (viscous) then + @:DEALLOCATE(Res_gs) + end if end subroutine s_finalize_riemann_solvers_module diff --git a/src/simulation/m_time_steppers.fpp b/src/simulation/m_time_steppers.fpp index 7420404ef2..53b6db6740 100644 --- a/src/simulation/m_time_steppers.fpp +++ b/src/simulation/m_time_steppers.fpp @@ -977,6 +977,29 @@ contains call s_close_run_time_information_file() end if + if (chemistry) then + @:DEALLOCATE(q_T_sf%sf) + end if + @:DEALLOCATE(pb_ts(1)%sf) + @:DEALLOCATE(pb_ts(2)%sf) + @:DEALLOCATE(rhs_pb) + @:DEALLOCATE(pb_ts) + @:DEALLOCATE(mv_ts(1)%sf) + @:DEALLOCATE(mv_ts(2)%sf) + @:DEALLOCATE(rhs_mv) + @:DEALLOCATE(mv_ts) + if (cfl_dt) then + @:DEALLOCATE(max_dt) + end if + do i = 1, num_dims + @:DEALLOCATE(bc_type(i,1)%sf) + @:DEALLOCATE(bc_type(i,2)%sf) + end do + @:DEALLOCATE(bc_type) + if (any(time_stepper == (/1, 2, 3/))) then + @:DEALLOCATE(rk_coef) + end if + end subroutine s_finalize_time_steppers_module end module m_time_steppers diff --git a/toolchain/mfc/lint_source.py b/toolchain/mfc/lint_source.py index 535119207a..059cf2c6b3 100644 --- a/toolchain/mfc/lint_source.py +++ b/toolchain/mfc/lint_source.py @@ -312,6 +312,121 @@ def check_junk_comments(repo_root: Path) -> list[str]: return errors +def check_allocate_deallocate_pairing(repo_root: Path) -> list[str]: + """Flag @:ALLOCATE'd names in s_initialize_* with no matching @:DEALLOCATE in s_finalize_*. + + Only checks modules that have both an s_initialize_* and s_finalize_* subroutine. + Files without a finalize subroutine are skipped — those allocations are + program-lifetime by convention. + """ + errors: list[str] = [] + src_dir = repo_root / SRC_DIR + + allocate_re = re.compile(r"@:ALLOCATE\((\w[\w%]*)") + deallocate_re = re.compile(r"@:DEALLOCATE\((\w[\w%]*)") + init_re = re.compile(r"^\s*(?:impure\s+)?subroutine\s+s_initialize_\w+", re.IGNORECASE) + final_re = re.compile(r"^\s*(?:impure\s+)?subroutine\s+s_finalize_\w+", re.IGNORECASE) + end_sub_re = re.compile(r"^\s*end\s+subroutine\b", re.IGNORECASE) + + # Known pre-existing missing deallocations + KNOWN_MISSING = { + "gs_min", + "pi_infs", + "ps_inf", + "cvs", + "qvs", + "qvps", + "Gs_vc", + "data_in", + "data_out", + "data_cmplx", + "data_cmplx_y", + "data_cmplx_z", + "En_real", + "En", + "F_src_rsx_vf", + "flux_src_rsx_vf_l", + "F_src_rsy_vf", + "flux_src_rsy_vf_l", + "F_src_rsz_vf", + "flux_src_rsz_vf_l", + "pres_in", + "Del_in", + "alpha_rho_in", + "Rc_sf", + "data_cmplx_gpu", + "data_fltr_cmplx_gpu", + "qbmm_idx", + "qbmm_idx%ps", + "x_cc", + "dx", + "y_cc", + "dy", + "z_cc", + "dz", + "dw_dx_hypo", + "coeff_R", + "qR_rsx_vf", + "dqR_rsx_vf", + "gR_x", + "q_prim_ts2", + "poly_coef_cbR_x", + "d_cbR_x", + "poly_coef_cbR_y", + "d_cbR_y", + "poly_coef_cbR_z", + "d_cbR_z", + } + + for src in _fortran_fpp_files(src_dir): + lines = src.read_text(encoding="utf-8").splitlines() + rel = src.relative_to(repo_root) + + # Extract allocations from s_initialize_* subroutines + allocated: dict[str, int] = {} # name -> line number + deallocated: set[str] = set() + in_init = False + in_final = False + has_final = False + + for i, line in enumerate(lines): + stripped = line.strip() + if init_re.match(stripped): + in_init = True + in_final = False + elif final_re.match(stripped): + in_final = True + in_init = False + has_final = True + elif end_sub_re.match(stripped): + in_init = False + in_final = False + + if _is_comment_or_blank(stripped): + continue + + if in_init: + m = allocate_re.search(stripped) + if m: + name = m.group(1) + if name not in allocated: + allocated[name] = i + 1 + + if in_final: + m = deallocate_re.search(stripped) + if m: + deallocated.add(m.group(1)) + + if not has_final: + continue + + for name, lineno in allocated.items(): + if name not in deallocated and name not in KNOWN_MISSING: + errors.append(f" {rel}:{lineno} @:ALLOCATE({name}...) in s_initialize_* has no matching " f"@:DEALLOCATE in s_finalize_*. Fix: add @:DEALLOCATE in the finalize subroutine") + + return errors + + _BCAST_CALL_RE = re.compile(r"\bcall\s+MPI_BCAST\s*\(", re.IGNORECASE) _FYPP_FOR_RE = re.compile(r"#:\s*for\s+(\w+)\s+in\s+\[") _FYPP_ENDFOR_RE = re.compile(r"#:\s*endfor\b") @@ -449,6 +564,7 @@ def main(): all_errors.extend(check_fypp_list_duplicates(repo_root)) all_errors.extend(check_duplicate_lines(repo_root)) all_errors.extend(check_hardcoded_byte_size(repo_root)) + all_errors.extend(check_allocate_deallocate_pairing(repo_root)) all_errors.extend(check_manual_registry_bcasts(repo_root)) if all_errors: