MFC
Exascale flow solver
Loading...
Searching...
No Matches
m_perturbation.fpp.f90
Go to the documentation of this file.
1# 1 "/home/runner/work/MFC/MFC/src/pre_process/m_perturbation.fpp"
2!>
3!! @file
4!! @brief Contains module m_perturbation
5
6!> @brief Perturbs initial mean flow fields with random noise, mixing-layer instabilities, or simplex noise
8
9 use m_derived_types ! Definitions of the derived types
10
11 use m_global_parameters ! Global parameters for the code
12
13 use m_mpi_proxy !< message passing interface (mpi) module proxy
14
15 use m_boundary_common ! Boundary conditions module
16
17 use m_helper
18
20
21 use ieee_arithmetic
22
23 implicit none
24
25 real(wp), allocatable, dimension(:, :, :, :) :: q_prim_temp
26
27contains
28
29 !> @brief Allocates the temporary primitive variable array used by elliptic smoothing.
31
32 if (elliptic_smoothing) then
33 allocate (q_prim_temp(0:m, 0:n, 0:p, 1:sys_size))
34 end if
35
37
38 !> @brief Randomly perturbs partial density fields at the interface of a spherical volume fraction region.
39 impure subroutine s_perturb_sphere(q_prim_vf)
40 type(scalar_field), dimension(sys_size), intent(inout) :: q_prim_vf
41 integer :: i, j, k, l !< generic loop operators
42
43 real(wp) :: perturb_alpha
44
45 real(wp) :: rand_real
46 call random_seed()
47
48 do k = 0, p
49 do j = 0, n
50 do i = 0, m
51 call random_number(rand_real)
52
53 perturb_alpha = q_prim_vf(e_idx + perturb_sph_fluid)%sf(i, j, k)
54
55 ! Perturb partial density fields to match perturbed volume fraction fields
56 ! IF ((perturb_alpha >= 25e-2_wp) .AND. (perturb_alpha <= 75e-2_wp)) THEN
57 if ((.not. f_approx_equal(perturb_alpha, 0._wp)) .and. (.not. f_approx_equal(perturb_alpha, 1._wp))) then
58
59 ! Derive new partial densities
60 do l = 1, num_fluids
61 q_prim_vf(l)%sf(i, j, k) = q_prim_vf(e_idx + l)%sf(i, j, k)*fluid_rho(l)
62 end do
63
64 end if
65 end do
66 end do
67 end do
68
69 end subroutine s_perturb_sphere
70
71 !> @brief Adds random noise to the velocity and void fraction of the surrounding flow field.
72 impure subroutine s_perturb_surrounding_flow(q_prim_vf)
73 type(scalar_field), dimension(sys_size), intent(inout) :: q_prim_vf
74 integer :: i, j, k !< generic loop iterators
75
76 real(wp) :: perturb_alpha
77 real(wp) :: rand_real
78 call random_seed()
79
80 ! Perturb partial density or velocity of surrounding flow by some random small amount of noise
81 do k = 0, p
82 do j = 0, n
83 do i = 0, m
84 perturb_alpha = q_prim_vf(e_idx + perturb_flow_fluid)%sf(i, j, k)
85 call random_number(rand_real)
86 rand_real = rand_real*perturb_flow_mag
87 q_prim_vf(mom_idx%beg)%sf(i, j, k) = (1._wp + rand_real)*q_prim_vf(mom_idx%beg)%sf(i, j, k)
88 q_prim_vf(mom_idx%end)%sf(i, j, k) = rand_real*q_prim_vf(mom_idx%beg)%sf(i, j, k)
89 if (bubbles_euler) then
90 q_prim_vf(alf_idx)%sf(i, j, k) = (1._wp + rand_real)*q_prim_vf(alf_idx)%sf(i, j, k)
91 end if
92 end do
93 end do
94 end do
95 end subroutine s_perturb_surrounding_flow
96
97 !> @brief Iteratively smooths all primitive variable fields using a discrete elliptic (Laplacian) filter.
98 impure subroutine s_elliptic_smoothing(q_prim_vf, bc_type)
99
100 type(scalar_field), dimension(sys_size), intent(inout) :: q_prim_vf
101 type(integer_field), dimension(1:num_dims, 1:2), intent(in) :: bc_type
102 integer :: i, j, k, l, q
103
105
106 ! Communication of buffer regions and apply boundary conditions
107 call s_populate_variables_buffers(bc_type, q_prim_vf, pb%sf, mv%sf)
108
109 ! Perform smoothing and store in temp array
110 if (n == 0) then
111 do j = 0, m
112 do i = 1, sys_size
113 q_prim_temp(j, 0, 0, i) = (1._wp/4._wp)* &
114 (q_prim_vf(i)%sf(j + 1, 0, 0) + q_prim_vf(i)%sf(j - 1, 0, 0) + &
115 2._wp*q_prim_vf(i)%sf(j, 0, 0))
116 end do
117 end do
118 else if (p == 0) then
119 do k = 0, n
120 do j = 0, m
121 do i = 1, sys_size
122 q_prim_temp(j, k, 0, i) = (1._wp/8._wp)* &
123 (q_prim_vf(i)%sf(j + 1, k, 0) + q_prim_vf(i)%sf(j - 1, k, 0) + &
124 q_prim_vf(i)%sf(j, k + 1, 0) + q_prim_vf(i)%sf(j, k - 1, 0) + &
125 4._wp*q_prim_vf(i)%sf(j, k, 0))
126 end do
127 end do
128 end do
129 else
130 do l = 0, p
131 do k = 0, n
132 do j = 0, m
133 do i = 1, sys_size
134 q_prim_temp(j, k, l, i) = (1._wp/12._wp)* &
135 (q_prim_vf(i)%sf(j + 1, k, l) + q_prim_vf(i)%sf(j - 1, k, l) + &
136 q_prim_vf(i)%sf(j, k + 1, l) + q_prim_vf(i)%sf(j, k - 1, l) + &
137 q_prim_vf(i)%sf(j, k, l + 1) + q_prim_vf(i)%sf(j, k, l - 1) + &
138 6._wp*q_prim_vf(i)%sf(j, k, l))
139 end do
140 end do
141 end do
142 end do
143 end if
144
145 ! Copy smoothed data back to array of scalar fields
146 do l = 0, p
147 do k = 0, n
148 do j = 0, m
149 do i = 1, sys_size
150 q_prim_vf(i)%sf(j, k, l) = q_prim_temp(j, k, l, i)
151 end do
152 end do
153 end do
154 end do
155 end do
156
157 end subroutine s_elliptic_smoothing
158
159 !> @brief Perturbs velocity and volume fraction fields using multi-octave simplex noise.
160 subroutine s_perturb_simplex(q_prim_vf)
161
162 type(scalar_field), dimension(sys_size), intent(inout) :: q_prim_vf
163 real(wp) :: mag, freq, scale, vel_rsm
164 real(wp), dimension(:, :), allocatable :: ofs
165 integer :: nOffsets
166 real(wp) :: xl, yl, zl
167
168 integer :: i, j, k, l, q
169
170 noffsets = max(num_dims, num_fluids)
171
172 allocate (ofs(noffsets, num_dims))
173
174 ! Store offsets
175 do i = 1, num_dims
176 do j = 1, num_dims
177 ofs(j, i) = simplex_params%perturb_vel_offset(j, i)
178 end do
179 end do
180
181 ! Perturb velocities
182 do i = 1, num_dims
183 if (simplex_params%perturb_vel(i)) then
184 freq = simplex_params%perturb_vel_freq(i)
185 scale = simplex_params%perturb_vel_scale(i)
186 do l = 0, p
187 do k = 0, n
188 do j = 0, m
189 xl = freq*(x_cc(j) + ofs(i, 1))
190 yl = freq*(y_cc(k) + ofs(i, 2))
191 if (num_dims == 2) then
192 mag = f_simplex2d(xl, yl)
193 elseif (num_dims == 3) then
194 zl = freq*(z_cc(l) + ofs(i, 3))
195 mag = f_simplex3d(xl, yl, zl)
196 end if
197
198 vel_rsm = 0._wp
199 do q = 1, num_dims
200 vel_rsm = vel_rsm + q_prim_vf(momxb + q - 1)%sf(j, k, l)**2._wp
201 end do
202 vel_rsm = sqrt(vel_rsm)
203
204 q_prim_vf(momxb + i - 1)%sf(j, k, l) = q_prim_vf(momxb + i - 1)%sf(j, k, l) + &
205 vel_rsm*scale*mag
206 end do
207 end do
208 end do
209 end if
210 end do
211
212 ! Store offsets
213 do i = 1, num_dims
214 do j = 1, num_fluids
215 ofs(j, i) = simplex_params%perturb_dens_offset(j, i)
216 end do
217 end do
218
219 ! Perturb densities
220 do i = 1, num_fluids
221 if (simplex_params%perturb_dens(i)) then
222 freq = simplex_params%perturb_dens_freq(i)
223 scale = simplex_params%perturb_dens_scale(i)
224 do l = 0, p
225 do k = 0, n
226 do j = 0, m
227 xl = freq*(x_cc(j) + ofs(i, 1))
228 yl = freq*(y_cc(k) + ofs(i, 2))
229 if (num_dims == 2) then
230 mag = f_simplex2d(xl, yl)
231 elseif (num_dims == 3) then
232 zl = freq*(z_cc(l) + ofs(i, 3))
233 mag = f_simplex3d(xl, yl, zl)
234 end if
235 q_prim_vf(contxb + i - 1)%sf(j, k, l) = q_prim_vf(contxb + i - 1)%sf(j, k, l) + &
236 q_prim_vf(contxb + i - 1)%sf(j, k, l)*scale*mag
237 end do
238 end do
239 end do
240 end if
241 end do
242
243 deallocate (ofs)
244
245 end subroutine s_perturb_simplex
246
247 !> This subroutine computes velocity perturbations for a temporal mixing
248 !! layer with a hyperbolic tangent mean streamwise velocity
249 !! profile, using an inverter version of the spectrum-based
250 !! synthetic turbulence generation method proposed by
251 !! Guo et al. (2023, JFM).
252 subroutine s_perturb_mixlayer(q_prim_vf)
253 type(scalar_field), dimension(sys_size), intent(inout) :: q_prim_vf
254 real(wp), dimension(mixlayer_perturb_nk) :: k, Ek
255 real(wp), dimension(3, 3) :: Rij, Lmat
256 real(wp), dimension(3) :: velfluc, sig_tmp, sig, khat, xi
257 real(wp) :: dk, alpha, Eksum, q, uu0, phi
258 integer :: i, j, l, r, ierr
259
260 ! Initialize parameters
261 dk = 1._wp/mixlayer_perturb_nk
262
263 ! Compute prescribed energy spectra
264 eksum = 0._wp
265 do i = 1, mixlayer_perturb_nk
266 k(i) = dk*i
267 ek(i) = (k(i)/mixlayer_perturb_k0)**4._wp*exp(-2._wp*(k(i)/mixlayer_perturb_k0)**2._wp)
268 eksum = eksum + ek(i)
269 end do
270
271 ! Main loop
272 do r = 0, n
273 ! Compute prescribed Reynolds stress tensor with about half
274 ! magnitude of its self-similar value
275 rij(:, :) = 0._wp
276 uu0 = patch_icpp(1)%vel(1)**2._wp &
277 *(1._wp - tanh(y_cc(r)*mixlayer_vel_coef)**2._wp)
278 rij(1, 1) = 0.05_wp*uu0
279 rij(2, 2) = 0.03_wp*uu0
280 rij(3, 3) = 0.03_wp*uu0
281 rij(1, 2) = -0.02_wp*uu0
282 rij(2, 1) = rij(1, 2)
283
284 ! Cholesky decomposition for inhomogeneity and anisotropy
285 lmat = 0._wp
286 lmat(1, 1) = sqrt(rij(1, 1))
287 if (abs(lmat(1, 1)) < sgm_eps) lmat(1, 1) = sgm_eps
288 lmat(2, 1) = rij(2, 1)/lmat(1, 1)
289 lmat(2, 2) = sqrt(rij(2, 2) - lmat(2, 1)**2._wp)
290 if (abs(lmat(2, 2)) < sgm_eps) lmat(2, 2) = sgm_eps
291 lmat(3, 1) = rij(3, 1)/lmat(1, 1)
292 lmat(3, 2) = (rij(3, 2) - lmat(3, 1)*lmat(2, 1))/lmat(2, 2)
293 lmat(3, 3) = sqrt(rij(3, 3) - lmat(3, 1)**2._wp - lmat(3, 2)**2._wp)
294
295 ! Compute perturbation for each Fourier component
296 do i = 1, mixlayer_perturb_nk
297 ! Generate random numbers for unit wavevector khat,
298 ! random unit vector xi, and random mode phase phi
299 if (proc_rank == 0) then
300 call s_generate_random_perturbation(khat, xi, phi, i, y_cc(r))
301 end if
302
303#ifdef MFC_MPI
304 call mpi_bcast(khat, 3, mpi_p, 0, mpi_comm_world, ierr)
305 call mpi_bcast(xi, 3, mpi_p, 0, mpi_comm_world, ierr)
306 call mpi_bcast(phi, 1, mpi_p, 0, mpi_comm_world, ierr)
307#endif
308
309 ! Compute mode direction by two-time cross product
310 sig_tmp = f_cross(xi, khat)
311 sig_tmp = sig_tmp/sqrt(sum(sig_tmp**2._wp))
312 sig = f_cross(khat, sig_tmp)
313
314 ! Compute perturbation for each grid
315 do l = 0, p
316 do j = 0, m
317 q = sqrt(ek(i)/eksum)
318 alpha = k(i)*(khat(1)*x_cc(j) + khat(2)*y_cc(r) + khat(3)*z_cc(l)) + 2._wp*pi*phi
319 velfluc = 2._wp*q*sig*cos(alpha)
320 velfluc = matmul(lmat, velfluc)
321 q_prim_vf(momxb)%sf(j, r, l) = q_prim_vf(momxb)%sf(j, r, l) + velfluc(1)
322 q_prim_vf(momxb + 1)%sf(j, r, l) = q_prim_vf(momxb + 1)%sf(j, r, l) + velfluc(2)
323 q_prim_vf(momxb + 2)%sf(j, r, l) = q_prim_vf(momxb + 2)%sf(j, r, l) + velfluc(3)
324 end do
325 end do
326 end do
327 end do
328
329 end subroutine s_perturb_mixlayer
330
331 !> @brief Generates deterministic pseudo-random wave vector, polarization, and phase for a perturbation mode.
332 subroutine s_generate_random_perturbation(khat, xi, phi, ik, yloc)
333 integer, intent(in) :: ik
334 real(wp), intent(in) :: yloc
335 real(wp), dimension(3), intent(out) :: khat, xi
336 real(wp), intent(out) :: phi
337 real(wp) :: theta, eta
338 integer :: seed, kfac, yfac
339
340 kfac = ik*amplifier
341 yfac = nint((sin(yloc) + 1._wp)*amplifier)
342 seed = nint(0.5_wp*modmul(kfac) + 0.5_wp*modmul(yfac))
343
344 call s_prng(theta, seed)
345 call s_prng(eta, seed)
346 khat = f_unit_vector(theta, eta)
347
348 call s_prng(theta, seed)
349 call s_prng(eta, seed)
350 xi = f_unit_vector(theta, eta)
351
352 call s_prng(phi, seed)
353
354 end subroutine s_generate_random_perturbation
355
356 !> @brief Generates a unit vector uniformly distributed on the sphere from two random parameters.
357 function f_unit_vector(theta, eta) result(vec)
358 real(wp), intent(in) :: theta, eta
359 real(wp) :: zeta, xi
360 real(wp), dimension(3) :: vec
361
362 xi = 2._wp*pi*theta
363 zeta = acos(2._wp*eta - 1._wp)
364 vec(1) = sin(zeta)*cos(xi)
365 vec(2) = sin(zeta)*sin(xi)
366 vec(3) = cos(zeta)
367
368 end function f_unit_vector
369
370 !> This function generates a pseudo-random number between 0 and 1 based on
371 !! linear congruential generator.
372 subroutine s_prng(var, seed)
373 integer, intent(inout) :: seed
374 real(wp), intent(out) :: var
375 integer :: i
376
377 seed = mod(modmul(seed), modulus)
378 var = seed/real(modulus, wp)
379
380 end subroutine s_prng
381
382 !> @brief Computes a modular multiplication step for the linear congruential pseudo-random number generator.
383 function modmul(a) result(val)
384 integer, intent(in) :: a
385 integer :: val
386 real(wp) :: x, y
387
388 x = (multiplier/real(modulus, wp))*a + (increment/real(modulus, wp))
389 y = nint((x - floor(x))*decimal_trim)/decimal_trim
390 val = nint(y*modulus)
391
392 end function modmul
393
394 !> @brief Deallocates the temporary primitive variable array used by elliptic smoothing.
396
397 if (elliptic_smoothing) then
398 deallocate (q_prim_temp)
399 end if
400
401 end subroutine s_finalize_perturbation_module
402
403end module m_perturbation
integer, intent(in) k
integer, intent(in) j
integer, intent(in) l
Noncharacteristic and processor boundary condition application for ghost cells and buffer regions.
impure subroutine, public s_populate_variables_buffers(bc_type, q_prim_vf, pb_in, mv_in)
The purpose of this procedure is to populate the buffers of the primitive variables,...
Shared derived types for field data, patch geometry, bubble dynamics, and MPI I/O structures.
Defines global parameters for the computational domain, simulation algorithm, and initial conditions.
real(wp) perturb_flow_mag
Magnitude of perturbation with perturb_flow flag.
real(wp) mixlayer_perturb_k0
Peak wavenumber of prescribed energy spectra with mixlayer_perturb flag Default value (k0 = 0....
integer perturb_flow_fluid
Fluid to be perturbed with perturb_flow flag.
type(int_bounds_info) mom_idx
Indexes of first & last momentum eqns.
integer num_fluids
Number of different fluids present in the flow.
real(wp), dimension(:), allocatable y_cc
integer proc_rank
Rank of the local processor.
integer sys_size
Number of unknowns in the system of equations.
type(simplex_noise_params) simplex_params
integer num_dims
Number of spatial dimensions.
real(wp), dimension(:), allocatable x_cc
integer mixlayer_perturb_nk
Number of Fourier modes for perturbation with mixlayer_perturb flag.
integer perturb_sph_fluid
Fluid to be perturbed with perturb_sph flag.
real(wp), dimension(num_fluids_max) fluid_rho
real(wp), dimension(:), allocatable z_cc
Locations of cell-centers (cc) in x-, y- and z-directions, respectively.
type(ic_patch_parameters), dimension(num_patches_max) patch_icpp
Database of the initial condition patch parameters (icpp) for each of the patches employed in the con...
integer e_idx
Index of total energy equation.
real(wp) mixlayer_vel_coef
Coefficient for the hyperbolic tangent streamwise velocity profile.
integer alf_idx
Index of void fraction.
Utility routines for bubble model setup, coordinate transforms, array sampling, and special functions...
pure real(wp) function, dimension(3), public f_cross(a, b)
This procedure computes the cross product of two vectors.
Broadcasts user inputs and decomposes the domain across MPI ranks for pre-processing.
Perturbs initial mean flow fields with random noise, mixing-layer instabilities, or simplex noise.
impure subroutine s_elliptic_smoothing(q_prim_vf, bc_type)
Iteratively smooths all primitive variable fields using a discrete elliptic (Laplacian) filter.
integer function modmul(a)
Computes a modular multiplication step for the linear congruential pseudo-random number generator.
impure subroutine s_perturb_sphere(q_prim_vf)
Randomly perturbs partial density fields at the interface of a spherical volume fraction region.
impure subroutine s_perturb_surrounding_flow(q_prim_vf)
Adds random noise to the velocity and void fraction of the surrounding flow field.
real(wp) function, dimension(3) f_unit_vector(theta, eta)
Generates a unit vector uniformly distributed on the sphere from two random parameters.
subroutine s_generate_random_perturbation(khat, xi, phi, ik, yloc)
Generates deterministic pseudo-random wave vector, polarization, and phase for a perturbation mode.
subroutine s_prng(var, seed)
This function generates a pseudo-random number between 0 and 1 based on linear congruential generator...
real(wp), dimension(:, :, :, :), allocatable q_prim_temp
impure subroutine s_finalize_perturbation_module()
Deallocates the temporary primitive variable array used by elliptic smoothing.
impure subroutine s_initialize_perturbation_module()
Allocates the temporary primitive variable array used by elliptic smoothing.
subroutine s_perturb_simplex(q_prim_vf)
Perturbs velocity and volume fraction fields using multi-octave simplex noise.
subroutine s_perturb_mixlayer(q_prim_vf)
This subroutine computes velocity perturbations for a temporal mixing layer with a hyperbolic tangent...
2D and 3D simplex noise generation for procedural initial condition perturbations
real(wp) function, public f_simplex2d(xin, yin)
Evaluates 2D simplex noise at the given coordinates and returns a value in [-1, 1].
real(wp) function, public f_simplex3d(xin, yin, zin)
Evaluates 3D simplex noise at the given coordinates and returns a value in [-1, 1].
Derived type annexing an integer scalar field (SF).
Derived type annexing a scalar field (SF).