1 |
+ |
! MTK (Martina Tuckerman and Klein) Nosie' Hoover chains |
2 |
+ |
! See Molecular Physics, 1996, Vol. 87, No. 5, 1117-1157 for details of |
3 |
+ |
! the algorithm. |
4 |
+ |
! |
5 |
+ |
! author: Charles F. Vardeman II |
6 |
+ |
! date: 6-12-02 |
7 |
+ |
! |
8 |
+ |
! Interfaces: |
9 |
+ |
! Called from dynamics module: |
10 |
+ |
! public: nose_hoover_dynamics(): controls nose-hoover chains dynamics |
11 |
+ |
! for the length of the simulation time: |
12 |
+ |
! No Arguments: |
13 |
+ |
|
14 |
+ |
! private: |
15 |
+ |
! nose_hoovera: updates nose_hoover chains and then advances particle positions and velocities |
16 |
+ |
! by velocity verlet. |
17 |
+ |
! nose_hooverb: updates velocities, second half of velocity verlet. and thermostat velocities and |
18 |
+ |
! positions. |
19 |
+ |
! init_hoover: initalize hoover structures |
20 |
+ |
! apply_NHC_par: performs details of nose-hoover chains. |
21 |
+ |
|
22 |
+ |
|
23 |
|
module dynamics_nose_hoover |
24 |
|
use definitions, ONLY : DP,NDIM |
25 |
|
use constants |
41 |
|
|
42 |
|
|
43 |
|
public :: nose_hoover_dynamics |
44 |
< |
|
44 |
> |
|
45 |
|
type (sim_status) :: nose_status |
46 |
|
|
47 |
|
|
48 |
|
contains |
49 |
|
|
50 |
|
|
51 |
< |
subroutine nose_hoover_dynamics() |
52 |
< |
use velocity, ONLY : calc_temp |
51 |
> |
subroutine nose_hoover_dynamics() |
52 |
> |
use velocity, ONLY : calc_temp |
53 |
|
|
54 |
|
|
55 |
|
|
56 |
< |
logical :: update_nlist |
57 |
< |
logical :: do_pot |
58 |
< |
logical :: not_done |
59 |
< |
logical :: nmflag |
56 |
> |
logical :: update_nlist |
57 |
> |
logical :: do_pot |
58 |
> |
logical :: not_done |
59 |
> |
logical :: nmflag |
60 |
|
|
61 |
< |
integer :: step, i |
40 |
< |
|
41 |
< |
call init_status(nose_status) |
42 |
< |
call init_dynamics_loop( nose_status ) |
61 |
> |
integer :: step, i |
62 |
|
|
63 |
< |
! Start the main simulation loop. |
63 |
> |
! initialize status (ke,pe,time, etc.) |
64 |
> |
call init_status(nose_status) |
65 |
> |
call init_dynamics_loop( nose_status ) |
66 |
|
|
67 |
< |
do_pot = .true. |
47 |
< |
not_done = .true. |
48 |
< |
nmflag = .false. |
49 |
< |
step = 0 |
67 |
> |
! Start the main simulation loop. |
68 |
|
|
69 |
< |
dynamics: do |
70 |
< |
if (.not.not_done) exit dynamics |
69 |
> |
do_pot = .true. |
70 |
> |
not_done = .true. |
71 |
> |
nmflag = .false. |
72 |
> |
step = 0 |
73 |
|
|
74 |
< |
step = step + 1 |
74 |
> |
dynamics: do |
75 |
> |
if (.not.not_done) exit dynamics |
76 |
|
|
77 |
+ |
step = step + 1 |
78 |
|
|
79 |
< |
if (checktemp.and.(mod(step,check_temp_steps).eq.0)) then |
79 |
> |
|
80 |
> |
if (checktemp.and.(mod(step,check_temp_steps).eq.0)) then |
81 |
|
call calc_temp(nose_status%temp) |
82 |
< |
if (dabs(nose_status%temp-target_temp).gt.therm_variance) then |
83 |
< |
call scale_velocities(target_temp) |
84 |
< |
end if |
85 |
< |
end if |
82 |
> |
if (dabs(nose_status%temp-target_temp).gt.therm_variance) then |
83 |
> |
call scale_velocities(target_temp) |
84 |
> |
end if |
85 |
> |
end if |
86 |
|
|
87 |
< |
call hooverb() |
87 |
> |
call nose_hoovera() |
88 |
|
|
89 |
< |
call check(update_nlist) |
89 |
> |
call check(update_nlist) |
90 |
|
|
68 |
– |
|
69 |
– |
if (do_pot) then |
70 |
– |
call calc_forces(update_nlist,nmflag,pe = nose_status%pot_e) |
71 |
– |
else |
72 |
– |
call calc_forces(update_nlist,nmflag) |
73 |
– |
endif |
91 |
|
|
92 |
< |
|
93 |
< |
call hoovera() |
94 |
< |
|
95 |
< |
call evolve_time(step,nose_status,do_pot,not_done) |
96 |
< |
|
92 |
> |
if (do_pot) then |
93 |
> |
call calc_forces(update_nlist,nmflag,pe = nose_status%pot_e) |
94 |
> |
else |
95 |
> |
call calc_forces(update_nlist,nmflag) |
96 |
> |
endif |
97 |
|
|
81 |
– |
#ifdef MPI |
82 |
– |
call mpi_barrier(mpi_comm_world,mpi_err) |
83 |
– |
#endif |
84 |
– |
end do dynamics |
98 |
|
|
99 |
< |
end subroutine nose_hoover_dynamics |
99 |
> |
call nose_hooverb() |
100 |
|
|
101 |
+ |
call evolve_time(step,nose_status,do_pot,not_done) |
102 |
|
|
89 |
– |
subroutine hoovera() |
90 |
– |
use velocity, ONLY : remove_cm_momentum,remove_angular_momentum |
103 |
|
|
104 |
+ |
#ifdef MPI |
105 |
+ |
call mpi_barrier(mpi_comm_world,mpi_err) |
106 |
+ |
#endif |
107 |
+ |
end do dynamics |
108 |
|
|
109 |
< |
! ** CONSTANT-TEMPERATURE MOLECULAR DYNAMICS USING CONSTRAINT. ** |
94 |
< |
! ** ** |
95 |
< |
! ** REFERENCES: ** |
96 |
< |
! ** ** |
97 |
< |
! ** HOOVER, LADD, AND MORAN, PHYS REV LETT 48, 1818, 1982. ** |
98 |
< |
! ** EVANS, J CHEM PHYS 78, 3297, 1983. ** |
99 |
< |
! ** BROWN AND CLARKE, MOL PHYS, 51, 1243, 1984. ** |
100 |
< |
! ** ** |
101 |
< |
! ** ROUTINES SUPPLIED: ** |
102 |
< |
! ** ** |
103 |
< |
! ** SUBROUTINE HOOVERA ( ) ** |
104 |
< |
! ** FIRST PART OF MOVE WITH VELOCITY CONSTRAINTS APPLIED. ** |
105 |
< |
! ** SUBROUTINE HOOVERB ( DT ) ** |
106 |
< |
! ** SECOND PART OF MOVE. ** |
109 |
> |
end subroutine nose_hoover_dynamics |
110 |
|
|
108 |
– |
|
111 |
|
|
112 |
< |
integer :: i, dim |
113 |
< |
real( kind = DP) :: ke, v2, instatemp, chi |
114 |
< |
real( kind = DP) :: kebar |
113 |
< |
|
114 |
< |
real( kind = DP ), dimension(ndim) :: v_dim_i |
112 |
> |
! nose_hoovera(): advances velocities from T to T + DT/2 and positions from T to T + DT |
113 |
> |
subroutine nose_hoovera() |
114 |
> |
use velocity, ONLY : remove_cm_momentum,remove_angular_momentum |
115 |
|
|
116 |
– |
real(kind=DP) :: ke_local |
116 |
|
|
118 |
– |
! units for time are femtosec (10^-15 sec) |
119 |
– |
! units for distance are angstroms (10^-10 m) |
120 |
– |
! units for velocity are angstroms femtosec^-1 |
121 |
– |
! units for mass are a.m.u. (1.661 * 10^-27 kg) |
122 |
– |
! units for force are kcal mol^-1 angstrom^-1 |
123 |
– |
! |
124 |
– |
! converter will put the final terms into angstroms. |
125 |
– |
! or angstrom/femtosecond. |
117 |
|
|
127 |
– |
! converter = 1.0d0/2.390664d3 |
118 |
|
|
119 |
+ |
integer :: i, dim |
120 |
+ |
real( kind = DP) :: ke, v2, instatemp, chi |
121 |
+ |
real( kind = DP) :: kebar |
122 |
|
|
123 |
< |
! first calculate the temperature using the unconstrained velocities: |
123 |
> |
real( kind = DP ), dimension(ndim) :: v_dim_i |
124 |
|
|
125 |
< |
ke = 0.0E0_DP |
133 |
< |
ke_local = 0.0E0_DP |
125 |
> |
real(kind=DP) :: ke_local |
126 |
|
|
127 |
< |
DO i = 1, nlocal |
128 |
< |
do dim = 1, ndim |
129 |
< |
v_dim_i(dim) = v(dim,i) + (dt2*f(dim,i)/mass(i))*KCALMOL_TO_AMUA2FS2 |
130 |
< |
ke_local = ke_local + mass(i)* & |
131 |
< |
(v_dim_i(dim)*v_dim_i(dim)) |
132 |
< |
enddo |
133 |
< |
enddo |
134 |
< |
#ifdef MPI |
143 |
< |
call mpi_allreduce(ke_local,ke,1,mpi_double_precision, & |
144 |
< |
mpi_sum,mpi_comm_world,mpi_err) |
145 |
< |
#else |
146 |
< |
ke = ke_local |
147 |
< |
#endif |
127 |
> |
! units for time are femtosec (10^-15 sec) |
128 |
> |
! units for distance are angstroms (10^-10 m) |
129 |
> |
! units for velocity are angstroms femtosec^-1 |
130 |
> |
! units for mass are a.m.u. (1.661 * 10^-27 kg) |
131 |
> |
! units for force are kcal mol^-1 angstrom^-1 |
132 |
> |
! |
133 |
> |
! converter will put the final terms into angstroms. |
134 |
> |
! or angstrom/femtosecond. |
135 |
|
|
136 |
+ |
! converter = 1.0d0/2.390664d3 |
137 |
|
|
150 |
– |
ke = ke * AMUA2FS2_TO_KCALMOL |
151 |
– |
kebar = kcal_to_kj*1000.0e0_DP*ke / real(3*(natoms - 1),DP) / NAVOGADRO |
152 |
– |
instatemp = kebar/k_boltz |
153 |
– |
|
154 |
– |
!write(*,*) 'before, insta = ', instatemp |
138 |
|
|
139 |
< |
chi = sqrt(target_temp / instatemp) |
139 |
> |
! call nose_hoover chains |
140 |
> |
call apply_NHC_par() |
141 |
|
|
142 |
< |
! ** CALCULATE THE CONSTRAINED VELOCITIES AT TIME T+DT/2 ** |
142 |
> |
! advance velocities from t to t + dt/2 |
143 |
> |
do i = 1, nlocal |
144 |
> |
v(1:ndim,i) = v(1:ndim,i) + & |
145 |
> |
(dt2*f(1:ndim,i)/mass(i))*KCALMOL_TO_AMUA2FS2 |
146 |
> |
end do |
147 |
|
|
148 |
< |
do i = 1, nlocal |
149 |
< |
do dim = 1, ndim |
150 |
< |
v(dim,i) = v(dim,i) * (2.0E0_DP * chi - 1.0E0_DP) + & |
151 |
< |
chi*dt*(f(dim,i)/mass(i))*KCALMOL_TO_AMUA2FS2 |
164 |
< |
enddo |
165 |
< |
enddo |
148 |
> |
! advance positions from t to t+dt |
149 |
> |
do i = 1, nlocal |
150 |
> |
q(1:ndim,i) = q(1:ndim,i) + dt*v(1:ndim,i) |
151 |
> |
end do |
152 |
|
|
167 |
– |
if (sim_type == "cluster") then |
168 |
– |
call remove_cm_momentum() |
169 |
– |
call remove_angular_momentum() |
170 |
– |
endif |
153 |
|
|
154 |
< |
end subroutine hoovera |
173 |
< |
|
174 |
< |
|
175 |
< |
|
176 |
< |
subroutine hooverb( ) |
154 |
> |
end subroutine nose_hoovera |
155 |
|
|
156 |
|
|
157 |
< |
! ******************************************************************* |
158 |
< |
! ** SECOND PART OF THE CONSTANT TEMPERATURE ALGORITHM ** |
159 |
< |
! ** ** |
160 |
< |
! ** THIS ADVANCES THE POSITIONS FROM T TO T + DT. ** |
183 |
< |
! ******************************************************************* |
184 |
< |
|
185 |
< |
|
186 |
< |
integer i, dim |
187 |
< |
! units for time are femtosec (10^-15 sec) |
188 |
< |
! units for distance are angstroms (10^-10 m) |
189 |
< |
! units for velocity are angstroms femtosec^-1 |
190 |
< |
! units for mass are a.m.u. (1.661 * 10^-27 kg) |
191 |
< |
! units for force are kcal mol^-1 angstrom^-1 |
157 |
> |
! advances velocities another half step from t+dt/2 to t + dt |
158 |
> |
subroutine nose_hooverb( ) |
159 |
> |
use velocity, ONLY : remove_cm_momentum,remove_angular_momentum |
160 |
> |
integer i |
161 |
|
|
162 |
< |
do i = 1, nlocal |
163 |
< |
do dim = 1, ndim |
164 |
< |
q(dim,i) = q(dim,i) + dt * v(dim,i) |
165 |
< |
enddo |
166 |
< |
enddo |
162 |
> |
! units for time are femtosec (10^-15 sec) |
163 |
> |
! units for distance are angstroms (10^-10 m) |
164 |
> |
! units for velocity are angstroms femtosec^-1 |
165 |
> |
! units for mass are a.m.u. (1.661 * 10^-27 kg) |
166 |
> |
! units for force are kcal mol^-1 angstrom^-1 |
167 |
|
|
168 |
< |
end subroutine hooverb |
168 |
> |
do i = 1, nlocal |
169 |
> |
v(1:ndim,i) = v(1:ndim,i) + & |
170 |
> |
(dt2*f(1:ndim,i)/mass(i))*KCALMOL_TO_AMUA2FS2 |
171 |
> |
end do |
172 |
|
|
173 |
+ |
! apply nose-hoover thermostat |
174 |
+ |
call apply_NHC_par() |
175 |
|
|
176 |
+ |
if (sim_type == "cluster") then |
177 |
+ |
call remove_cm_momentum() |
178 |
+ |
call remove_angular_momentum() |
179 |
+ |
endif |
180 |
+ |
end subroutine nose_hooverb |
181 |
+ |
|
182 |
+ |
|
183 |
+ |
! does the Nose-Hoover part of the integration from t = 0 to t=DT/2 |
184 |
+ |
! borrowed from Chris Fennel's code of thermostating |
185 |
+ |
! by way of Hoover, Phys.Rev.A 1985, Vol.31 (3) 1695-1697 |
186 |
+ |
|
187 |
+ |
subroutine apply_NHC_par() |
188 |
+ |
use velocity, ONLY : calc_temp |
189 |
+ |
real(kind = DP) :: system_ke |
190 |
+ |
real(kind = DP) :: system_temp |
191 |
+ |
real(kind = DP) :: G_NkT |
192 |
+ |
real(kind = DP) :: Q_mass |
193 |
+ |
real(kind = DP) :: zeta |
194 |
+ |
real(kind = DP) :: zetaScale |
195 |
+ |
real(kind = DP),dimension(ndim) :: vi |
196 |
+ |
|
197 |
+ |
integer :: i |
198 |
+ |
|
199 |
+ |
call calc_temp(system_temp,system_ke) |
200 |
+ |
|
201 |
+ |
G_NkT = natoms*ndim*K_BOLTZ*target_temp |
202 |
+ |
! and aren't the system_temp and system_ke similar to chris's kt and ke? |
203 |
+ |
|
204 |
+ |
! Q_mass is a function of the omega parameter |
205 |
+ |
Q_mass = G_NkT / (omega * omega) |
206 |
+ |
|
207 |
+ |
! advance the zeta term to zeta(t + dt) |
208 |
+ |
zeta = zeta + dt*((system_temp*2 - G_NkT)/Q_mass) |
209 |
+ |
zetaScale = zeta * dt |
210 |
+ |
|
211 |
+ |
! perform thermostating on velocities and angular momenta |
212 |
+ |
do i = 1, nlocal |
213 |
+ |
vi(1:ndim) = v(1:ndim,i)*zetaScale |
214 |
+ |
! similar thing here for our angular momenta |
215 |
+ |
|
216 |
+ |
v(1:ndim,i) = v(1:ndim,i) - vi(1:ndim) |
217 |
+ |
! similar thing here for our angular momenta |
218 |
+ |
enddo |
219 |
+ |
|
220 |
+ |
end subroutine apply_NHC_par |
221 |
+ |
|
222 |
+ |
|
223 |
+ |
|
224 |
+ |
!!$ ! other code borrowed from Chris Fennell's code of |
225 |
+ |
!!$ ! G.J. Martyna et al. Mol. Phys., 1996, Vol. 87, No. 5, 1117-1157 |
226 |
+ |
!!$ |
227 |
+ |
!!$ subroutine apply_NHC_par() |
228 |
+ |
!!$ use velocity, ONLY : calc_temp |
229 |
+ |
!!$ real(kind = DP) :: scale |
230 |
+ |
!!$ real(kind = DP) :: system_ke |
231 |
+ |
!!$ real(kind = DP) :: system_temp |
232 |
+ |
!!$ real(kind = DP) :: G_NkT |
233 |
+ |
!!$ real(kind = DP) :: omega2 |
234 |
+ |
!!$ real(kind = DP) :: value1, value2, value3, value4 |
235 |
+ |
!!$ real(kind = DP) :: wdti2(n_ysval), wdti4(n_ysval), wdti8(n_ysval) |
236 |
+ |
!!$ real(kind = DP) :: G_val(N_nos), vval(N_nos), xival(N_nos) |
237 |
+ |
!!$ real(kind = DP) :: Q_mass |
238 |
+ |
!!$ |
239 |
+ |
!!$ integer :: i, j |
240 |
+ |
!!$ |
241 |
+ |
!!$ scale = 1._DP |
242 |
+ |
!!$ |
243 |
+ |
!!$ call calc_temp(system_temp,system_ke) |
244 |
+ |
!!$ |
245 |
+ |
!!$ G_NkT = natoms*ndim*K_BOLTZ*my_temp |
246 |
+ |
!!$ |
247 |
+ |
!!$ ! we need to get the w numbers for these values |
248 |
+ |
!!$ ! but i think that chris has calculated the values |
249 |
+ |
!!$ ! because they are values of constants |
250 |
+ |
!!$ value1 = 0.67560359598d0 * dt |
251 |
+ |
!!$ value2 = -0.85120719196d0 * dt |
252 |
+ |
!!$ value3 = 0.207245385897d0 * dt |
253 |
+ |
!!$ value4 = -0.328981543589d0 * dt |
254 |
+ |
!!$ if (n_ysval.eq.3) then |
255 |
+ |
!!$ wdti2(1) = value1 |
256 |
+ |
!!$ wdti2(2) = value2 |
257 |
+ |
!!$ wdti2(3) = value1 |
258 |
+ |
!!$ wdti4(1) = 0.5_DP * wdti2(1) |
259 |
+ |
!!$ wdti4(2) = 0.5_DP * wdti2(2) |
260 |
+ |
!!$ wdti4(3) = wdti4(1) |
261 |
+ |
!!$ wdti8(1) = 0.5_DP * wdti4(1) |
262 |
+ |
!!$ wdti8(2) = 0.5_DP * wdti4(2) |
263 |
+ |
!!$ wdti8(3) = wdti8(1) |
264 |
+ |
!!$ else |
265 |
+ |
!!$ write(*,*) 'Error: n_syval must be 3' |
266 |
+ |
!!$ stop |
267 |
+ |
!!$ endif |
268 |
+ |
!!$ |
269 |
+ |
!!$ omega2 = omega * omega |
270 |
+ |
!!$ |
271 |
+ |
!!$ ! set the Q_mass values |
272 |
+ |
!!$ Q_mass(1) = G_NkT/omega2 |
273 |
+ |
!!$ do i = 2, N_nos-1 |
274 |
+ |
!!$ Q_mass(i) = system_temp/omega2 |
275 |
+ |
!!$ enddo |
276 |
+ |
!!$ |
277 |
+ |
!!$ qmass(N_nos) = 2 * omega |
278 |
+ |
!!$ |
279 |
+ |
!!$ ! zero the intial xivals and vvals |
280 |
+ |
!!$ do i = 1, N_nos |
281 |
+ |
!!$ xival(i) = 0._DP |
282 |
+ |
!!$ vval(i) = 0._DP |
283 |
+ |
!!$ enddo |
284 |
+ |
!!$ |
285 |
+ |
!!$ ! Start the Nose_Hoover chain stepping |
286 |
+ |
!!$ ! update the forces |
287 |
+ |
!!$ G_val(1) = (system_ke - G_NkT)/Q_mass(1) |
288 |
+ |
!!$ |
289 |
+ |
!!$ ! start the multiple time stepping |
290 |
+ |
!!$ do i = 1, n_cval |
291 |
+ |
!!$ do j = 1, n_ysval |
292 |
+ |
!!$ |
293 |
+ |
!!$ ! update the thermostat velocities |
294 |
+ |
!!$ vval(N_nos) = vval(N_nos) + G_val(N_nos)*wdti4(j)/i |
295 |
+ |
!!$ do mcount = 1, N_nos-1 |
296 |
+ |
!!$ aa = exp((-wdti8(j)/i) * vval((N_nos+1)-mcount)) |
297 |
+ |
!!$ vval(N_nos-mcount) = vval(N_nos-mcount)*aa*aa & |
298 |
+ |
!!$ + wdti4(j)*G_val(N_nos)*wdti4(j)/(i*i) |
299 |
+ |
!!$ enddo |
300 |
+ |
!!$ |
301 |
+ |
!!$ ! update particle velocities |
302 |
+ |
!!$ aa = exp((-wdti2(j)/i)*vval(1)) |
303 |
+ |
!!$ scale = scale*aa |
304 |
+ |
!!$ |
305 |
+ |
!!$ ! update the forces |
306 |
+ |
!!$ G_val(1) = (scale*scale*ke -G_NkT)/Q_mass(1) |
307 |
+ |
!!$ |
308 |
+ |
!!$ ! update the thermostat positions |
309 |
+ |
!!$ do mcount = 1, N_nos-1 |
310 |
+ |
!!$ xival(mcount) = xival(mcount) + vval(mcount)*(wdti2(j)/i) |
311 |
+ |
!!$ enddo |
312 |
+ |
!!$ |
313 |
+ |
!!$ ! update the thermostat velocities |
314 |
+ |
!!$ do mcount = 1, N_nos-1 |
315 |
+ |
!!$ aa = exp((-wdti8(j)/i)*vval(mcount+1)) |
316 |
+ |
!!$ vval(mcount) = vval(mcount)*aa*aa & |
317 |
+ |
!!$ + (wdti4(j)/i)*G_val(mcount)*aa |
318 |
+ |
!!$ G_val(mcount+1) = (Q_mass(mcount)*vval(mcount)*vval(mcount) & |
319 |
+ |
!!$ - system_temp)/Q_mass(mcount+1) |
320 |
+ |
!!$ enddo |
321 |
+ |
!!$ |
322 |
+ |
!!$ vval(N_nos) = vval(N_nos) + G_val(N_nos)*(wdti4(j)/i) |
323 |
+ |
!!$ enddo |
324 |
+ |
!!$ enddo |
325 |
+ |
!!$ |
326 |
+ |
!!$ ! scale both linear velocities and angular momenta |
327 |
+ |
!!$ do i = 1, nlocal |
328 |
+ |
!!$ v(1:ndim,i) = v(1:ndim,i)*scale |
329 |
+ |
!!$ !something here for what we use for angular momenta |
330 |
+ |
!!$ enddo |
331 |
+ |
!!$ |
332 |
+ |
!!$ end subroutine apply_NHC_par |
333 |
+ |
|
334 |
+ |
|
335 |
+ |
|
336 |
|
end module dynamics_nose_hoover |