MOM6
mom_random Module Reference

Detailed Description

Provides gridded random number capability.

Provides MOM6 wrappers to the FMS implementation of the Mersenne twister.

Example usage:

type(PRNG) :: rng
real :: rn
call random_0d_constructor(rng, time, seed) ! Call this each time-step
rn = random_01(rng)
rn = random_norm(rng)
type(PRNG) :: rng
real, dimension(:,:) :: rn2d
call random_2d_constructor(rng, hi, time, seed) ! Call this each time-step
call random_2d_01(rng, hi, rn2d)
call random_2d_norm(rng, hi, rn2d)
note: reproducibility across restarts is implemented by using time-derived
seeds to pass to the mersenne twister. it is therefore important that any
prng type be re-initialized each time-step.

Data Types

type  prng
 Container for pseudo-random number generators. More...
 

Functions/Subroutines

real function, public random_01 (CS)
 Returns a random number between 0 and 1. More...
 
real function, public random_norm (CS)
 Returns an approximately normally distributed random number with mean 0 and variance 1. More...
 
subroutine, public random_2d_01 (CS, HI, rand)
 Generates random numbers between 0 and 1 for each cell of the model grid. More...
 
subroutine, public random_2d_norm (CS, HI, rand)
 Returns an approximately normally distributed random number with mean 0 and variance 1 for each cell of the model grid. More...
 
subroutine, public random_0d_constructor (CS, Time, seed)
 Constructor for scalar PRNG. Can be used to reset the sequence. More...
 
subroutine, public random_2d_constructor (CS, HI, Time, seed)
 Constructor for gridded PRNG. Can be used to reset the sequence. More...
 
integer function seed_from_time (Time)
 Return a seed derived as hash of values in Time. More...
 
integer function seed_from_index (HI, i, j)
 Create seed from position index. More...
 
subroutine random_destruct (CS)
 Destructor for PRNG. More...
 
logical function, public random_unit_tests (verbose)
 Runs some statistical tests on the PRNG. More...
 
logical function test_fn (verbose, good, label, rvalue, ivalue)
 Convenience function for reporting result of test. More...
 

Function/Subroutine Documentation

◆ random_01()

real function, public mom_random::random_01 ( type(prng), intent(inout)  CS)

Returns a random number between 0 and 1.

Parameters
[in,out]csContainer for pseudo-random number generators

Definition at line 42 of file MOM_random.F90.

43  type(PRNG), intent(inout) :: CS !< Container for pseudo-random number generators
44 
45  random_01 = getrandomreal(cs%stream0d)
46 

◆ random_0d_constructor()

subroutine, public mom_random::random_0d_constructor ( type(prng), intent(inout)  CS,
type(time_type), intent(in)  Time,
integer, intent(in)  seed 
)

Constructor for scalar PRNG. Can be used to reset the sequence.

Parameters
[in,out]csContainer for pseudo-random number generators
[in]timeCurrent model time
[in]seedSeed for PRNG

Definition at line 101 of file MOM_random.F90.

102  type(PRNG), intent(inout) :: CS !< Container for pseudo-random number generators
103  type(time_type), intent(in) :: Time !< Current model time
104  integer, intent(in) :: seed !< Seed for PRNG
105  ! Local variables
106  integer :: tseed
107 
108  tseed = seed_from_time(time)
109  tseed = ieor(tseed, seed)
110  cs%stream0d = new_randomnumbersequence(tseed)
111 

◆ random_2d_01()

subroutine, public mom_random::random_2d_01 ( type(prng), intent(inout)  CS,
type(hor_index_type), intent(in)  HI,
real, dimension( hi %isd: hi %ied, hi %jsd: hi %jed), intent(out)  rand 
)

Generates random numbers between 0 and 1 for each cell of the model grid.

Parameters
[in,out]csContainer for pseudo-random number generators
[in]hiHorizontal index structure
[out]randRandom numbers between 0 and 1

Definition at line 63 of file MOM_random.F90.

64  type(PRNG), intent(inout) :: CS !< Container for pseudo-random number generators
65  type(hor_index_type), intent(in) :: HI !< Horizontal index structure
66  real, dimension(SZI_(HI),SZJ_(HI)), intent(out) :: rand !< Random numbers between 0 and 1
67  ! Local variables
68  integer :: i,j
69 
70  do j = hi%jsd,hi%jed
71  do i = hi%isd,hi%ied
72  rand(i,j) = getrandomreal( cs%stream2d(i,j) )
73  enddo
74  enddo
75 

