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.
1012module 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
2324contains
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
0 commit comments