26 type(ib_patch_parameters),
allocatable,
intent(out),
dimension(:) :: particle_cloud_ibs
27 integer :: cloud_idx, ib_idx, n_total_particles
28 real(wp) :: t_start, t_end
30 if (num_particle_clouds == 0)
then
31 allocate (particle_cloud_ibs(0))
35 call cpu_time(t_start)
39 do cloud_idx = 1, num_particle_clouds
40 n_total_particles = n_total_particles + particle_cloud(cloud_idx)%num_particles
42 allocate (particle_cloud_ibs(n_total_particles))
46 do cloud_idx = 1, num_particle_clouds
47 select case (particle_cloud(cloud_idx)%packing_method)
56 if (
proc_rank == 0) print
'(a,i0,a,f0.3,a)',
'Particle beds placed ', ib_idx,
' particles in ', t_end - t_start,
' seconds.'
63 integer,
intent(in) :: cloud_idx
64 integer,
intent(inout) :: ib_idx
65 type(ib_patch_parameters),
intent(inout),
dimension(:) :: particle_cloud_ibs
66 integer :: n_placed, geom, seed
67 integer(8) :: n_attempts, max_attempts
68 real(wp) :: xmin, xmax, ymin, ymax, zmin, zmax, min_dist
69 real(wp) :: rx, ry, rz, dist
71 real(wp),
allocatable :: placed(:,:)
72 integer :: hash_size, slot
73 integer :: bx, by, bz, nbx, nby, nbz
74 integer :: dx_b, dy_b, dz_b, dz_lo, dz_hi, j
75 integer,
allocatable :: hash_head(:), chain_next(:)
77 xmin = particle_cloud(cloud_idx)%x_centroid - 0.5_wp*particle_cloud(cloud_idx)%length_x
78 xmax = particle_cloud(cloud_idx)%x_centroid + 0.5_wp*particle_cloud(cloud_idx)%length_x
79 ymin = particle_cloud(cloud_idx)%y_centroid - 0.5_wp*particle_cloud(cloud_idx)%length_y
80 ymax = particle_cloud(cloud_idx)%y_centroid + 0.5_wp*particle_cloud(cloud_idx)%length_y
81 zmin = particle_cloud(cloud_idx)%z_centroid - 0.5_wp*particle_cloud(cloud_idx)%length_z
82 zmax = particle_cloud(cloud_idx)%z_centroid + 0.5_wp*particle_cloud(cloud_idx)%length_z
84 min_dist = 2._wp*particle_cloud(cloud_idx)%radius + particle_cloud(cloud_idx)%min_spacing
96 max_attempts = int(particle_cloud(cloud_idx)%num_particles, 8)*1000_8
99 seed = particle_cloud(cloud_idx)%seed
100 if (seed == 0) seed = 1 + cloud_idx*1013904223
102 allocate (placed(3, particle_cloud(cloud_idx)%num_particles))
106 hash_size = max(16, 4*particle_cloud(cloud_idx)%num_particles)
107 allocate (hash_head(hash_size))
108 allocate (chain_next(particle_cloud(cloud_idx)%num_particles))
112 do while (n_placed < particle_cloud(cloud_idx)%num_particles .and. n_attempts < max_attempts)
113 n_attempts = n_attempts + 1
118 rz = particle_cloud(cloud_idx)%z_centroid
123 bx = int(floor(rx/min_dist))
124 by = int(floor(ry/min_dist))
126 if (p /= 0) bz = int(floor(rz/min_dist))
130 outer:
do dx_b = -1, 1
132 do dz_b = dz_lo, dz_hi
140 dist = sqrt((rx - placed(1, j))**2 + (ry - placed(2, j))**2)
142 dist = sqrt((rx - placed(1, j))**2 + (ry - placed(2, j))**2 + (rz - placed(3, j))**2)
144 if (dist < min_dist)
then
154 if (.not. overlaps)
then
155 n_placed = n_placed + 1
156 placed(1, n_placed) = rx
157 placed(2, n_placed) = ry
158 placed(3, n_placed) = rz
162 chain_next(n_placed) = hash_head(slot)
163 hash_head(slot) = n_placed
169 if (n_placed < particle_cloud(cloud_idx)%num_particles)
then
170 call s_mpi_abort(
"Error :: Failed to place all particles in particle bed")
173 deallocate (placed, hash_head, chain_next)
183 integer,
intent(in) :: cloud_idx
184 integer,
intent(inout) :: ib_idx
185 type(ib_patch_parameters),
intent(inout),
dimension(:) :: particle_cloud_ibs
186 integer :: n_placed, n_target, geom
187 integer :: row, col, ncx, ncy, ix, jy, kz, b
188 real(wp) :: xmin, xmax, ymin, ymax, zmin, zmax, min_dist
189 real(wp) :: spacing, row_dy, cell, x0, px, py
190 real(wp),
dimension(4) :: bx_off, by_off, bz_off
192 xmin = particle_cloud(cloud_idx)%x_centroid - 0.5_wp*particle_cloud(cloud_idx)%length_x
193 xmax = particle_cloud(cloud_idx)%x_centroid + 0.5_wp*particle_cloud(cloud_idx)%length_x
194 ymin = particle_cloud(cloud_idx)%y_centroid - 0.5_wp*particle_cloud(cloud_idx)%length_y
195 ymax = particle_cloud(cloud_idx)%y_centroid + 0.5_wp*particle_cloud(cloud_idx)%length_y
196 zmin = particle_cloud(cloud_idx)%z_centroid - 0.5_wp*particle_cloud(cloud_idx)%length_z
197 zmax = particle_cloud(cloud_idx)%z_centroid + 0.5_wp*particle_cloud(cloud_idx)%length_z
199 min_dist = 2._wp*particle_cloud(cloud_idx)%radius + particle_cloud(cloud_idx)%min_spacing
200 n_target = particle_cloud(cloud_idx)%num_particles
206 spacing = sqrt(2._wp*(xmax - xmin)*(ymax - ymin)/(sqrt(3._wp)*real(n_target, wp)))
210 spacing = (sqrt(2._wp)*(xmax - xmin)*(ymax - ymin)*(zmax - zmin)/real(n_target, wp))**(1._wp/3._wp)
213 if (spacing < min_dist)
then
214 call s_mpi_abort(
"Error :: Particle cloud is too dense for lattice packing; " &
215 & //
"reduce num_particles or min_spacing, or enlarge the cloud region")
220 row_dy = spacing*sqrt(3._wp)/2._wp
222 do while (n_placed < n_target)
223 py = ymin + real(row, wp)*row_dy
225 if (mod(row, 2) == 1) x0 = xmin + 0.5_wp*spacing
228 do while (px <= xmax .and. n_placed < n_target)
229 call s_add_cloud_particle(cloud_idx, ib_idx, geom, px, py, particle_cloud(cloud_idx)%z_centroid, &
230 & particle_cloud_ibs)
231 n_placed = n_placed + 1
233 px = x0 + real(col, wp)*spacing
239 cell = spacing*sqrt(2._wp)
240 bx_off = [0._wp, 0.5_wp, 0.5_wp, 0._wp]*cell
241 by_off = [0._wp, 0.5_wp, 0._wp, 0.5_wp]*cell
242 bz_off = [0._wp, 0._wp, 0.5_wp, 0.5_wp]*cell
243 ncx = max(1, ceiling((xmax - xmin)/cell))
244 ncy = max(1, ceiling((ymax - ymin)/cell))
246 do while (n_placed < n_target)
250 if (n_placed >= n_target)
exit
252 & ymin + real(jy, wp)*cell + by_off(b), zmin + real(kz, &
253 & wp)*cell + bz_off(b), particle_cloud_ibs)
254 n_placed = n_placed + 1
268 integer,
intent(in) :: cloud_idx, geom
269 integer,
intent(inout) :: ib_idx
270 real(wp),
intent(in) :: px, py, pz
271 type(ib_patch_parameters),
intent(inout),
dimension(:) :: particle_cloud_ibs
276 particle_cloud_ibs(ib_idx)%gbl_patch_id = ib_idx
277 particle_cloud_ibs(ib_idx)%geometry = geom
278 particle_cloud_ibs(ib_idx)%x_centroid = px
279 particle_cloud_ibs(ib_idx)%y_centroid = py
280 particle_cloud_ibs(ib_idx)%z_centroid = pz
281 particle_cloud_ibs(ib_idx)%step_x_centroid = px
282 particle_cloud_ibs(ib_idx)%step_y_centroid = py
283 particle_cloud_ibs(ib_idx)%step_z_centroid = pz
284 particle_cloud_ibs(ib_idx)%angles(:) = 0._wp
285 particle_cloud_ibs(ib_idx)%step_angles(:) = 0._wp
286 particle_cloud_ibs(ib_idx)%vel(:) = 0._wp
287 particle_cloud_ibs(ib_idx)%step_vel(:) = 0._wp
288 particle_cloud_ibs(ib_idx)%angular_vel(:) = 0._wp
289 particle_cloud_ibs(ib_idx)%step_angular_vel(:) = 0._wp
290 particle_cloud_ibs(ib_idx)%force(:) = 0._wp
291 particle_cloud_ibs(ib_idx)%torque(:) = 0._wp
292 particle_cloud_ibs(ib_idx)%centroid_offset(:) = 0._wp
293 particle_cloud_ibs(ib_idx)%rotation_matrix = 0._wp
294 particle_cloud_ibs(ib_idx)%rotation_matrix(1, 1) = 1._wp
295 particle_cloud_ibs(ib_idx)%rotation_matrix(2, 2) = 1._wp
296 particle_cloud_ibs(ib_idx)%rotation_matrix(3, 3) = 1._wp
297 particle_cloud_ibs(ib_idx)%rotation_matrix_inverse = particle_cloud_ibs(ib_idx)%rotation_matrix
298 particle_cloud_ibs(ib_idx)%radius = particle_cloud(cloud_idx)%radius
299 particle_cloud_ibs(ib_idx)%mass = particle_cloud(cloud_idx)%mass
300 particle_cloud_ibs(ib_idx)%moment =
dflt_real
301 particle_cloud_ibs(ib_idx)%moving_ibm = particle_cloud(cloud_idx)%moving_ibm
302 particle_cloud_ibs(ib_idx)%slip = .false.
Generates particle beds by converting particle_cloud patch specifications into individual immersed bo...
impure subroutine, public s_generate_particle_clouds(particle_cloud_ibs)
Generate all particle beds and fill particle_cloud_ibs. Called on all ranks before s_reduce_ib_patch_...
subroutine s_add_cloud_particle(cloud_idx, ib_idx, geom, px, py, pz, particle_cloud_ibs)
Writes a single placed particle into particle_cloud_ibs at the next free slot, advancing ib_idx....
real(wp) function f_xorshift(seed)
Xorshift PRNG. Advances seed in-place and returns a value in [0, 1).
integer function f_bin_hash(bx, by, bz, hash_size)
Hash bin coordinates to a 1-indexed slot in [1, hash_size]. Uses large prime multipliers to spread bi...
subroutine s_particle_cloud_lattice(cloud_idx, ib_idx, particle_cloud_ibs)
Places particles on the optimally dense lattice for the cloud region: a triangular lattice in 2D,...
subroutine s_particle_cloud_random_box(cloud_idx, ib_idx, particle_cloud_ibs)
Generates a random distributions of particles in a box with a minimum spacing.