Skip to content

Commit 40af4f1

Browse files
Initial move of code from simulation to pre_process
1 parent 7f49b7b commit 40af4f1

7 files changed

Lines changed: 286 additions & 114 deletions

File tree

src/pre_process/m_data_output.fpp

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ module m_data_output
2828

2929
private
3030
public :: s_write_serial_data_files, s_write_parallel_data_files, s_write_data_files, s_initialize_data_output_module, &
31-
& s_finalize_data_output_module
31+
& s_finalize_data_output_module, s_write_ib_state_0
3232

3333
type(scalar_field), allocatable, dimension(:) :: q_cons_temp
3434

@@ -733,6 +733,50 @@ contains
733733

734734
end subroutine s_initialize_data_output_module
735735

736+
!> @brief Writes restart_data/ib_state_0.dat: the initial IB layout (namelist patch_ib entries, then any generated
737+
!! particle-cloud beds). Rank-0-only. Read back by simulation at startup via s_read_ib_restart_data(0, ...)
738+
!! (src/simulation/m_start_up.fpp). Uses the same 20-field record layout s_write_serial_ib_state
739+
!! (src/simulation/m_data_output.fpp) writes, so simulation's own writers can freely overwrite this file later at t_step_start
740+
!! == 0 without changing its layout - only position (fields 17:19) and radius (field 20) are populated here; everything else
741+
!! (time, force, torque, vel, angular_vel, angles) is zero for a freshly generated IB.
742+
impure subroutine s_write_ib_state_0(particle_cloud_ibs, num_particle_cloud_ibs)
743+
744+
type(ib_patch_parameters), dimension(:), intent(in) :: particle_cloud_ibs
745+
integer, intent(in) :: num_particle_cloud_ibs
746+
character(LEN=len_trim(case_dir) + 2*name_len) :: file_loc
747+
integer :: i, ios, file_unit
748+
integer, parameter :: NFIELDS_PER_IB = 20
749+
real(wp) :: ib_buf(NFIELDS_PER_IB)
750+
751+
call s_create_directory(trim(case_dir) // '/restart_data')
752+
753+
file_loc = trim(case_dir) // '/restart_data/ib_state_0.dat'
754+
755+
open (newunit=file_unit, file=trim(file_loc), form='unformatted', access='stream', status='replace', iostat=ios)
756+
if (ios /= 0) call s_mpi_abort('Cannot open IB state output file: ' // trim(file_loc))
757+
758+
ib_buf = 0._wp
759+
760+
do i = 1, num_ibs
761+
ib_buf(17) = patch_ib(i)%x_centroid
762+
ib_buf(18) = patch_ib(i)%y_centroid
763+
ib_buf(19) = patch_ib(i)%z_centroid
764+
ib_buf(20) = patch_ib(i)%radius
765+
write (file_unit) ib_buf
766+
end do
767+
768+
do i = 1, num_particle_cloud_ibs
769+
ib_buf(17) = particle_cloud_ibs(i)%x_centroid
770+
ib_buf(18) = particle_cloud_ibs(i)%y_centroid
771+
ib_buf(19) = particle_cloud_ibs(i)%z_centroid
772+
ib_buf(20) = particle_cloud_ibs(i)%radius
773+
write (file_unit) ib_buf
774+
end do
775+
776+
close (file_unit)
777+
778+
end subroutine s_write_ib_state_0
779+
736780
!> Resets s_write_data_files pointer
737781
impure subroutine s_finalize_data_output_module
738782

src/pre_process/m_global_parameters.fpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,27 @@ contains
361361
patch_ib(i)%rotation_matrix_inverse = patch_ib(i)%rotation_matrix
362362
end do
363363

364+
num_particle_clouds = 0
365+
do i = 1, num_particle_clouds_max
366+
particle_cloud(i)%x_centroid = 0._wp
367+
particle_cloud(i)%y_centroid = 0._wp
368+
particle_cloud(i)%z_centroid = 0._wp
369+
particle_cloud(i)%length_x = dflt_real
370+
particle_cloud(i)%length_y = dflt_real
371+
particle_cloud(i)%length_z = dflt_real
372+
particle_cloud(i)%num_particles = 0
373+
particle_cloud(i)%radius = dflt_real
374+
particle_cloud(i)%mass = dflt_real
375+
particle_cloud(i)%min_spacing = 0._wp
376+
particle_cloud(i)%shell_inner_radius = dflt_real
377+
particle_cloud(i)%shell_outer_radius = dflt_real
378+
particle_cloud(i)%moving_ibm = 0
379+
particle_cloud(i)%seed = 0
380+
particle_cloud(i)%cloud_geometry = 1
381+
particle_cloud(i)%packing_method = dflt_int
382+
particle_cloud(i)%periodic = 0
383+
end do
384+
364385
do i = 1, num_ib_airfoils_max
365386
ib_airfoil(i)%c = dflt_real
366387
ib_airfoil(i)%p = dflt_real
Lines changed: 26 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
!>
22
!! @file m_particle_cloud.fpp
33
!! @brief Generates particle beds: converts particle_cloud specifications into
4-
!! individual sphere/circle particle_cloud_ibs entries before reduction.
4+
!! individual sphere/circle particle_cloud_ibs entries, written to
5+
!! restart_data/ib_state_0.dat for simulation to read at startup.
56

67
#:include 'macros.fpp'
78

8-
!> @brief Generates particle beds by converting particle_cloud patch specifications into individual immersed boundary patches before
9-
!! domain reduction. Each rank runs the same deterministic placement so no MPI broadcast of particle positions is needed.
9+
!> @brief Generates particle beds by converting particle_cloud patch specifications into individual immersed boundary patches. Runs
10+
!! once, on rank 0 only, producing the full global (unfiltered) particle list - simulation applies its own neighborhood filtering
11+
!! when it reads the result back from restart_data/ib_state_0.dat.
1012
module m_particle_cloud
1113

1214
use m_global_parameters
1315
use m_constants
1416
use m_mpi_common
15-
use m_collisions
1617

1718
implicit none
1819

@@ -22,11 +23,10 @@ module m_particle_cloud
2223

2324
contains
2425

25-
!> Generate all particle beds and fill particle_cloud_ibs. Called on all ranks before s_reduce_ib_patch_array. Each packing
26-
!! method owns and allocates its own per-cloud working array (see s_particle_cloud_lattice / s_particle_cloud_rejection_pack)
27-
!! and hands back only the entries that fall within this rank's IB neighborhood. Only the first num_particle_cloud_ibs of them
28-
!! are actually written - callers must use that count, not size(particle_cloud_ibs), since the remainder of the array is left
29-
!! uninitialized.
26+
!> Generate all particle beds and fill particle_cloud_ibs with the full, global (unfiltered) particle list. Called once, on rank
27+
!! 0 only. Each packing method owns and allocates its own per-cloud working array (see s_particle_cloud_lattice /
28+
!! s_particle_cloud_rejection_pack). Only the first num_particle_cloud_ibs of them are actually written - callers must use that
29+
!! count, not size(particle_cloud_ibs), since the remainder of the array is left uninitialized.
3030
impure subroutine s_generate_particle_clouds(particle_cloud_ibs, num_particle_cloud_ibs)
3131

3232
type(ib_patch_parameters), allocatable, intent(out), dimension(:) :: particle_cloud_ibs
@@ -78,12 +78,10 @@ contains
7878

7979
end subroutine s_generate_particle_clouds
8080

81-
!> Rejection-samples particle centres into a box or hemisphere-shell region with a minimum centre-to-centre spacing. Rejection
82-
!! sampling needs every placed particle tracked (regardless of which rank's neighborhood it falls in) to detect overlaps
83-
!! deterministically, so cloud_ibs is allocated here to the cloud's full requested particle count and only pared down to this
84-
!! rank's neighborhood afterwards, via s_reduce_particle_cloud_ibs. Only the per-candidate geometry sampling differs between box
81+
!> Rejection-samples particle centres into a box or hemisphere-shell region with a minimum centre-to-centre spacing. cloud_ibs
82+
!! is allocated here to the cloud's full requested particle count. Only the per-candidate geometry sampling differs between box
8583
!! and hemisphere shell; it is delegated to s_sample_cloud_candidate, and every other step (overlap rejection via the spatial
86-
!! hash, acceptance, reduction) is geometry-independent.
84+
!! hash, acceptance) is geometry-independent.
8785
subroutine s_particle_cloud_rejection_pack(cloud_idx, glbl_idx, cloud_ibs, num_cloud_ibs)
8886
8987
integer, intent(in) :: cloud_idx
@@ -174,7 +172,6 @@ contains
174172
175173
deallocate (placed, hash_head, chain_next)
176174
177-
call s_reduce_particle_cloud_ibs(cloud_ibs, ib_idx)
178175
num_cloud_ibs = ib_idx
179176
180177
end subroutine s_particle_cloud_rejection_pack
@@ -243,9 +240,7 @@ contains
243240
!> Places particles on the optimally dense lattice for the cloud region: a triangular lattice in 2D, a face-centered cubic
244241
!! lattice in 3D. The lattice spacing is set by the particle density (num_particles over the region area/volume); if that
245242
!! spacing falls below the required centre-to-centre distance (2*radius + min_spacing), the region is too dense and the run is
246-
!! aborted. No two lattice sites can overlap, so unlike rejection packing each site's IB neighborhood membership
247-
!! (get_neighbor_bounds() must already have run) is checked as it is generated and only in-neighborhood sites are stored;
248-
!! cloud_ibs is therefore allocated to the neighborhood-sized cap rather than the cloud's full particle count.
243+
!! aborted. No two lattice sites can overlap, so cloud_ibs is allocated to the cloud's full particle count.
249244
subroutine s_particle_cloud_lattice(cloud_idx, glbl_idx, cloud_ibs, num_cloud_ibs)
250245

251246
integer, intent(in) :: cloud_idx
@@ -259,7 +254,7 @@ contains
259254
real(wp), dimension(4) :: bx_off, by_off, bz_off
260255
real(wp), dimension(3) :: centroid
261256

262-
allocate (cloud_ibs(min(num_ib_patches_max_namelist, particle_cloud(cloud_idx)%num_particles)))
257+
allocate (cloud_ibs(particle_cloud(cloud_idx)%num_particles))
263258
ib_idx = 0
264259

265260
xmin = particle_cloud(cloud_idx)%x_centroid - 0.5_wp*particle_cloud(cloud_idx)%length_x
@@ -301,10 +296,7 @@ contains
301296
do while (px <= xmax .and. n_placed < n_target)
302297
glbl_idx = glbl_idx + 1
303298
centroid = [px, py, particle_cloud(cloud_idx)%z_centroid]
304-
if (f_neighborhood_ranks_own_location(centroid)) then
305-
call s_add_cloud_particle(cloud_idx, ib_idx, glbl_idx, geom, centroid(1), centroid(2), centroid(3), &
306-
& cloud_ibs)
307-
end if
299+
call s_add_cloud_particle(cloud_idx, ib_idx, glbl_idx, geom, centroid(1), centroid(2), centroid(3), cloud_ibs)
308300
n_placed = n_placed + 1
309301
col = col + 1
310302
px = x0 + real(col, wp)*spacing
@@ -328,10 +320,8 @@ contains
328320
centroid = [xmin + real(ix, wp)*cell + bx_off(b), ymin + real(jy, wp)*cell + by_off(b), &
329321
& zmin + real(kz, wp)*cell + bz_off(b)]
330322
glbl_idx = glbl_idx + 1
331-
if (f_neighborhood_ranks_own_location(centroid)) then
332-
call s_add_cloud_particle(cloud_idx, ib_idx, glbl_idx, geom, centroid(1), centroid(2), &
333-
& centroid(3), cloud_ibs)
334-
end if
323+
call s_add_cloud_particle(cloud_idx, ib_idx, glbl_idx, geom, centroid(1), centroid(2), centroid(3), &
324+
& cloud_ibs)
335325
n_placed = n_placed + 1
336326
end do
337327
end do
@@ -344,11 +334,11 @@ contains
344334

345335
end subroutine s_particle_cloud_lattice
346336

347-
!> Writes a single placed particle into particle_cloud_ibs at the next free slot, advancing ib_idx. The caller decides whether
348-
!! this particle belongs in the array (neighborhood membership, for lattice packing, or unconditionally for rejection packing -
349-
!! see s_particle_cloud_lattice / s_particle_cloud_rejection_pack) and supplies its already-assigned, absolute global patch id
350-
!! via glbl_idx - s_reduce_ib_patch_array copies gbl_patch_id as-is. Shared by all packing methods so the per-particle
351-
!! ib_patch_parameters setup stays in one place.
337+
!> Writes a single placed particle into particle_cloud_ibs at the next free slot, advancing ib_idx, tagged with its
338+
!! already-assigned, absolute global patch id via glbl_idx. Shared by all packing methods so the per-particle
339+
!! ib_patch_parameters setup stays in one place. Only x/y/z_centroid and radius are ever read back out of this struct (by
340+
!! s_write_ib_state_0); the rest is filled in for completeness and because leaving fields uninitialized here would otherwise
341+
!! surface as garbage if this struct is ever read further downstream.
352342
subroutine s_add_cloud_particle(cloud_idx, ib_idx, glbl_idx, geom, px, py, pz, particle_cloud_ibs)
353343

354344
integer, intent(in) :: cloud_idx, glbl_idx, geom
@@ -358,7 +348,7 @@ contains
358348

359349
ib_idx = ib_idx + 1
360350
@:PROHIBIT(ib_idx > size(particle_cloud_ibs), &
361-
& "Too many particle-cloud IBs in one rank's neighborhood. Modify case file or increase num_ib_patches_max_namelist.")
351+
& "Too many particle-cloud IBs. Modify case file or increase num_ib_patches_max_namelist.")
362352

363353
particle_cloud_ibs(ib_idx)%gbl_patch_id = glbl_idx
364354
particle_cloud_ibs(ib_idx)%geometry = geom
@@ -388,12 +378,9 @@ contains
388378
particle_cloud_ibs(ib_idx)%moving_ibm = particle_cloud(cloud_idx)%moving_ibm
389379
particle_cloud_ibs(ib_idx)%slip = .false.
390380

391-
! Particles are inert surfaces. These must be set explicitly: particle_cloud_ibs is
392-
! allocated (not default-initialized) and s_reduce_ib_patch_array copies the whole
393-
! struct into patch_ib, overwriting the defaults from
394-
! s_assign_default_values_to_user_inputs -- so anything left unset here reaches the
395-
! solver as uninitialized memory (a nonzero v_blow injects a garbage wall-normal
396-
! velocity and NaNs the field).
381+
! Particles are inert surfaces. particle_cloud_ibs is allocated (not default-initialized),
382+
! so these must be set explicitly even though only x/y/z_centroid and radius are read back
383+
! out of this struct downstream.
397384
particle_cloud_ibs(ib_idx)%v_blow = 0._wp
398385
particle_cloud_ibs(ib_idx)%inj_species = 0
399386
particle_cloud_ibs(ib_idx)%burn_rate_exp = 0._wp
@@ -500,29 +487,6 @@ contains
500487
501488
end subroutine s_check_cloud_particle_overlap
502489
503-
!> Compacts cloud_ibs(1:num_ibs) in place, discarding entries outside this rank's IB neighborhood (get_neighbor_bounds() must
504-
!! already have run) and updating num_ibs to the retained count. Used by rejection packing, which cannot filter as it places
505-
!! particles (see s_particle_cloud_rejection_pack), to pare its full, unfiltered placement down to this rank's neighborhood.
506-
subroutine s_reduce_particle_cloud_ibs(cloud_ibs, num_cloud_ibs)
507-
508-
type(ib_patch_parameters), intent(inout), dimension(:) :: cloud_ibs
509-
integer, intent(inout) :: num_cloud_ibs
510-
integer :: i, write_idx
511-
real(wp), dimension(3) :: centroid
512-
513-
write_idx = 0
514-
do i = 1, num_cloud_ibs
515-
centroid = [cloud_ibs(i)%x_centroid, cloud_ibs(i)%y_centroid, 0._wp]
516-
if (num_dims == 3) centroid(3) = cloud_ibs(i)%z_centroid
517-
if (f_neighborhood_ranks_own_location(centroid)) then
518-
write_idx = write_idx + 1
519-
if (write_idx /= i) cloud_ibs(write_idx) = cloud_ibs(i)
520-
end if
521-
end do
522-
num_cloud_ibs = write_idx
523-
524-
end subroutine s_reduce_particle_cloud_ibs
525-
526490
!> Xorshift PRNG. Advances seed in-place and returns a value in [0, 1).
527491
function f_xorshift(seed) result(rval)
528492

src/pre_process/m_start_up.fpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ module m_start_up
2828

2929
use m_check_patches
3030
use m_check_ib_patches
31+
use m_particle_cloud
3132
use m_helper
3233
use m_checker_common
3334
use m_checker
@@ -133,7 +134,21 @@ contains
133134
134135
call s_check_patches()
135136
136-
if (ib) call s_check_ib_patches()
137+
if (ib) then
138+
call s_check_ib_patches()
139+
140+
if (proc_rank == 0) then
141+
block
142+
type(ib_patch_parameters), allocatable :: particle_cloud_ibs(:)
143+
integer :: num_particle_cloud_ibs
144+
145+
call s_generate_particle_clouds(particle_cloud_ibs, num_particle_cloud_ibs)
146+
call s_write_ib_state_0(particle_cloud_ibs, num_particle_cloud_ibs)
147+
deallocate (particle_cloud_ibs)
148+
end block
149+
end if
150+
call s_mpi_barrier()
151+
end if
137152
138153
end subroutine s_check_input_file
139154

0 commit comments

Comments
 (0)