MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_random.t
Go to the documentation of this file.
1!> Module for pseudo random number generation. The internal pseudo random
2!> generator is the xoroshiro128plus method.
4
5#include "amrvac.h"
6
7 implicit none
8 private
9
10 ! A 64 bit floating point type
11 integer, parameter :: dp = kind(0.0d0)
12
13 ! A 32 bit integer type
14 integer, parameter :: i4 = selected_int_kind(9)
15
16 ! A 64 bit integer type
17 integer, parameter :: i8 = selected_int_kind(18)
18
19 !> Random number generator type, which contains the state
20 type rng_t
21 !> The rng state (always use your own seed)
22 integer(i8), private :: s(2) = [123456789_i8, 987654321_i8]
23 contains
24 procedure, non_overridable :: set_seed ! Seed the generator
25 procedure, non_overridable :: jump ! Jump function (see below)
26 procedure, non_overridable :: int_4 ! 4-byte random integer
27 procedure, non_overridable :: int_8 ! 8-byte random integer
28 procedure, non_overridable :: unif_01 ! Uniform (0,1] real
29 procedure, non_overridable :: unif_01_vec ! Uniform (0,1] real vector
30 procedure, non_overridable :: normal ! One normal(0,1) number
31 procedure, non_overridable :: two_normals ! Two normal(0,1) samples
32 procedure, non_overridable :: poisson ! Sample from Poisson-dist.
33 procedure, non_overridable :: circle ! Sample on a circle
34 procedure, non_overridable :: sphere ! Sample on a sphere
35 procedure, non_overridable :: next ! Internal method
36 end type rng_t
37
38 !> Parallel random number generator type
39 type prng_t
40 type(rng_t), allocatable :: rngs(:)
41 contains
42 procedure, non_overridable :: init_parallel
43 end type prng_t
44
45 public :: rng_t
46 public :: prng_t
47 public :: poisson_disk_sampling
48
49contains
50
51
52#if defined(__NVCOMPILER) || (defined(USE_INTRINSIC_SHIFT) && USE_INTRINSIC_SHIFT==0)
53
54 !> added for nvidia compilers
55 pure function shiftl(val, shift) result(res_value)
56 integer(i8), intent(in) :: val
57 integer, intent(in) :: shift
58 integer(i8) :: res_value
59 integer(i8) :: bit_mask1, bit_mask2
60
61 ! cannot be initialized with b values in gnu, cannot have the big decimal numbers in nvidia
62 bit_mask1=huge(val)
63 bit_mask2=-bit_mask1-1
64
65
66 if(val<0) then
67 res_value = ior(lshift(iand(val, bit_mask1), shift),bit_mask2)
68 else
69 res_value = lshift(val, shift)
70 endif
71
72 end function shiftl
73
74 pure function shiftr(val, shift) result(res_value)
75 integer(i8), intent(in) :: val
76 integer, intent(in) :: shift
77 integer(i8) :: res_value
78 integer(i8) :: bit_mask1, bit_mask2
79
80 ! cannot be initialized with b values in gnu, cannot have the big decimal numbers in nvidia
81 bit_mask1=huge(val)
82 bit_mask2=-bit_mask1-1
83
84 if(val<0) then
85 res_value = ior(rshift(iand(val, bit_mask1), shift),bit_mask2)
86 else
87 res_value = rshift(val, shift)
88 endif
89
90 end function shiftr
91
92#endif
93
94 !> Initialize a collection of rng's for parallel use
95 subroutine init_parallel(self, n_proc, rng)
96 class(prng_t), intent(inout) :: self
97 type(rng_t), intent(inout) :: rng
98 integer, intent(in) :: n_proc
99 integer :: n
100
101 allocate(self%rngs(n_proc))
102 self%rngs(1) = rng
103
104 do n = 2, n_proc
105 self%rngs(n) = self%rngs(n-1)
106 call self%rngs(n)%jump()
107 end do
108 end subroutine init_parallel
109
110 !> Set a seed for the rng
111 subroutine set_seed(self, the_seed)
112 class(rng_t), intent(inout) :: self
113 integer(i8), intent(in) :: the_seed(2)
114
115 self%s = the_seed
116
117 ! Simulate calls to next() to improve randomness of first number
118 call self%jump()
119 end subroutine set_seed
120
121 ! This is the jump function for the generator. It is equivalent
122 ! to 2^64 calls to next(); it can be used to generate 2^64
123 ! non-overlapping subsequences for parallel computations.
124 subroutine jump(self)
125 class(rng_t), intent(inout) :: self
126 integer :: i, b
127 integer(i8) :: global_time(2), dummy
128
129 ! The signed equivalent of the unsigned constants
130 integer(i8), parameter :: jmp_c(2) = &
131 (/-4707382666127344949_i8, -2852180941702784734_i8/)
132
133 global_time = 0
134 do i = 1, 2
135 do b = 0, 63
136 if (iand(jmp_c(i), shiftl(1_i8, b)) /= 0) then
137 global_time = ieor(global_time, self%s)
138 end if
139 dummy = self%next()
140 end do
141 end do
142
143 self%s = global_time
144 end subroutine jump
145
146 !> Return 4-byte integer
147 integer(i4) function int_4(self)
148 class(rng_t), intent(inout) :: self
149 int_4 = int(self%next(), i4)
150 end function int_4
151
152 !> Return 8-byte integer
153 integer(i8) function int_8(self)
154 class(rng_t), intent(inout) :: self
155 int_8 = self%next()
156 end function int_8
157
158 !> Get a uniform [0,1) random real (double precision)
159 real(dp) function unif_01(self)
160 class(rng_t), intent(inout) :: self
161 integer(i8) :: x
162 real(dp) :: tmp
163
164 x = self%next()
165 x = ior(shiftl(1023_i8, 52), shiftr(x, 12))
166 unif_01 = transfer(x, tmp) - 1.0_dp
167 end function unif_01
168
169 !> Fill array with uniform random numbers
170 subroutine unif_01_vec(self, rr)
171 class(rng_t), intent(inout) :: self
172 real(dp), intent(out) :: rr(:)
173 integer :: i
174
175 do i = 1, size(rr)
176 rr(i) = self%unif_01()
177 end do
178 end subroutine unif_01_vec
179
180 !> Return two normal random variates with mean 0 and variance 1.
181 !> http://en.wikipedia.org/wiki/Marsaglia_polar_method
182 function two_normals(self) result(rands)
183 class(rng_t), intent(inout) :: self
184 real(dp) :: rands(2), sum_sq
185
186 do
187 rands(1) = 2 * self%unif_01() - 1
188 rands(2) = 2 * self%unif_01() - 1
189 sum_sq = sum(rands**2)
190 if (sum_sq < 1.0_dp .and. sum_sq > 0.0_dp) exit
191 end do
192 rands = rands * sqrt(-2 * log(sum_sq) / sum_sq)
193 end function two_normals
194
195 !> Single normal random number
196 real(dp) function normal(self)
197 class(rng_t), intent(inout) :: self
198 real(dp) :: rands(2)
199
200 rands = self%two_normals()
201 normal = rands(1)
202 end function normal
203
204 !> Return Poisson random variate with rate lambda. Works well for lambda < 30
205 !> or so. For lambda >> 1 it can produce wrong results due to roundoff error.
206 function poisson(self, lambda) result(rr)
207 class(rng_t), intent(inout) :: self
208 real(dp), intent(in) :: lambda
209 integer(i4) :: rr
210 real(dp) :: expl, p
211
212 expl = exp(-lambda)
213 rr = 0
214 p = self%unif_01()
215
216 do while (p > expl)
217 rr = rr + 1
218 p = p * self%unif_01()
219 end do
220 end function poisson
221
222 !> Sample point on a circle with given radius
223 function circle(self, radius) result(xy)
224 class(rng_t), intent(inout) :: self
225 real(dp), intent(in) :: radius
226 real(dp) :: rands(2), xy(2)
227 real(dp) :: sum_sq
228
229 ! Method for uniform sampling on circle
230 do
231 rands(1) = 2 * self%unif_01() - 1
232 rands(2) = 2 * self%unif_01() - 1
233 sum_sq = sum(rands**2)
234 if (sum_sq <= 1) exit
235 end do
236
237 xy(1) = (rands(1)**2 - rands(2)**2) / sum_sq
238 xy(2) = 2 * rands(1) * rands(2) / sum_sq
239 xy = xy * radius
240 end function circle
241
242 !> Sample point on a sphere with given radius
243 function sphere(self, radius) result(xyz)
244 class(rng_t), intent(inout) :: self
245 real(dp), intent(in) :: radius
246 real(dp) :: rands(2), xyz(3)
247 real(dp) :: sum_sq, tmp_sqrt
248
249 ! Marsaglia method for uniform sampling on sphere
250 do
251 rands(1) = 2 * self%unif_01() - 1
252 rands(2) = 2 * self%unif_01() - 1
253 sum_sq = sum(rands**2)
254 if (sum_sq <= 1) exit
255 end do
256
257 tmp_sqrt = sqrt(1 - sum_sq)
258 xyz(1:2) = 2 * rands(1:2) * tmp_sqrt
259 xyz(3) = 1 - 2 * sum_sq
260 xyz = xyz * radius
261 end function sphere
262
263 !> Interal routine: get the next value (returned as 64 bit signed integer)
264 function next(self) result(res)
265 class(rng_t), intent(inout) :: self
266 integer(i8) :: res
267 integer(i8) :: global_time(2)
268
269 global_time = self%s
270 res = global_time(1) + global_time(2)
271 global_time(2) = ieor(global_time(1), global_time(2))
272 self%s(1) = ieor(ieor(rotl(global_time(1), 55), global_time(2)), shiftl(global_time(2), 14))
273 self%s(2) = rotl(global_time(2), 36)
274 end function next
275
276 !> Helper function for next()
277 pure function rotl(x, k) result(res)
278 integer(i8), intent(in) :: x
279 integer, intent(in) :: k
280 integer(i8) :: res
281
282 res = ior(shiftl(x, k), shiftr(x, 64 - k))
283 end function rotl
284
285 subroutine poisson_disk_sampling(r_min,bmin1,bmin2,bmax1,bmax2,nmax,points_store,n_setpoints)
286 use mod_constants
287 ! a bigger number than expected total number of sampling points
288 integer, intent(in) :: nmax
289 double precision, intent(in) :: r_min,bmin1,bmin2,bmax1,bmax2
290 double precision, intent(out) :: points_store(nmax,2)
291 integer, intent(out) :: n_setpoints
292
293 double precision :: gsize,x1len,x2len,r2,threer2,r_rand,a_rand,x_rand,y_rand
294 double precision :: unif_random_number(2),candidate(2),distance,random01
295 type(rng_t) :: rng
296 ! the id of a point in a background grid, in which each cell can only contain at most one point
297 integer, allocatable :: id_grid(:,:)
298 integer :: nx1,nx2,ix1,ix2,n_active,id,j,k_candi
299 integer :: id_active_points(nmax),point_index_in_grid(2,nmax)
300 logical :: points_active(nmax)
301 logical :: candidate_found
302
303 k_candi=30
304 id_active_points=0
305 r2=r_min**2
306 threer2=3.d0*r2
307 points_active=.false.
308 ! cell size of the background grid
309 gsize=r_min/sqrt(2.d0)
310 ! size of region to sample
311 x1len=bmax1-bmin1
312 x2len=bmax2-bmin2
313 nx1=ceiling(x1len/gsize)
314 nx2=ceiling(x2len/gsize)
315 allocate(id_grid(nx1,nx2))
316 id_grid=0
317 ! set the first point
318 call rng%unif_01_vec(unif_random_number)
319 points_store(1,1)=unif_random_number(1)*x1len
320 points_store(1,2)=unif_random_number(2)*x2len
321 ix1=ceiling(points_store(1,1)/gsize)
322 ix2=ceiling(points_store(1,2)/gsize)
323 id_grid(ix1,ix2)=1
324 point_index_in_grid(1,1)=ix1
325 point_index_in_grid(2,1)=ix2
326 points_active(1)=.true.
327 n_setpoints=1
328
329 do while(any(points_active))
330 ! generate active point list
331 n_active=0
332 do id=1,nmax
333 if(points_active(id)) then
334 n_active=n_active+1
335 id_active_points(n_active)=id
336 end if
337 end do
338 ! randomly select one point from the active point list
339 id=id_active_points(ceiling(rng%unif_01()*dble(n_active)))
340 do j=1,k_candi
341 candidate_found=.true.
342 r_rand=sqrt(rng%unif_01()*threer2+r2)
343 a_rand=2.d0*dpi*rng%unif_01()
344 x_rand=points_store(id,1)+r_rand*cos(a_rand)
345 y_rand=points_store(id,2)+r_rand*sin(a_rand)
346 if(x_rand<0.d0 .or. x_rand> x1len .or. y_rand<0.d0 .or. y_rand > x2len) cycle
347 do ix2=max(1,point_index_in_grid(2,id)-3),min(nx2,point_index_in_grid(2,id)+3)
348 do ix1=max(1,point_index_in_grid(1,id)-3),min(nx1,point_index_in_grid(1,id)+3)
349 if(id_grid(ix1,ix2)==id) cycle
350 if(id_grid(ix1,ix2)/=0) then
351 distance=sqrt((x_rand-points_store(id_grid(ix1,ix2),1))**2+(y_rand-points_store(id_grid(ix1,ix2),2))**2)
352 if(distance<r_min) then
353 candidate_found=.false.
354 go to 12
355 end if
356 end if
357 end do
358 end do
359 if(candidate_found) then
360 n_setpoints=n_setpoints+1
361 points_store(n_setpoints,1)=x_rand
362 points_store(n_setpoints,2)=y_rand
363 ix1=ceiling(points_store(n_setpoints,1)/gsize)
364 ix2=ceiling(points_store(n_setpoints,2)/gsize)
365 point_index_in_grid(1,n_setpoints)=ix1
366 point_index_in_grid(2,n_setpoints)=ix2
367 id_grid(ix1,ix2)=n_setpoints
368 points_active(n_setpoints)=.true.
369 exit
370 end if
37112 continue
372 end do
373 if(.not.candidate_found) then
374 points_active(id)=.false.
375 end if
376 end do
377 points_store(1:n_setpoints,1)=points_store(1:n_setpoints,1)+bmin1
378 points_store(1:n_setpoints,2)=points_store(1:n_setpoints,2)+bmin2
379 deallocate(id_grid)
380
381 end subroutine poisson_disk_sampling
382
383end module mod_random
Module for physical and numeric constants.
double precision, parameter dpi
Pi.
Module for pseudo random number generation. The internal pseudo random generator is the xoroshiro128p...
Definition mod_random.t:3
pure integer(i8) function shiftl(val, shift)
added for nvidia compilers
Definition mod_random.t:56
subroutine, public poisson_disk_sampling(r_min, bmin1, bmin2, bmax1, bmax2, nmax, points_store, n_setpoints)
Definition mod_random.t:286
Parallel random number generator type.
Definition mod_random.t:39
Random number generator type, which contains the state.
Definition mod_random.t:20