9 use mersennetwister_mod,
only : randomnumbersequence
10 use mersennetwister_mod,
only : new_randomnumbersequence
11 use mersennetwister_mod,
only : getrandomreal
12 use mersennetwister_mod,
only : getrandompositiveint
14 use iso_fortran_env,
only : stdout=>output_unit, stderr=>error_unit
16 implicit none ;
private
18 public :: random_0d_constructor
21 public :: random_2d_constructor
22 public :: random_2d_01
23 public :: random_2d_norm
24 public :: random_unit_tests
26 #include <MOM_memory.h>
29 type,
public ::
prng ;
private
32 type(randomnumbersequence) :: stream0d
35 type(randomnumbersequence),
dimension(:,:),
allocatable :: stream2d
42 real function random_01(CS)
43 type(
prng),
intent(inout) :: cs
45 random_01 = getrandomreal(cs%stream0d)
47 end function random_01
50 real function random_norm(CS)
51 type(
prng),
intent(inout) :: cs
55 random_norm = getrandomreal(cs%stream0d) - 0.5
57 random_norm = random_norm + ( getrandomreal(cs%stream0d) - 0.5 )
60 end function random_norm
63 subroutine random_2d_01(CS, HI, rand)
64 type(
prng),
intent(inout) :: cs
66 real,
dimension(SZI_(HI),SZJ_(HI)),
intent(out) :: rand
72 rand(i,j) = getrandomreal( cs%stream2d(i,j) )
76 end subroutine random_2d_01
80 subroutine random_2d_norm(CS, HI, rand)
81 type(
prng),
intent(inout) :: cs
83 real,
dimension(SZI_(HI),SZJ_(HI)),
intent(out) :: rand
89 rand(i,j) = getrandomreal( cs%stream2d(i,j) ) - 0.5
93 rand(i,j) = rand(i,j) + ( getrandomreal( cs%stream2d(i,j) ) - 0.5 )
98 end subroutine random_2d_norm
101 subroutine random_0d_constructor(CS, Time, seed)
102 type(
prng),
intent(inout) :: cs
103 type(time_type),
intent(in) :: time
104 integer,
intent(in) :: seed
108 tseed = seed_from_time(time)
109 tseed = ieor(tseed, seed)
110 cs%stream0d = new_randomnumbersequence(tseed)
112 end subroutine random_0d_constructor
115 subroutine random_2d_constructor(CS, HI, Time, seed)
116 type(
prng),
intent(inout) :: cs
118 type(time_type),
intent(in) :: time
119 integer,
intent(in) :: seed
121 integer :: i,j,sseed,tseed
123 if (.not.
allocated(cs%stream2d))
allocate( cs%stream2d(hi%isd:hi%ied,hi%jsd:hi%jed) )
125 tseed = seed_from_time(time)
126 tseed = ieor(tseed*9007, seed)
129 sseed = seed_from_index(hi, i, j)
130 sseed = ieor(tseed, sseed*7993)
131 cs%stream2d(i,j) = new_randomnumbersequence(sseed)
135 end subroutine random_2d_constructor
138 integer function seed_from_time(Time)
139 type(time_type),
intent(in) :: time
141 integer :: yr,mo,dy,hr,mn,sc,s1,s2
143 call get_date(time,yr,mo,dy,hr,mn,sc)
144 s1 = sc + 61*(mn + 61*hr) + 379
148 s2 = modulo(dy + 32*(mo + 13*yr), 2147483647_4)
149 seed_from_time = ieor(s1*4111, s2)
151 end function seed_from_time
154 integer function seed_from_index(HI, i, j)
156 integer,
intent(in) :: i
157 integer,
intent(in) :: j
159 integer :: ig, jg, ni, nj, ij
164 ig = mod(hi%idg_offset + i - 1 + ni, ni)+1
165 jg = max(hi%jdg_offset + j, 0)
170 seed_from_index = ig + ni*(jg-1)
172 end function seed_from_index
175 subroutine random_destruct(CS)
176 type(
prng),
pointer :: CS
178 if (
allocated(cs%stream2d))
deallocate(cs%stream2d)
180 end subroutine random_destruct
183 logical function random_unit_tests(verbose)
186 type(
prng) :: test_rng
187 type(time_type) :: time
189 real :: mean, var, ar1, std
191 integer,
parameter :: n_samples = 800
192 integer :: i, j, ni, nj
195 real,
dimension(:,:),
allocatable :: r2d
210 random_unit_tests = .false.
212 write(stdunit,
'(1x,a)')
'==== MOM_random: random_unit_tests ======================='
214 if (verbose)
write(stdunit,
'(1x,"random: ",a)')
'-- Time-based seeds ---------------------'
216 time = set_date(1903, 11, 21, 13, 47, 29)
217 i = seed_from_time(time)
218 random_unit_tests = random_unit_tests .or. &
219 test_fn(verbose, i==212584341,
'time seed 1903/11/21 13:47:29', ivalue=i)
220 time = set_date(1903, 11, 22, 13, 47, 29)
221 i = seed_from_time(time)
222 random_unit_tests = random_unit_tests .or.&
223 test_fn(verbose, i==212584342,
'time seed 1903/11/22 13:47:29', ivalue=i)
224 time = set_date(1903, 11, 21, 13, 47, 30)
225 i = seed_from_time(time)
226 random_unit_tests = random_unit_tests .or.&
227 test_fn(verbose, i==212596634,
'time seed 1903/11/21 13:47:30', ivalue=i)
229 if (verbose)
write(stdunit,
'(1x,"random: ",a)')
'-- PRNG tests ---------------------------'
231 call random_0d_constructor(test_rng, time, 1)
232 r1 = random_01(test_rng)
233 random_unit_tests = random_unit_tests .or. &
234 test_fn(verbose, abs(r1-4.75310122e-2)<1.e-9,
'first call', r1)
237 r2 = random_01(test_rng)
238 random_unit_tests = random_unit_tests .or. &
239 test_fn(verbose, abs(r2-2.71289742e-1)<1.e-9,
'consecutive test', r2)
242 call random_0d_constructor(test_rng, time, 1)
243 r2 = random_01(test_rng)
244 random_unit_tests = random_unit_tests .or. &
245 test_fn(verbose, abs(r2-r1)==0.,
'reproduce test', r2)
248 call random_0d_constructor(test_rng, time, 2)
249 r2 = random_01(test_rng)
250 random_unit_tests = random_unit_tests .or. &
251 test_fn(verbose, abs(r2-7.15508473e-1)<1.e-9,
'different seed test', r2)
254 time = set_date(1903, 11, 21, 13, 0, 29)
255 call random_0d_constructor(test_rng, time, 1)
256 r2 = random_01(test_rng)
257 random_unit_tests = random_unit_tests .or. &
258 test_fn(verbose, abs(r2-9.56667163e-1)<1.e-9,
'different date test', r2)
260 if (verbose)
write(stdunit,
'(1x,"random: ",a)')
'-- index-based seeds --------------------'
262 i = seed_from_index(hi,1,1)
263 random_unit_tests = random_unit_tests .or. test_fn(verbose, i==1,
'seed from index (1,1)', ivalue=i)
264 j = seed_from_index(hi,ni+1,1)
265 random_unit_tests = random_unit_tests .or. test_fn(verbose, j==i,
'seed from index (n+1,1)', ivalue=j)
266 i = seed_from_index(hi,ni,1)
267 random_unit_tests = random_unit_tests .or. test_fn(verbose, i==6,
'seed from index (n,1)', ivalue=i)
268 j = seed_from_index(hi,0,1)
269 random_unit_tests = random_unit_tests .or. test_fn(verbose, j==i,
'seed from index (0,1)', ivalue=j)
270 i = seed_from_index(hi,1,nj)
271 random_unit_tests = random_unit_tests .or. test_fn(verbose, i==49,
'seed from index (1,n)', ivalue=i)
272 j = seed_from_index(hi,ni,nj+1)
273 random_unit_tests = random_unit_tests .or. test_fn(verbose, j==i,
'seed from index (n,n+1)', ivalue=j)
274 i = seed_from_index(hi,ni,nj)
275 random_unit_tests = random_unit_tests .or. test_fn(verbose, i==54,
'seed from index (n,n)', ivalue=i)
276 j = seed_from_index(hi,1,nj+1)
277 random_unit_tests = random_unit_tests .or. test_fn(verbose, j==i,
'seed from index (1,n+1)', ivalue=j)
279 if (.not.random_unit_tests)
write(stdunit,
'(1x,a)')
'Passed unit tests'
284 mean = 0. ; var = 0. ; ar1 = 0. ; r2 = 0.
286 r1 = random_01(test_rng) - 0.5
292 mean = mean / real(n_samples)
293 var = var / real(n_samples)
294 ar1 = ar1 / real(n_samples-1)
296 r2 = mean*sqrt(real(12*n_samples))
298 r1 = ( ar1 * sqrt(real(n_samples-1)) ) / var
300 write(stdunit,
'(1x,"random: ",a)')
'-- Uniform -0.5 .. 0.5 generator --------'
301 write(stdunit,
'(1x,"random: ",a,f12.9)')
'mean =',mean,
'std =',std,
'AR1 =',ar1
302 write(stdunit,
'(1x,"random: ",a,f12.9)')
'norm. mean =',r2, &
303 'norm. std =',r3,
'norm. AR1 =',r1
305 random_unit_tests = random_unit_tests .or. &
306 test_fn(verbose, abs(r2)<2., &
307 'n>>1, mean within 2 sigma [uniform]', r2)
308 random_unit_tests = random_unit_tests .or. &
309 test_fn(verbose, abs(r3-1.)<1./sqrt(real(n_samples)), &
310 'n>>1, std ~ 1/sqrt(12) [uniform]', r3-1.)
311 random_unit_tests = random_unit_tests .or. &
312 test_fn(verbose, abs(r1)<2., &
313 'n>>1, AR1 < std/sqrt(n) [uniform]', r1)
316 mean = 0. ; var = 0. ; ar1 = 0. ; r2 = 0.
318 r1 = random_norm(test_rng)
324 mean = mean / real(n_samples)
325 var = var / real(n_samples)
326 ar1 = ar1 / real(n_samples)
328 r3 = 1./sqrt(real(n_samples))
329 r2 = mean*sqrt(real(n_samples))
331 r1 = ( ar1 * sqrt(real(n_samples-1)) ) / var
333 write(stdunit,
'(1x,"random: ",a)')
'-- Normal distribution generator --------'
334 write(stdunit,
'(1x,"random: ",a,f12.9)')
'mean =',mean,
'std =',std,
'AR1 =',ar1
335 write(stdunit,
'(1x,"random: ",a,f12.9)')
'norm. error in mean =',r2, &
336 'norm. standard deviation =',r3,
'norm. AR1 =',r1
338 random_unit_tests = random_unit_tests .or. &
339 test_fn(verbose, abs(r2)<2., &
340 'n>>1, mean within 2 sigma [norm]', r2)
341 random_unit_tests = random_unit_tests .or. &
342 test_fn(verbose, abs(r3-1.)<1./sqrt(real(n_samples)), &
343 'n>>1, std ~ 1 [norm]', r3-1.)
344 random_unit_tests = random_unit_tests .or. &
345 test_fn(verbose, abs(r1)<2., &
346 'n>>1, AR1 < std/sqrt(n) [norm]', r1)
348 if (verbose)
write(stdunit,
'(1x,"random: ",a)')
'-- 2d PRNG ------------------------------'
350 allocate( r2d(hi%isd:hi%ied,hi%jsd:hi%jed) )
351 call random_2d_constructor(test_rng, hi, time, 123)
353 call random_2d_01(test_rng, hi, r2d)
354 if (any(abs(r2d(:,:)+999.)<=0.)) random_unit_tests=.true.
357 random_unit_tests = random_unit_tests .or. test_fn(verbose, r1>=0.,
'2d all set', r1)
358 random_unit_tests = random_unit_tests .or. test_fn(verbose, r2<=1.,
'2d all valid', r2)
359 mean = sum( r2d(1:ni,1:nj) - 0.5 )/real(ni*nj)
360 var = sum( (r2d(1:ni,1:nj) - 0.5 - mean)**2 )/real(ni*nj)
362 r3 = 1./sqrt(real(12*ni*nj))
363 r2 = mean*sqrt(real(12*ni*nj))
366 write(stdunit,
'(1x,"random: ",a)')
'2D uniform 0..1 generator'
367 write(stdunit,
'(1x,"random: ",a,f12.9)')
'mean =',mean,
'std =',std
368 write(stdunit,
'(1x,"random: ",a,f12.9)')
'norm. error in mean =',r2
369 write(stdunit,
'(1x,"random: ",a,f12.9)')
'norm. standard deviation =',r3
371 random_unit_tests = random_unit_tests .or. &
372 test_fn(verbose, abs(r2)<2., &
373 '2d, mean within 2 sigma [uniform]', r2)
374 random_unit_tests = random_unit_tests .or. &
375 test_fn(verbose, abs(r3-1.)<1./sqrt(real(ni*nj)), &
376 '2d, std ~ 1/sqrt(12) [uniform]', r3-1.)
378 write(stdunit,
'(1x,"random:")')
379 write(stdunit,
'(1x,"random:",8f8.5)') r2d
380 write(stdunit,
'(1x,"random:")')
384 call random_2d_norm(test_rng, hi, r2d)
385 mean = sum( r2d(1:ni,1:nj) )/real(ni*nj)
386 var = sum( r2d(1:ni,1:nj)**2 )/real(ni*nj)
388 r3 = 1./sqrt(real(ni*nj))
389 r2 = mean*sqrt(real(ni*nj))
392 write(stdunit,
'(1x,"random: ",a)')
'2D normal generator'
393 write(stdunit,
'(1x,"random: ",a,f12.9)')
'mean =',mean,
'std =',std
394 write(stdunit,
'(1x,"random: ",a,f12.9)')
'norm. error in mean =',r2
395 write(stdunit,
'(1x,"random: ",a,f12.9)')
'norm. standard deviation =',r3
397 random_unit_tests = random_unit_tests .or. &
398 test_fn(verbose, abs(r2)<2., &
399 '2d, mean within 2 sigma [norm]', r2)
400 random_unit_tests = random_unit_tests .or. &
401 test_fn(verbose, abs(r3-1.)<1./sqrt(real(ni*nj)), &
402 '2d, std ~ 1/sqrt(12) [norm]', r3-1.)
408 if (.not.random_unit_tests)
write(stdunit,
'(1x,a)')
'Passed statistical tests'
410 end function random_unit_tests
413 logical function test_fn(verbose, good, label, rvalue, ivalue)
414 logical,
intent(in) :: verbose
415 logical,
intent(in) :: good
416 character(len=*),
intent(in) :: label
417 real,
intent(in) :: rvalue
418 integer,
intent(in) :: ivalue
419 optional :: rvalue, ivalue
421 if (
present(ivalue))
then
423 write(stdout,
'(1x,a,i10,1x,a,a)')
'random: result =',ivalue,label,
' <------- FAIL!'
424 write(stderr,
'(1x,a,i10,1x,a,a)')
'random: result =',ivalue,label,
' <------- FAIL!'
425 elseif (verbose)
then
426 write(stdout,
'(1x,a,i10,1x,a)')
'random: result =',ivalue,label
430 write(stdout,
'(1x,a,1pe15.8,1x,a,a)')
'random: result =',rvalue,label,
' <------- FAIL!'
431 write(stderr,
'(1x,a,1pe15.8,1x,a,a)')
'random: result =',rvalue,label,
' <------- FAIL!'
432 elseif (verbose)
then
433 write(stdout,
'(1x,a,1pe15.8,1x,a)')
'random: result =',rvalue,label