◆ random_2d_constructor()

subroutine, public mom_random::random_2d_constructor ( type(prng), intent(inout)  CS,
type(hor_index_type), intent(in)  HI,
type(time_type), intent(in)  Time,
integer, intent(in)  seed 
)

Constructor for gridded PRNG. Can be used to reset the sequence.

Parameters
[in,out]csContainer for pseudo-random number generators
[in]hiHorizontal index structure
[in]timeCurrent model time
[in]seedSeed for PRNG

Definition at line 115 of file MOM_random.F90.

116  type(PRNG), intent(inout) :: CS !< Container for pseudo-random number generators
117  type(hor_index_type), intent(in) :: HI !< Horizontal index structure
118  type(time_type), intent(in) :: Time !< Current model time
119  integer, intent(in) :: seed !< Seed for PRNG
120  ! Local variables
121  integer :: i,j,sseed,tseed
122 
123  if (.not. allocated(cs%stream2d)) allocate( cs%stream2d(hi%isd:hi%ied,hi%jsd:hi%jed) )
124 
125  tseed = seed_from_time(time)
126  tseed = ieor(tseed*9007, seed)
127  do j = hi%jsd,hi%jed
128  do i = hi%isd,hi%ied
129  sseed = seed_from_index(hi, i, j)
130  sseed = ieor(tseed, sseed*7993)
131  cs%stream2d(i,j) = new_randomnumbersequence(sseed)
132  enddo
133  enddo
134 

◆ random_2d_norm()

subroutine, public mom_random::random_2d_norm ( type(prng), intent(inout)  CS,
type(hor_index_type), intent(in)  HI,
real, dimension( hi %isd: hi %ied, hi %jsd: hi %jed), intent(out)  rand 
)

Returns an approximately normally distributed random number with mean 0 and variance 1 for each cell of the model grid.

Parameters
[in,out]csContainer for pseudo-random number generators
[in]hiHorizontal index structure
[out]randRandom numbers between 0 and 1

Definition at line 80 of file MOM_random.F90.

81  type(PRNG), intent(inout) :: CS !< Container for pseudo-random number generators
82  type(hor_index_type), intent(in) :: HI !< Horizontal index structure
83  real, dimension(SZI_(HI),SZJ_(HI)), intent(out) :: rand !< Random numbers between 0 and 1
84  ! Local variables
85  integer :: i,j,n
86 
87  do j = hi%jsd,hi%jed
88  do i = hi%isd,hi%ied
89  rand(i,j) = getrandomreal( cs%stream2d(i,j) ) - 0.5
90  enddo
91  do n = 1,11
92  do i = hi%isd,hi%ied
93  rand(i,j) = rand(i,j) + ( getrandomreal( cs%stream2d(i,j) ) - 0.5 )
94  enddo
95  enddo
96  enddo
97 

◆ random_destruct()

subroutine mom_random::random_destruct ( type(prng), pointer  CS)
private

Destructor for PRNG.

Parameters
csContainer for pseudo-random number generators

Definition at line 175 of file MOM_random.F90.

176  type(PRNG), pointer :: CS !< Container for pseudo-random number generators
177 
178  if (allocated(cs%stream2d)) deallocate(cs%stream2d)
179  !deallocate(CS)

◆ random_norm()

real function, public mom_random::random_norm ( type(prng), intent(inout)  CS)

Returns an approximately normally distributed random number with mean 0 and variance 1.

Parameters
[in,out]csContainer for pseudo-random number generators

Definition at line 50 of file MOM_random.F90.

51  type(PRNG), intent(inout) :: CS !< Container for pseudo-random number generators
52  ! Local variables
53  integer :: i
54 
55  random_norm = getrandomreal(cs%stream0d) - 0.5
56  do i = 1,11
57  random_norm = random_norm + ( getrandomreal(cs%stream0d) - 0.5 )
58  enddo
59 

◆ random_unit_tests()

logical function, public mom_random::random_unit_tests ( logical  verbose)

Runs some statistical tests on the PRNG.

Parameters
verboseTrue if results should be written to stdout

Definition at line 183 of file MOM_random.F90.

