Branch data Line data Source code
1 : : !!****m* ABINIT/m_random_zbq
2 : : !! NAME
3 : : !! m_random_zbq
4 : : !!
5 : : !! FUNCTION
6 : : !! This library contains routines for generating uniform random numbers
7 : : !! between 0 & 1, using a Marsaglia-Zaman type subtract-with-borrow generator.
8 : : !! This random number generator is non deterministic (it is not "pseudo-random").
9 : : !!
10 : : !! These routines have been downloaded from Richard Chandler homepage,
11 : : !! then converted into Fortran90 standard.
12 : : !! Authors:
13 : : !! Richard Chandler (email: richard@stats.ucl.ac.uk)
14 : : !! Paul Northrop (email: paul@stats.ucl.ac.uk)
15 : : !!
16 : : !! COPYRIGHT
17 : : !! Copyright (C) Richard Chandler/Paul Northrop
18 : : !! Please feel free to use and adapt these routines for your own use. We
19 : : !! do, however, request that if the routines are used in work which is later
20 : : !! presented in public, or published, the source of the code (ie. us!) is
21 : : !! acknowledged. If for no other reason, this will allow other people to test
22 : : !! the routines to see if there's anything horribly wrong with them that will
23 : : !! invalidate your results.
24 : : !!
25 : : !! See http://www.ucl.ac.uk/~ucakarc/work/software/randgen.txt
26 : : !! for further information
27 : : !!
28 : : !! SOURCE
29 : :
30 : : #if defined HAVE_CONFIG_H
31 : : #include "config.h"
32 : : #endif
33 : :
34 : : #include "abi_common.h"
35 : :
36 : :
37 : : module m_random_zbq
38 : :
39 : : use m_abicore
40 : :
41 : : use defs_basis, only : sp, dp, std_out
42 : :
43 : : implicit none
44 : :
45 : : private
46 : : !!***
47 : :
48 : : public :: ZBQLU01
49 : : public :: ZBQLINI
50 : :
51 : : REAL(DP),SAVE,PRIVATE :: B=4.294967291D9,C=0.0D0
52 : : REAL(DP),SAVE,PRIVATE :: ZBQLIX(43)= &
53 : : & (/8.001441D7,5.5321801D8,&
54 : : & 1.69570999D8,2.88589940D8,2.91581871D8,1.03842493D8,&
55 : : & 7.9952507D7,3.81202335D8,3.11575334D8,4.02878631D8, &
56 : : & 2.49757109D8,1.15192595D8,2.10629619D8,3.99952890D8,&
57 : : & 4.12280521D8,1.33873288D8,7.1345525D7,2.23467704D8, &
58 : : & 2.82934796D8,9.9756750D7,1.68564303D8,2.86817366D8, &
59 : : & 1.14310713D8,3.47045253D8,9.3762426D7 ,1.09670477D8,&
60 : : & 3.20029657D8,3.26369301D8,9.441177D6,3.53244738D8, &
61 : : & 2.44771580D8,1.59804337D8,2.07319904D8,3.37342907D8,&
62 : : & 3.75423178D8,7.0893571D7 ,4.26059785D8,3.95854390D8,&
63 : : & 2.0081010D7,5.9250059D7,1.62176640D8,3.20429173D8, &
64 : : & 2.63576576D8/)
65 : :
66 : :
67 : : contains
68 : :
69 : : ! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
70 : :
71 : : !*******************************************************************
72 : 0 : REAL(DP) FUNCTION ZBQLU01(DUMMY)
73 : : !*******************************************************************
74 : : ! Returns a uniform random number between 0 & 1, using
75 : : ! a Marsaglia-Zaman type subtract-with-borrow generator.
76 : : ! Uses double precision, rather than integer, arithmetic
77 : : ! throughout because MZ's integer constants overflow
78 : : ! 32-bit integer storage (which goes from -2^31 to 2^31).
79 : : ! Ideally, we would explicitly truncate all integer
80 : : ! quantities at each stage to ensure that the double
81 : : ! precision representations do not accumulate approximation
82 : : ! error; however, on some machines the use of DNINT to
83 : : ! accomplish this is *seriously* slow (run-time increased
84 : : ! by a factor of about 3). This double precision version
85 : : ! has been tested against an integer implementation that
86 : : ! uses long integers (non-standard and, again, slow) -
87 : : ! the output was identical up to the 16th decimal place
88 : : ! after 10^10 calls, so we're probably OK ...
89 : : !
90 : : REAL(DP) :: DUMMY
91 : :
92 : : INTEGER,SAVE :: CURPOS=1,ID22=22,ID43=43
93 : : REAL(DP) :: X,B2,BINV
94 : :
95 : 0 : B2 = B
96 : 0 : BINV = 1.0D0/B
97 : 0 : 5 X = ZBQLIX(ID22) - ZBQLIX(ID43) - C
98 [ # # ]: 0 : IF (X<0.0D0) THEN
99 : 0 : X = X + B
100 : 0 : C = 1.0D0
101 : : ELSE
102 : 0 : C = 0.0D0
103 : : ENDIF
104 : 0 : ZBQLIX(ID43) = X
105 : : !
106 : : ! Update array pointers. Do explicit check for bounds of each to
107 : : ! avoid expense of modular arithmetic. If one of them is 0 the others
108 : : ! won't be
109 : : !
110 : 0 : CURPOS = CURPOS - 1
111 : 0 : ID22 = ID22 - 1
112 : 0 : ID43 = ID43 - 1
113 [ # # ]: 0 : IF (CURPOS==0) THEN
114 : 0 : CURPOS=43
115 [ # # ]: 0 : ELSEIF (ID22==0) THEN
116 : 0 : ID22 = 43
117 [ # # ]: 0 : ELSEIF (ID43==0) THEN
118 : 0 : ID43 = 43
119 : : ENDIF
120 : : !
121 : : ! The integer arithmetic there can yield X=0, which can cause
122 : : ! problems in subsequent routines (e.g. ZBQLEXP). The problem
123 : : ! is simply that X is discrete whereas U is supposed to
124 : : ! be continuous - hence if X is 0, go back and generate another
125 : : ! X and return X/B^2 (etc.), which will be uniform on (0,1/B).
126 [ # # ]: 0 : IF (X<BINV) THEN
127 : 0 : B2 = B2*B
128 : 0 : GOTO 5
129 : : ENDIF
130 : :
131 : 0 : ZBQLU01 = X/B2
132 : :
133 : : RETURN;X=DUMMY
134 : :
135 : : END FUNCTION ZBQLU01
136 : :
137 : : !*********************************************************************
138 : 0 : SUBROUTINE ZBQLINI(SEED)
139 : : !*********************************************************************
140 : : ! To initialize the random number generator - either
141 : : ! repeatably or nonrepeatably. Need double precision
142 : : ! variables because integer storage can't handle the
143 : : ! numbers involved
144 : : !*********************************************************************
145 : : ! ARGUMENTS
146 : : ! =========
147 : : ! SEED (integer, input). User-input number which generates
148 : : ! elements of the array ZBQLIX, which is subsequently used
149 : : ! in the random number generation algorithm. If SEED=0,
150 : : ! the array is seeded using the system clock if the
151 : : ! FORTRAN implementation allows it.
152 : : !*********************************************************************
153 : : INTEGER :: SEED
154 : : !*********************************************************************
155 : : ! PARAMETERS
156 : : ! ==========
157 : : ! LFLNO (integer). Number of lowest file handle to try when
158 : : ! opening a temporary file to copy the system clock into.
159 : : ! Default is 80 to keep out of the way of any existing
160 : : ! open files (although the program keeps searching till
161 : : ! it finds an available handle). If this causes problems,
162 : : ! (which will only happen if handles 80 through 99 are
163 : : ! already in use), decrease the default value.
164 : : !*********************************************************************
165 : : ! INTEGER,PARAMETER :: LFLNO=80
166 : : !*********************************************************************
167 : : ! VARIABLES
168 : : ! =========
169 : : ! SEED See above
170 : : ! ZBQLIX Seed array for the random number generator. Defined
171 : : ! in ZBQLBD01
172 : : ! B,C Used in congruential initialisation of ZBQLIX
173 : : ! SS,MM,} System clock secs, mins, hours and days
174 : : ! HH,DD }
175 : : ! FILNO File handle used for temporary file
176 : : ! INIT Indicates whether generator has already been initialised
177 : :
178 : : INTEGER,SAVE :: INIT
179 : : INTEGER :: SS,MM,HH,DD,I
180 : : REAL(DP) :: TMPVAR1,DSS,DMM,DHH,DDD
181 : : ! Variable used by date_and_time function:
182 : : CHARACTER(LEN=8) :: DATE
183 : : CHARACTER(LEN=10) :: TIME
184 : : CHARACTER(LEN=5) :: ZONE
185 : : INTEGER :: VALUES(8)
186 : :
187 : : ! Ensure we don't call this more than once in a program
188 [ # # ]: 0 : IF (INIT>=1) THEN
189 [ # # ]: 0 : IF(INIT==1) THEN
190 : 0 : WRITE(std_out,1)
191 : : 1 FORMAT(//5X,'****WARNING**** You have called routine ZBQLINI ',&
192 : : & 'more than',/5X,'once. I''m ignoring any subsequent calls.',//)
193 : 0 : INIT = 2
194 : : END IF
195 : 0 : RETURN
196 : : ELSE
197 : 0 : INIT = 1
198 : : ENDIF
199 : :
200 : : ! If SEED = 0, cat the contents of the clock into a file
201 : : ! and transform to obtain ZQBLIX(1), then use a congr.
202 : : ! algorithm to set remaining elements. Otherwise take
203 : : ! specified value of SEED.
204 : : !
205 [ # # ]: 0 : IF (SEED==0) THEN
206 : : ! Initial coding:
207 : : !>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
208 : : !>>>>>>> COMMENT OUT FROM HERE IF YOU DON'T HAVE >>>>>>>>>>>>
209 : : !>>>>>>> 'CALL SYSTEM' CAPABILITY ... >>>>>>>>>>>>
210 : : !>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
211 : : ! CALL SYSTEM(' date +%S%M%H%j > zbql1234.tmp')
212 : : ! ! Try all file numbers for LFLNO to 999
213 : : ! FILNO = LFLNO
214 : : ! 10 OPEN(FILNO,FILE='zbql1234.tmp',ERR=11)
215 : : ! GOTO 12
216 : : ! 11 FILNO = FILNO + 1
217 : : ! IF (FILNO.GT.999) THEN
218 : : ! WRITE(std_out,*) 'ZBQLINI: Error on temporary file !'
219 : : ! RETURN
220 : : ! ENDIF
221 : : ! GOTO 10
222 : : ! 12 READ(FILNO,'(3(I2),I3)') SS,MM,HH,DD
223 : : ! CLOSE(FILNO)
224 : : ! CALL SYSTEM('rm zbql1234.tmp')
225 : :
226 : : ! New F90 coding:
227 : 0 : CALL DATE_AND_TIME(DATE,TIME,ZONE,VALUES)
228 : 0 : HH=VALUES(4)
229 : 0 : MM=VALUES(5)
230 : 0 : SS=VALUES(6)
231 : 0 : DD=(VALUES(2)-1)*30+VALUES(3) ! Approx. for the day index
232 : :
233 : 0 : DSS = DINT((DBLE(SS)/6.0D1) * B)
234 : 0 : DMM = DINT((DBLE(MM)/6.0D1) * B)
235 : 0 : DHH = DINT((DBLE(HH)/2.4D1) * B)
236 : 0 : DDD = DINT((DBLE(DD)/3.65D2) * B)
237 : 0 : TMPVAR1 = DMOD(DSS+DMM+DHH+DDD,B)
238 : : !<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
239 : : !<<<<<<<< ... TO HERE (END OF COMMENTING OUT FOR <<<<<<<<<<<
240 : : !<<<<<<<< USERS WITHOUT 'CALL SYSTEM' CAPABILITY <<<<<<<<<<<
241 : : !<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
242 : : ELSE
243 : 0 : TMPVAR1 = DMOD(DBLE(SEED),B)
244 : : ENDIF
245 : :
246 : 0 : ZBQLIX(1) = TMPVAR1
247 [ # # ]: 0 : DO I = 2,43
248 : 0 : TMPVAR1 = ZBQLIX(I-1)*3.0269D4
249 : 0 : TMPVAR1 = DMOD(TMPVAR1,B)
250 : 0 : ZBQLIX(I) = TMPVAR1
251 : : END DO
252 : :
253 : : END SUBROUTINE ZBQLINI
254 : :
255 : : end module m_random_zbq
256 : : !!***
|