EMPIRE DA  v1.9.1
Data assimilation codes using EMPIRE communication
 All Classes Files Functions Variables Pages
ziggurat.f90
Go to the documentation of this file.
1 ! Marsaglia & Tsang generator for random normals & random exponentials.
2 ! Translated from C by Alan Miller (amiller@bigpond.net.au)
3 
4 ! Marsaglia, G. & Tsang, W.W. (2000) `The ziggurat method for generating
5 ! random variables', J. Statist. Software, v5(8).
6 
7 ! This is an electronic journal which can be downloaded from:
8 ! http://www.jstatsoft.org/v05/i08
9 
10 ! N.B. It is assumed that all integers are 32-bit.
11 ! N.B. The value of M2 has been halved to compensate for the lack of
12 ! unsigned integers in Fortran.
13 
14 ! Latest version - 1 January 2001
15 !
16 ! Modified by D. M. scott in 2016 to assist vectorisation by the compiler.
17 MODULE ziggurat
18  IMPLICIT NONE
19 
20  PRIVATE
21 
22 ! Define the number of integers in a vector, N_INT, and also the number of
23 ! pseudo random integers to be generated by each call to shr3_vec, N_GEN.
24 ! The size of the array used to hold the pseudo random numbers must be an
25 ! integral multiple of N_INT. N_GEN must be no greater than the size of this array.
26 ! Also, N_GEN must be a power of two.
27 !
28 ! This code is suitable for processors implementing AVX (e.g. Sandy Bridge) for
29 ! which the vector length is 4 and arrays used in loops should be 32 byte aligned.
30 ! See https://en.wikipedia.org/wiki/Advanced_Vector_Extensions#CPUs_with_AVX
31 ! for details of which processors implement which instruction sets.
32  INTEGER, PARAMETER :: n_int = 4
33  INTEGER, PARAMETER :: multiplier = 1
34  INTEGER, PARAMETER :: array_size = multiplier*n_int
35  INTEGER, PARAMETER :: array_size_m1 = array_size - 1
36  INTEGER, PARAMETER :: n_gen = array_size ! Must <= ARRAY_SIZE
37  INTEGER, PARAMETER :: n_gen_m1 = n_gen - 1
38 
39  INTEGER, PARAMETER :: dp=selected_real_kind( 12, 60 )
40  REAL(DP), PARAMETER :: m1=2147483648.0_dp, m2=2147483648.0_dp, &
41  half=0.5_dp
42  REAL(DP) :: dn=3.442619855899_dp, tn=3.442619855899_dp, &
43  vn=0.00991256303526217_dp, &
44  q, de=7.697117470131487_dp, &
45  te=7.697117470131487_dp, &
46  ve=0.003949659822581572_dp
47  INTEGER, SAVE :: iz, jz, kn(0:127), &
48  ke(0:255), hz
49  REAL(DP), SAVE :: wn(0:127), fn(0:127), we(0:255), fe(0:255)
50  LOGICAL, SAVE :: initialized=.false.
51 
52  INTEGER, DIMENSION(0:ARRAY_SIZE_M1), SAVE :: jz_vec, jsr_vec
53  INTEGER, DIMENSION(0:ARRAY_SIZE_M1), SAVE :: rand_ints
54  !DIR$ ATTRIBUTES ALIGN : 32 :: jz_vec
55  !DIR$ ATTRIBUTES ALIGN : 32 :: jsr_vec
56  INTEGER, SAVE :: index_shr3 = 0
57 
58  PUBLIC :: zigset, shr3, uni, rnor, rexp
59 
60 
61 CONTAINS
62 
63 
64 SUBROUTINE zigset( jsrseed )
65 
66  INTEGER, INTENT(IN) :: jsrseed
67 
68  INTEGER :: i
69 
70  ! Set the seeds
71  !DIR$ VECTOR ALIGNED
72  DO i = 0, n_gen_m1
73  jsr_vec(i) = 1 + i + n_gen*jsrseed
74  END DO
75 
76  ! Tables for RNOR
77  q = vn*exp(half*dn*dn)
78  kn(0) = (dn/q)*m1
79  kn(1) = 0
80  wn(0) = q/m1
81  wn(127) = dn/m1
82  fn(0) = 1.0_dp
83  fn(127) = exp( -half*dn*dn )
84  DO i = 126, 1, -1
85  dn = sqrt( -2.0_dp * log( vn/dn + exp( -half*dn*dn ) ) )
86  kn(i+1) = (dn/tn)*m1
87  tn = dn
88  fn(i) = exp(-half*dn*dn)
89  wn(i) = dn/m1
90  END DO
91 
92  ! Tables for REXP
93  q = ve*exp( de )
94  ke(0) = (de/q)*m2
95  ke(1) = 0
96  we(0) = q/m2
97  we(255) = de/m2
98  fe(0) = 1.0_dp
99  fe(255) = exp( -de )
100  DO i = 254, 1, -1
101  de = -log( ve/de + exp( -de ) )
102  ke(i+1) = m2 * (de/te)
103  te = de
104  fe(i) = exp( -de )
105  we(i) = de/m2
106  END DO
107  initialized = .true.
108  RETURN
109 END SUBROUTINE zigset
110 
111 
112 
113 ! Generate N_GEN pseudo random 32-bit integers.
114 FUNCTION shr3_vec( ) RESULT( ival_vec )
115  INTEGER, DIMENSION(0:ARRAY_SIZE_M1) :: ival_vec
116  INTEGER :: i
117 
118  IF( .NOT. initialized ) stop 'ERROR: shr3_vec has not been initialised (call zigset).'
119  !DIR$ VECTOR ALIGNED
120  DO i = 0, n_gen_m1
121  jz_vec(i) = jsr_vec(i)
122  jsr_vec(i) = ieor( jsr_vec(i), ishft( jsr_vec(i), 13 ) )
123  jsr_vec(i) = ieor( jsr_vec(i), ishft( jsr_vec(i), -17 ) )
124  jsr_vec(i) = ieor( jsr_vec(i), ishft( jsr_vec(i), 5 ) )
125  ival_vec(i) = jz_vec(i) + jsr_vec(i)
126  END DO
127  RETURN
128 END FUNCTION shr3_vec
129 
130 
131 
132 ! Generate pseudo random 32-bit integers.
133 FUNCTION shr3( ) RESULT( ival )
134  INTEGER :: ival
135 
136  IF (index_shr3 == 0) THEN
137  rand_ints = shr3_vec()
138  END IF
139 
140  ival = rand_ints( index_shr3 )
141 
142  index_shr3 = index_shr3 + 1
143  index_shr3 = iand( index_shr3, n_gen_m1 ) ! Calculate modulus.
144 
145  RETURN
146 END FUNCTION shr3
147 
148 
149 
150 ! Generate uniformly distributed pseudo random numbers.
151 FUNCTION uni( ) RESULT( fn_val )
152  REAL(DP) :: fn_val
153 
154  fn_val = half + 0.2328306e-9_dp * shr3( )
155  RETURN
156 END FUNCTION uni
157 
158 
159 
160 ! Generate pseudo random numbers with a normal distribution.
161 FUNCTION rnor( ) RESULT( fn_val )
162  REAL(DP) :: fn_val
163 
164  REAL(DP), PARAMETER :: r = 3.442620_DP
165  REAL(DP) :: x, y
166 
167  hz = shr3( )
168  iz = iand( hz, 127 )
169  IF( abs( hz ) < kn(iz) ) THEN
170  fn_val = hz * wn(iz)
171  ELSE
172  DO
173  IF( iz == 0 ) THEN
174  DO
175  x = -0.2904764_dp* log( uni( ) )
176  y = -log( uni( ) )
177  IF( y+y >= x*x ) EXIT
178  END DO
179  fn_val = r+x
180  IF( hz <= 0 ) fn_val = -fn_val
181  RETURN
182  END IF
183  x = hz * wn(iz)
184  IF( fn(iz) + uni( )*(fn(iz-1)-fn(iz)) < exp(-half*x*x) ) THEN
185  fn_val = x
186  RETURN
187  END IF
188  hz = shr3( )
189  iz = iand( hz, 127 )
190  IF( abs( hz ) < kn(iz) ) THEN
191  fn_val = hz * wn(iz)
192  RETURN
193  END IF
194  END DO
195  END IF
196  RETURN
197 END FUNCTION rnor
198 
199 
200 
201 ! Generate pseudo random numbers with an exponential distribution.
202 FUNCTION rexp( ) RESULT( fn_val )
203  REAL(DP) :: fn_val
204 
205  REAL(DP) :: x
206 
207  jz = shr3( )
208  iz = iand( jz, 255 )
209  IF( abs( jz ) < ke(iz) ) THEN
210  fn_val = abs(jz) * we(iz)
211  RETURN
212  END IF
213  DO
214  IF( iz == 0 ) THEN
215  fn_val = 7.69711 - log( uni( ) )
216  RETURN
217  END IF
218  x = abs( jz ) * we(iz)
219  IF( fe(iz) + uni( )*(fe(iz-1) - fe(iz)) < exp( -x ) ) THEN
220  fn_val = x
221  RETURN
222  END IF
223  jz = shr3( )
224  iz = iand( jz, 255 )
225  IF( abs( jz ) < ke(iz) ) THEN
226  fn_val = abs( jz ) * we(iz)
227  RETURN
228  END IF
229  END DO
230  RETURN
231 END FUNCTION rexp
232 
233 END MODULE ziggurat
integer function, public shr3()
Definition: ziggurat.f90:133
real(dp) function, public uni()
Definition: ziggurat.f90:151
real(dp) function, public rexp()
Definition: ziggurat.f90:202
subroutine, public zigset(jsrseed)
Definition: ziggurat.f90:64
real(dp) function, public rnor()
Definition: ziggurat.f90:161
subroutine q(nrhs, x, Qx)
subroutine to take a full state vector x and return Qx in state space.