184  logical :: verbose !< True if results should be written to stdout
185  ! Local variables
186  type(PRNG) :: test_rng ! Generator
187  type(time_type) :: Time ! Model time
188  real :: r1, r2, r3 ! Some random numbers and re-used work variables
189  real :: mean, var, ar1, std ! Some statistics
190  integer :: stdunit ! For messages
191  integer, parameter :: n_samples = 800
192  integer :: i, j, ni, nj
193  ! Fake being on a decomposed domain
194  type(hor_index_type), pointer :: HI => null() !< Not the real HI
195  real, dimension(:,:), allocatable :: r2d ! Random numbers
196 
197  ! Fake a decomposed domain
198  ni = 6
199  nj = 9
200  allocate(hi)
201  hi%isd = 0
202  hi%ied = ni+1
203  hi%jsd = 0
204  hi%jed = nj+1
205  hi%niglobal = ni
206  hi%njglobal = nj
207  hi%idg_offset = 0
208  hi%jdg_offset = 0
209 
210  random_unit_tests = .false.
211  stdunit = stdout
212  write(stdunit,'(1x,a)') '==== MOM_random: random_unit_tests ======================='
213 
214  if (verbose) write(stdunit,'(1x,"random: ",a)') '-- Time-based seeds ---------------------'
215  ! Check time-based seed generation
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)
228 
229  if (verbose) write(stdunit,'(1x,"random: ",a)') '-- PRNG tests ---------------------------'
230  ! Generate a random number, r1
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)
235 
236  ! Check that we get a different number, r2, on a second call
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)
240 
241  ! Check that we can reproduce r1 by resetting the seed
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)
246 
247  ! Check that we get a different number, r2, with a different seed but same date
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)
252 
253  ! Check that we get a different number, r2, for a different date but same seed
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)
259 
260  if (verbose) write(stdunit,'(1x,"random: ",a)') '-- index-based seeds --------------------'
261  ! Check index-based seed
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)
278 
279  if (.not.random_unit_tests) write(stdunit,'(1x,a)') 'Passed unit tests'
280  ! The rest of these are not unit tests but statistical tests and as such
281  ! could fail for different sample sizes but happen to pass here.
282 
283  ! Check statistics of large samples for uniform generator
284  mean = 0. ; var = 0. ; ar1 = 0. ; r2 = 0.
285  do i = 1, n_samples
286  r1 = random_01(test_rng) - 0.5
287  mean = mean + r1
288  var = var + r1**2
289  ar1 = ar1 + r1*r2
290  r2 = r1 ! Keep copy of last value
291  enddo
292  mean = mean / real(n_samples) ! Expected mean is 0
293  var = var / real(n_samples) ! Expected variance is 1/12
294  ar1 = ar1 / real(n_samples-1) ! Autocovariance
295  std = sqrt(var) ! Expected std is sqrt(1/12)
296  r2 = mean*sqrt(real(12*n_samples)) ! Normalized error in mean
297  r3 = std*sqrt(12.) ! Normalized standard deviation
298  r1 = ( ar1 * sqrt(real(n_samples-1)) ) / var
299  if (verbose) then
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
304  endif
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)
314 
315  ! Check statistics of large samples for normal generator
316  mean = 0. ; var = 0. ; ar1 = 0. ; r2 = 0.
317  do i = 1, n_samples
318  r1 = random_norm(test_rng)
319  mean = mean + r1
320  var = var + r1**2
321  ar1 = ar1 + r1*r2
322  r2 = r1 ! Keep copy of last value for AR calculation
323  enddo
324  mean = mean / real(n_samples)
325  var = var / real(n_samples)
326  ar1 = ar1 / real(n_samples)
327  std = sqrt(var)
328  r3 = 1./sqrt(real(n_samples)) ! Standard error of mean
329  r2 = mean*sqrt(real(n_samples)) ! Normalized error in mean
330  r3 = std ! Normalized standard deviation
331  r1 = ( ar1 * sqrt(real(n_samples-1)) ) / var
332  if (verbose) then
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
337  endif
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)
347 
348  if (verbose) write(stdunit,'(1x,"random: ",a)') '-- 2d PRNG ------------------------------'
349  ! Check 2d random number generator 0..1
350  allocate( r2d(hi%isd:hi%ied,hi%jsd:hi%jed) )
351  call random_2d_constructor(test_rng, hi, time, 123)
352  r2d(:,:) = -999. ! Use -9. to detect unset values
353  call random_2d_01(test_rng, hi, r2d)
354  if (any(abs(r2d(:,:)+999.)<=0.)) random_unit_tests=.true.
355  r1 = minval(r2d)
356  r2 = maxval(r2d)
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)
361  std = sqrt(var)
362  r3 = 1./sqrt(real(12*ni*nj)) ! Standard error of mean
363  r2 = mean*sqrt(real(12*ni*nj)) ! Normalized error in mean
364  r3 = std*sqrt(12.) ! Normalized standard deviation
365  if (verbose) then
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
370  endif
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.)
377  if (verbose) then
378  write(stdunit,'(1x,"random:")')
379  write(stdunit,'(1x,"random:",8f8.5)') r2d
380  write(stdunit,'(1x,"random:")')
381  endif
382 
383  ! Check 2d normal random number generator
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)
387  std = sqrt(var)
388  r3 = 1./sqrt(real(ni*nj)) ! Standard error of mean
389  r2 = mean*sqrt(real(ni*nj)) ! Normalized error in mean
390  r3 = std ! Normalized standard deviation
391  if (verbose) then
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
396  endif
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.)
403 
404  ! Clean up
405  deallocate(r2d)
406  deallocate(hi)
407 
408  if (.not.random_unit_tests) write(stdunit,'(1x,a)') 'Passed statistical tests'
409 

