Skip to content

Commit b8a14ae

Browse files
committed
merge: AMR restart (SP10) into the PR branch
2 parents a57dcb6 + 9064ecd commit b8a14ae

3 files changed

Lines changed: 228 additions & 2 deletions

File tree

docs/documentation/case.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,21 @@ Accumulated fine-level fluxes are applied back to the coarse level (reflux corre
813813
after each coarse step.
814814
`amr_subcycle` is incompatible with `cfl_dt` (variable time step) and requires `amr = T`.
815815

816+
**Restart.**
817+
Each save step writes a fine-level AMR restart file alongside the level-0 restart data
818+
(whose format is unchanged): the current — possibly regridded — patch box and the fine
819+
solution, per rank (an `amr_fine.dat` in each rank's step directory, or a single shared
820+
`amr_*.dat` next to the level-0 MPI-IO restart file when `parallel_io` is on).
821+
Restarting (`t_step_start > 0`) restores the saved box and fine state seamlessly; it
822+
requires the same rank count (and decomposition) as the run that wrote the file, and
823+
aborts with a clear message otherwise.
824+
If the AMR file is absent (e.g., data from an older run), the run proceeds with a
825+
warning and re-initializes the fine level by prolongation from the coarse restart data,
826+
losing the accumulated fine-level accuracy.
827+
Note that level-0 output already contains the restricted (coarse-resolution) fine
828+
solution over the patch, so existing visualization works unchanged; fine-resolution
829+
visualization output is future work.
830+
816831
| Parameter | Type | Description |
817832
| ---: | :----: | :--- |
818833
| `amr` | Logical | Enable AMR (see prose above for requirements and restrictions) |

src/simulation/m_amr.fpp

Lines changed: 206 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
!> @brief AMR hierarchy (SP1: one static, inert refined level-1 patch alongside the base solve).
88
module m_amr
99

10+
#ifdef MFC_MPI
11+
use mpi !< MPI-IO for the parallel_io AMR restart file
12+
#endif
13+
1014
use m_derived_types ! scalar_field, t_box, int_bounds_info
1115
use m_global_parameters
1216
use m_constants, only: num_fluids_max
@@ -23,7 +27,8 @@ module m_amr
2327
public :: t_level, amr_fine, amr_maxc, amr_dt_fine, s_initialize_amr_module, s_populate_amr_fine, &
2428
& s_interpolate_coarse_to_fine, s_restrict_fine_to_coarse, s_amr_conservation_check, s_finalize_amr_module, &
2529
& s_amr_swap_to_fine, s_amr_restore_coarse, s_amr_fill_fine_ghosts, s_amr_operator_checks, s_advance_amr_fine_stage, &
26-
& s_advance_amr_fine_substeps, s_amr_conservation_defect, s_set_amr_fine_geometry, s_amr_regrid
30+
& s_advance_amr_fine_substeps, s_amr_conservation_defect, s_set_amr_fine_geometry, s_amr_regrid, s_write_amr_restart, &
31+
& s_read_amr_restart
2732

2833
!> Fine-level time step for subcycling (= 0.5*dt after init; 0 when amr is off).
2934
real(wp) :: amr_dt_fine = 0._wp
@@ -1160,6 +1165,206 @@ contains
11601165

11611166
end subroutine s_amr_regrid
11621167

1168+
!> Write the fine-level restart file for save step t_step alongside the level-0 restart (whose format stays untouched): the
1169+
!! CURRENT patch box (it moves under regrid), the writing rank count, and each rank's intersection-local fine conservative
1170+
!! state. Serial mode: one unformatted file per rank inside its level-0 step directory. Parallel mode: one shared MPI-IO file
1171+
!! with a 7-integer header (np, box lo, box hi) followed by the ranks' fine blocks concatenated in rank order.
1172+
impure subroutine s_write_amr_restart(t_step)
1173+
1174+
integer, intent(in) :: t_step
1175+
character(LEN=path_len + 3*name_len) :: file_loc
1176+
integer :: i
1177+
1178+
#ifdef MFC_MPI
1179+
integer :: ifile, ierr, cnt, idx, fi, fj, fk, hdr(7)
1180+
integer, dimension(MPI_STATUS_SIZE) :: status
1181+
integer(kind=MPI_OFFSET_KIND) :: my_cnt, my_off, disp
1182+
logical :: file_exist
1183+
real(stp), allocatable :: buf(:)
1184+
#endif
1185+
1186+
if (.not. amr) return
1187+
! host consumer: the fine state is device-current during stepping
1188+
if (amr_rank_owns_patch) then
1189+
do i = 1, sys_size
1190+
$:GPU_UPDATE(host='[amr_fine%q_cons(i)%sf]')
1191+
end do
1192+
end if
1193+
1194+
if (.not. parallel_io) then
1195+
! per-rank file in the step directory freshly created by the level-0 serial write
1196+
write (file_loc, '(A,I0,A,I0,A)') trim(case_dir) // '/p_all/p', proc_rank, '/', t_step, '/amr_fine.dat'
1197+
open (2, FILE=trim(file_loc), form='unformatted', STATUS='new')
1198+
write (2) num_procs, amr_fine%region%lo, amr_fine%region%hi, amr_fine%m, amr_fine%n, amr_fine%p
1199+
if (amr_rank_owns_patch) then
1200+
do i = 1, sys_size
1201+
write (2) amr_fine%q_cons(i)%sf(0:amr_fine%m,0:amr_fine%n,0:amr_fine%p)
1202+
end do
1203+
end if
1204+
close (2)
1205+
else
1206+
#ifdef MFC_MPI
1207+
write (file_loc, '(A,I0,A)') 'amr_', t_step, '.dat'
1208+
file_loc = trim(case_dir) // '/restart_data' // trim(mpiiofs) // trim(file_loc)
1209+
inquire (FILE=trim(file_loc), EXIST=file_exist)
1210+
if (file_exist .and. proc_rank == 0) then
1211+
call MPI_FILE_DELETE(file_loc, mpi_info_int, ierr)
1212+
end if
1213+
call MPI_FILE_OPEN(MPI_COMM_WORLD, file_loc, ior(MPI_MODE_WRONLY, MPI_MODE_CREATE), mpi_info_int, ifile, ierr)
1214+
1215+
! rank-order concatenation offset: exclusive prefix sum of the per-rank block sizes
1216+
cnt = sys_size*(amr_fine%m + 1)*(amr_fine%n + 1)*(amr_fine%p + 1) ! 0 on ranks without fine cells
1217+
if (.not. amr_rank_owns_patch) cnt = 0
1218+
my_cnt = int(cnt, MPI_OFFSET_KIND)
1219+
my_off = int(0, MPI_OFFSET_KIND)
1220+
call MPI_EXSCAN(my_cnt, my_off, 1, MPI_OFFSET, MPI_SUM, MPI_COMM_WORLD, ierr)
1221+
if (proc_rank == 0) then
1222+
my_off = int(0, MPI_OFFSET_KIND)
1223+
hdr(1) = num_procs; hdr(2:4) = amr_fine%region%lo; hdr(5:7) = amr_fine%region%hi
1224+
call MPI_FILE_WRITE_AT(ifile, int(0, MPI_OFFSET_KIND), hdr, 7, MPI_INTEGER, status, ierr)
1225+
end if
1226+
allocate (buf(max(cnt, 1)))
1227+
idx = 0
1228+
do i = 1, sys_size
1229+
do fk = 0, amr_fine%p
1230+
do fj = 0, amr_fine%n
1231+
do fi = 0, amr_fine%m
1232+
idx = idx + 1
1233+
buf(idx) = amr_fine%q_cons(i)%sf(fi, fj, fk)
1234+
end do
1235+
end do
1236+
end do
1237+
end do
1238+
disp = int(7*storage_size(0)/8, MPI_OFFSET_KIND) + my_off*int(storage_size(0._stp)/8, MPI_OFFSET_KIND)
1239+
call MPI_FILE_WRITE_AT_ALL(ifile, disp, buf, cnt*mpi_io_type, mpi_io_p, status, ierr)
1240+
deallocate (buf)
1241+
call MPI_FILE_CLOSE(ifile, ierr)
1242+
#endif
1243+
end if
1244+
1245+
end subroutine s_write_amr_restart
1246+
1247+
!> Restore the fine level from the AMR restart file at t_step_start (n_start under cfl_dt), if one exists: rebuild the saved
1248+
!! (possibly regridded) box via s_set_amr_fine_geometry, then read each rank's intersection-local fine state (exact stp
1249+
!! round-trip). Requires the rank count that wrote the file (np-flexible restart is a follow-up). restored = false on a fresh
1250+
!! start, or - with a one-line warning - on a legacy restart without the file; the caller then re-prolongs from coarse.
1251+
!! Collective: ALL ranks must call together (the existence decision is allreduced).
1252+
impure subroutine s_read_amr_restart(restored)
1253+
1254+
logical, intent(out) :: restored
1255+
character(LEN=path_len + 3*name_len) :: file_loc
1256+
character(LEN=200) :: msg
1257+
logical :: file_exist
1258+
integer :: i, ts, have_loc, have_glb, hdr(7), rm, rn, rp
1259+
1260+
#ifdef MFC_MPI
1261+
integer :: ifile, ierr, cnt, idx, fi, fj, fk
1262+
integer, dimension(MPI_STATUS_SIZE) :: status
1263+
integer(kind=MPI_OFFSET_KIND) :: my_cnt, my_off, tot_cnt, disp, file_sz
1264+
real(stp), allocatable :: buf(:)
1265+
#endif
1266+
1267+
restored = .false.
1268+
if (.not. amr) return
1269+
if (cfl_dt) then
1270+
ts = n_start
1271+
else
1272+
ts = t_step_start
1273+
end if
1274+
if (ts == 0) return ! fresh start: the fine level is prolonged from the pre_process ICs
1275+
1276+
if (.not. parallel_io) then
1277+
write (file_loc, '(A,I0,A,I0,A)') trim(case_dir) // '/p_all/p', proc_rank, '/', ts, '/amr_fine.dat'
1278+
else
1279+
write (file_loc, '(A,I0,A)') 'amr_', ts, '.dat'
1280+
file_loc = trim(case_dir) // '/restart_data' // trim(mpiiofs) // trim(file_loc)
1281+
end if
1282+
inquire (FILE=trim(file_loc), EXIST=file_exist)
1283+
have_loc = merge(1, 0, file_exist)
1284+
call s_mpi_allreduce_integer_min(have_loc, have_glb)
1285+
if (have_glb == 0) then
1286+
if (proc_rank == 0) then
1287+
print '(A)', &
1288+
& ' [amr] WARNING: no AMR restart file at this step; the fine level is re-initialized by ' &
1289+
& // 'prolongation from coarse (fine-level accuracy is lost across this restart)'
1290+
end if
1291+
return
1292+
end if
1293+
1294+
if (.not. parallel_io) then
1295+
open (2, FILE=trim(file_loc), form='unformatted', ACTION='read', STATUS='old')
1296+
read (2) hdr, rm, rn, rp
1297+
if (hdr(1) /= num_procs) then
1298+
write (msg, '(A,I0,A,I0,A)') 'amr restart rank-count mismatch: the AMR restart file was written with ', hdr(1), &
1299+
& ' ranks but this run has ', num_procs, '; restart with the same rank count'
1300+
call s_mpi_abort(trim(msg))
1301+
end if
1302+
call s_set_amr_fine_geometry(hdr(2:4), hdr(5:7))
1303+
if (rm /= amr_fine%m .or. rn /= amr_fine%n .or. rp /= amr_fine%p) then
1304+
call s_mpi_abort('amr restart decomposition mismatch: this rank''s fine extents differ from the file' &
1305+
& // ' (identical decomposition - rank count and load_balance settings - required)')
1306+
end if
1307+
if (amr_rank_owns_patch) then
1308+
do i = 1, sys_size
1309+
read (2) amr_fine%q_cons(i)%sf(0:amr_fine%m,0:amr_fine%n,0:amr_fine%p)
1310+
end do
1311+
end if
1312+
close (2)
1313+
else
1314+
#ifdef MFC_MPI
1315+
call MPI_FILE_OPEN(MPI_COMM_WORLD, file_loc, MPI_MODE_RDONLY, mpi_info_int, ifile, ierr)
1316+
call MPI_FILE_READ_AT_ALL(ifile, int(0, MPI_OFFSET_KIND), hdr, 7, MPI_INTEGER, status, ierr)
1317+
if (hdr(1) /= num_procs) then
1318+
write (msg, '(A,I0,A,I0,A)') 'amr restart rank-count mismatch: the AMR restart file was written with ', hdr(1), &
1319+
& ' ranks but this run has ', num_procs, '; restart with the same rank count'
1320+
call s_mpi_abort(trim(msg))
1321+
end if
1322+
call s_set_amr_fine_geometry(hdr(2:4), hdr(5:7))
1323+
cnt = sys_size*(amr_fine%m + 1)*(amr_fine%n + 1)*(amr_fine%p + 1)
1324+
if (.not. amr_rank_owns_patch) cnt = 0
1325+
my_cnt = int(cnt, MPI_OFFSET_KIND)
1326+
my_off = int(0, MPI_OFFSET_KIND)
1327+
call MPI_EXSCAN(my_cnt, my_off, 1, MPI_OFFSET, MPI_SUM, MPI_COMM_WORLD, ierr)
1328+
if (proc_rank == 0) my_off = int(0, MPI_OFFSET_KIND)
1329+
call MPI_ALLREDUCE(my_cnt, tot_cnt, 1, MPI_OFFSET, MPI_SUM, MPI_COMM_WORLD, ierr)
1330+
call MPI_FILE_GET_SIZE(ifile, file_sz, ierr)
1331+
if (file_sz /= int(7*storage_size(0)/8, MPI_OFFSET_KIND) + tot_cnt*int(storage_size(0._stp)/8, MPI_OFFSET_KIND)) then
1332+
call s_mpi_abort('amr restart decomposition mismatch: the AMR restart file size differs from this run''s' &
1333+
& // ' fine intersections (identical decomposition - rank count and load_balance settings' &
1334+
& // ' - required)')
1335+
end if
1336+
allocate (buf(max(cnt, 1)))
1337+
disp = int(7*storage_size(0)/8, MPI_OFFSET_KIND) + my_off*int(storage_size(0._stp)/8, MPI_OFFSET_KIND)
1338+
call MPI_FILE_READ_AT_ALL(ifile, disp, buf, cnt*mpi_io_type, mpi_io_p, status, ierr)
1339+
idx = 0
1340+
do i = 1, sys_size
1341+
do fk = 0, amr_fine%p
1342+
do fj = 0, amr_fine%n
1343+
do fi = 0, amr_fine%m
1344+
idx = idx + 1
1345+
amr_fine%q_cons(i)%sf(fi, fj, fk) = buf(idx)
1346+
end do
1347+
end do
1348+
end do
1349+
end do
1350+
deallocate (buf)
1351+
call MPI_FILE_CLOSE(ifile, ierr)
1352+
#endif
1353+
end if
1354+
1355+
! restored fine state to the device (mirrors s_populate_amr_fine's push; host reads above)
1356+
if (amr_rank_owns_patch) then
1357+
do i = 1, sys_size
1358+
$:GPU_UPDATE(device='[amr_fine%q_cons(i)%sf]')
1359+
end do
1360+
end if
1361+
restored = .true.
1362+
if (proc_rank == 0) then
1363+
print '(A,I0,A,I0)', ' [amr] restart: restored fine level, box x ', amr_fine%region%lo(1), ':', amr_fine%region%hi(1)
1364+
end if
1365+
1366+
end subroutine s_read_amr_restart
1367+
11631368
!> Global Sum(dV*U) for the per-fluid masses (continuity variables) and energy (eqn_idx%E) over the level-0 interior. First call
11641369
!! (finalize_report=F) stores the baselines; the finalize call prints the relative drifts (~roundoff with refluxing).
11651370
impure subroutine s_amr_conservation_defect(q_cons_base, finalize_report)

src/simulation/m_start_up.fpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,9 @@ contains
791791
! Write IB kinematic state for restart
792792
if (ib) call s_write_ib_state_file(save_count)
793793

794+
! Fine-level AMR restart file (current box + intersection-local fine state) alongside the level-0 restart
795+
if (amr) call s_write_amr_restart(save_count)
796+
794797
call nvtxEndRange
795798
call cpu_time(finish)
796799
if (cfl_dt) then
@@ -812,6 +815,7 @@ contains
812815

813816
integer :: m_ds, n_ds, p_ds
814817
integer :: i
818+
logical :: amr_restored
815819

816820
call s_initialize_global_parameters_module()
817821
#:if USING_AMD
@@ -892,7 +896,9 @@ contains
892896
call s_initialize_amr_module()
893897
call s_initialize_amr_registers()
894898
call s_amr_operator_checks()
895-
call s_populate_amr_fine(q_cons_ts(1)%vf)
899+
! restarts restore the saved (possibly regridded) box and fine state; otherwise prolong from coarse
900+
call s_read_amr_restart(amr_restored)
901+
if (.not. amr_restored) call s_populate_amr_fine(q_cons_ts(1)%vf)
896902
call s_amr_conservation_check(q_cons_ts(1)%vf)
897903
call s_amr_conservation_defect(q_cons_ts(1)%vf, .false.)
898904

0 commit comments

Comments
 (0)