◆ seed_from_index()

integer function mom_random::seed_from_index ( type(hor_index_type), intent(in)  HI,
integer, intent(in)  i,
integer, intent(in)  j 
)
private

Create seed from position index.

Parameters
[in]hiHorizontal index structure
[in]ii-index (of h-cell)
[in]jj-index (of h-cell)

Definition at line 154 of file MOM_random.F90.

155  type(hor_index_type), intent(in) :: HI !< Horizontal index structure
156  integer, intent(in) :: i !< i-index (of h-cell)
157  integer, intent(in) :: j !< j-index (of h-cell)
158  ! Local variables
159  integer :: ig, jg, ni, nj, ij
160 
161  ni = hi%niglobal
162  nj = hi%njglobal
163  ! Periodicity is assumed here but does not break non-periodic models
164  ig = mod(hi%idg_offset + i - 1 + ni, ni)+1
165  jg = max(hi%jdg_offset + j, 0)
166  if (jg>nj) then ! Tri-polar hard-coded until we put needed info in HI **TODO**
167  jg = 2*nj+1-jg
168  ig = ni+1-ig
169  endif
170  seed_from_index = ig + ni*(jg-1)
171 

◆ seed_from_time()

integer function mom_random::seed_from_time ( type(time_type), intent(in)  Time)
private

Return a seed derived as hash of values in Time.

Parameters
[in]timeCurrent model time

Definition at line 138 of file MOM_random.F90.

139  type(time_type), intent(in) :: Time !< Current model time
140  ! Local variables
141  integer :: yr,mo,dy,hr,mn,sc,s1,s2
142 
143  call get_date(time,yr,mo,dy,hr,mn,sc)
144  s1 = sc + 61*(mn + 61*hr) + 379 ! Range 379 .. 89620
145  ! Fun fact: 2147483647 is the eighth Mersenne prime.
146  ! This is not the reason for using 2147483647 here. It is the
147  ! largest integer of kind=4.
148  s2 = modulo(dy + 32*(mo + 13*yr), 2147483647_4) ! Range 0 .. 2147483646
149  seed_from_time = ieor(s1*4111, s2)
150 

◆ test_fn()

logical function mom_random::test_fn ( logical, intent(in)  verbose,
logical, intent(in)  good,
character(len=*), intent(in)  label,
real, intent(in), optional  rvalue,
integer, intent(in), optional  ivalue 
)
private

Convenience function for reporting result of test.

Parameters
[in]verboseVerbosity
[in]goodTrue if pass, false otherwise
[in]labelLabel for messages
[in]rvalueResult of calculation
[in]ivalueResult of calculation

Definition at line 413 of file MOM_random.F90.

414  logical, intent(in) :: verbose !< Verbosity
415  logical, intent(in) :: good !< True if pass, false otherwise
416  character(len=*), intent(in) :: label !< Label for messages
417  real, intent(in) :: rvalue !< Result of calculation
418  integer, intent(in) :: ivalue !< Result of calculation
419  optional :: rvalue, ivalue
420 
421  if (present(ivalue)) then
422  if (.not. good) 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
427  endif
428  else
429  if (.not. good) then
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
434  endif
435  endif
436  test_fn = .not. good
437