ddx 1.0.0
Libary for domain-decomposition methods for polarizable continuum models
ddx_input.f90
1
2
4use ddx_errors, only: ddx_error_type, update_error
5use ddx, only: ddinit, ddx_type, closest_supported_lebedev_grid
8implicit none
9
10contains
11
37subroutine ddfromfile(fname, ddx_data, tol, charges, ddx_error)
38 implicit none
39 character(len=*), intent(in) :: fname
40 type(ddx_type), intent(out) :: ddx_data
41 real(dp), intent(out) :: tol
42 real(dp), allocatable, intent(out) :: charges(:)
43 type(ddx_error_type), intent(inout) :: ddx_error
44 ! Local variables
45 character(len=512) :: line, key, val, msg, probe, error_text
46 character(len=255) :: output_filename
47 integer :: unit, io_info, lineno, index_comment, index_splitting, &
48 & k, nsph, natoms
49 integer :: nproc, model, lmax, ngrid, force, fmm, pm, pl, &
50 & matvecmem, maxiter, jacobi_ndiis, switching
51 real(dp) :: eps, se, eta, kappa
52 real(dp), allocatable :: csph(:, :), radii(:)
53 logical :: in_atoms, have_model, have_eps, have_se
54
55 open(newunit=unit, file=fname, status="old", action="read", &
56 & form="formatted", iostat=io_info)
57 if (io_info .ne. 0) then
58 call update_error(ddx_error, "Cannot open input file " // &
59 & trim(fname))
60 return
61 end if
62 do
63 read(unit, "(a)", iostat=io_info) line
64 index_comment = scan(line, "#!")
65 if (index_comment .gt. 0) line(index_comment:) = " "
66 if (io_info .ne. 0) exit
67 line = adjustl(line)
68 if (len_trim(line) .eq. 0 .or. line(1:1) .eq. "#") cycle
69 exit
70 end do
71 index_splitting = 0
72 if (io_info .eq. 0) then
73 ! `key=val`, `key = val` and `key val` all count as two tokens
74 probe = line
75 do k = 1, len(probe)
76 if (probe(k:k) .eq. achar(9) .or. probe(k:k) .eq. "=") &
77 & probe(k:k) = " "
78 end do
79 probe = adjustl(probe)
80 index_splitting = index(trim(probe), " ")
81 end if
82 if (index_splitting .eq. 0) then
83 ! single token (or empty file): legacy format
84 close(unit)
85 call ddfromfile_legacy(fname, ddx_data, tol, charges, ddx_error)
86 return
87 end if
88 rewind(unit)
89
90 output_filename = ""
91 nproc = default_nproc
92 lmax = default_lmax
93 ngrid = default_ngrid
94 eta = default_eta
95 kappa = default_kappa
96 matvecmem = default_incore
97 tol = default_tol
98 maxiter = default_maxiter
99 jacobi_ndiis = default_jacobi_ndiis
100 force = default_force
101 fmm = default_enable_fmm
102 pm = default_pm
103 pl = default_pl
104 switching = default_switching
105 model = 0
106 eps = -one
107 nsph = 0
108 natoms = 0
109 have_model = .false.
110 have_eps = .false.
111 have_se = .false.
112 in_atoms = .false.
113
114 lineno = 0
115 do
116 read(unit, "(a)", iostat=io_info) line
117 if (io_info .ne. 0) exit
118 lineno = lineno + 1
119 index_comment = scan(line, "#!")
120 if (index_comment .gt. 0) line(index_comment:) = " "
121 do k = 1, len(line)
122 if (line(k:k) .eq. achar(9)) line(k:k) = " "
123 end do
124 line = adjustl(line)
125 if (len_trim(line) .eq. 0) cycle
126
127 error_text = ""
128 io_info = 0
129 if (in_atoms) then
130 natoms = natoms + 1
131 read(line, *, iostat=io_info) charges(natoms), &
132 & csph(1, natoms), csph(2, natoms), csph(3, natoms), &
133 & radii(natoms)
134 if (io_info .ne. 0) &
135 & error_text = "expected `charge x y z radius`"
136 else
137 index_splitting = scan(line, " =")
138 if (index_splitting .eq. 0) then
139 key = line
140 val = ""
141 else
142 key = line(1:index_splitting-1)
143 val = adjustl(line(index_splitting+1:))
144 if (val(1:1) .eq. "=") val = adjustl(val(2:))
145 end if
146 do k = 1, len_trim(key)
147 if (key(k:k) .ge. "A" .and. key(k:k) .le. "Z") &
148 & key(k:k) = achar(iachar(key(k:k)) + 32)
149 end do
150
151 select case (key)
152 case ("output", "logfile")
153 output_filename = trim(val)
154 case ("nproc")
155 read(val, *, iostat=io_info) nproc
156 case ("model")
157 do k = 1, len_trim(val)
158 if (val(k:k) .ge. "A" .and. val(k:k) .le. "Z") &
159 & val(k:k) = achar(iachar(val(k:k)) + 32)
160 end do
161 select case (trim(val))
162 case ("cosmo")
163 model = 1
164 case ("pcm")
165 model = 2
166 case ("lpb")
167 model = 3
168 case default
169 read(val, *, iostat=io_info) model
170 end select
171 have_model = .true.
172 case ("lmax")
173 read(val, *, iostat=io_info) lmax
174 case ("ngrid")
175 read(val, *, iostat=io_info) ngrid
176 case ("eps")
177 read(val, *, iostat=io_info) eps
178 have_eps = .true.
179 case ("se", "shift")
180 read(val, *, iostat=io_info) se
181 have_se = .true.
182 case ("eta")
183 read(val, *, iostat=io_info) eta
184 case ("kappa")
185 read(val, *, iostat=io_info) kappa
186 case ("matvecmem", "incore")
187 read(val, *, iostat=io_info) matvecmem
188 case ("tol")
189 read(val, *, iostat=io_info) tol
190 case ("maxiter")
191 read(val, *, iostat=io_info) maxiter
192 case ("jacobi_ndiis")
193 read(val, *, iostat=io_info) jacobi_ndiis
194 case ("force")
195 read(val, *, iostat=io_info) force
196 case ("fmm")
197 read(val, *, iostat=io_info) fmm
198 case ("pm")
199 read(val, *, iostat=io_info) pm
200 case ("pl")
201 read(val, *, iostat=io_info) pl
202 case ("switching")
203 read(val, *, iostat=io_info) switching
204 case ("nsph")
205 read(val, *, iostat=io_info) nsph
206 if (io_info .eq. 0 .and. nsph .le. 0) &
207 & error_text = "`nsph` must be positive"
208 case ("atoms")
209 if (nsph .le. 0) then
210 error_text = "`nsph` must be given before `atoms`"
211 else
212 allocate(charges(nsph), csph(3, nsph), radii(nsph))
213 in_atoms = .true.
214 end if
215 case default
216 error_text = "unknown key `" // trim(key) // "`"
217 end select
218 if (io_info .ne. 0 .and. len_trim(error_text) .eq. 0) &
219 & error_text = "invalid value for `" // trim(key) // "`"
220 end if
221
222 if (len_trim(error_text) .gt. 0) then
223 write(msg, "(a,a,i0,a,a)") trim(fname), ", line ", lineno, &
224 & ": ", trim(error_text)
225 call update_error(ddx_error, trim(msg))
226 exit
227 end if
228 ! all atoms read: ignore whatever follows
229 if (in_atoms .and. natoms .eq. nsph) exit
230 end do
231 close(unit)
232 if (ddx_error % flag .ne. 0) return
233
234 if (.not. have_model) &
235 & call update_error(ddx_error, "missing key `model`")
236 if (have_model .and. (model .lt. 1 .or. model .gt. 3)) &
237 & call update_error(ddx_error, "`model` must be cosmo," // &
238 & " pcm, lpb or 1..3")
239 if (.not. have_eps) &
240 & call update_error(ddx_error, "missing key `eps`")
241 if (.not. in_atoms) then
242 call update_error(ddx_error, "missing `atoms` section")
243 else if (natoms .lt. nsph) then
244 write(msg, "(a,i0,a,i0,a)") "`nsph` is ", nsph, " but only ", &
245 & natoms, " atom lines were found"
246 call update_error(ddx_error, trim(msg))
247 end if
248 if (nproc .lt. 0) &
249 & call update_error(ddx_error, "`nproc` must be non-negative")
250 if (lmax .lt. 0) &
251 & call update_error(ddx_error, "`lmax` must be non-negative")
252 if (ngrid .lt. 0) &
253 & call update_error(ddx_error, "`ngrid` must be non-negative")
254 if (have_eps .and. eps .lt. zero) &
255 & call update_error(ddx_error, "`eps` must be non-negative")
256 if (se .lt. -one .or. se .gt. one) &
257 & call update_error(ddx_error, "`se` must be in [-1, 1]")
258 if (eta .lt. zero .or. eta .gt. one) &
259 & call update_error(ddx_error, "`eta` must be in [0, 1]")
260 if (kappa .lt. zero) &
261 & call update_error(ddx_error, "`kappa` must be non-negative")
262 if (matvecmem .lt. 0 .or. matvecmem .gt. 1) &
263 & call update_error(ddx_error, "`matvecmem` must be 0 or 1")
264 if (tol .lt. 1d-14 .or. tol .gt. one) &
265 & call update_error(ddx_error, "`tol` must be in [1d-14, 1]")
266 if (maxiter .le. 0) &
267 & call update_error(ddx_error, "`maxiter` must be positive")
268 if (jacobi_ndiis .lt. 0) &
269 & call update_error(ddx_error, "`jacobi_ndiis` must be" // &
270 & " non-negative")
271 if (force .lt. 0 .or. force .gt. 1) &
272 & call update_error(ddx_error, "`force` must be 0 or 1")
273 if (fmm .lt. 0 .or. fmm .gt. 1) &
274 & call update_error(ddx_error, "`fmm` must be 0 or 1")
275 if (pm .lt. 0 .or. pl .lt. 0) &
276 & call update_error(ddx_error, "`pm` and `pl` must be" // &
277 & " non-negative")
278 if (ddx_error % flag .ne. 0) return
279
280 ! The default switching shift depends on the model
281 if (.not.have_se) se = default_se(model)
282 csph = csph * tobohr
283 radii = radii * tobohr
284 kappa = kappa / tobohr
285 call closest_supported_lebedev_grid(ngrid)
286
287 call ddinit(model, nsph, csph, radii, eps, ddx_data, ddx_error, &
288 & force=force, kappa=kappa, eta=eta, shift=se, lmax=lmax, &
289 & ngrid=ngrid, incore=matvecmem, maxiter=maxiter, &
290 & jacobi_ndiis=jacobi_ndiis, enable_fmm=fmm, pm=pm, pl=pl, &
291 & nproc=nproc, logfile=output_filename, switching=switching)
292 if (ddx_error % flag .ne. 0) then
293 call update_error(ddx_error, "ddinit returned an error," // &
294 & " exiting")
295 return
296 end if
297end subroutine ddfromfile
298
309subroutine ddfromfile_legacy(fname, ddx_data, tol, charges, ddx_error)
310 implicit none
311 character(len=*), intent(in) :: fname
312 type(ddx_type), intent(out) :: ddx_data
313 real(dp), intent(out) :: tol
314 real(dp), allocatable, intent(out) :: charges(:)
315 type(ddx_error_type), intent(inout) :: ddx_error
316 ! Local variables
317 integer :: nproc, model, lmax, ngrid, force, fmm, pm, pl, &
318 & nsph, i, matvecmem, maxiter, jacobi_ndiis, &
319 & istatus
320 real(dp) :: eps, se, eta, kappa
321 real(dp), allocatable :: csph(:, :), radii(:)
322 character(len=255) :: output_filename
323
324 write(6, *) "Warning: the legacy input file is deprecated, not" // &
325 & " every parameter can be set with it."
326
327 !! Read all the parameters from the file
328 ! Open a configuration file
329 open(unit=100, file=fname, form="formatted", access="sequential")
330 ! Printing flag
331 read(100, *) output_filename
332 ! Number of OpenMP threads to be used
333 read(100, *) nproc
334 if(nproc .lt. 0) then
335 call update_error(ddx_error, "Error on the 2nd line of a config " // &
336 & "file " // trim(fname) // ": `nproc` must be a positive " // &
337 & "integer value.")
338 end if
339 ! Model to be used: 1 for COSMO, 2 for PCM and 3 for LPB
340 read(100, *) model
341 if((model .lt. 1) .or. (model .gt. 3)) then
342 call update_error(ddx_error, "Error on the 3rd line of a config file " // &
343 & trim(fname) // ": `model` must be an integer of a value " // &
344 & "1, 2 or 3.")
345 end if
346 ! Max degree of modeling spherical harmonics
347 read(100, *) lmax
348 if(lmax .lt. 0) then
349 call update_error(ddx_error, "Error on the 4th line of a config file " // &
350 & trim(fname) // ": `lmax` must be a non-negative integer value.")
351 end if
352 ! Approximate number of Lebedev points
353 read(100, *) ngrid
354 if(ngrid .lt. 0) then
355 call update_error(ddx_error, "Error on the 5th line of a config file " // &
356 & trim(fname) // ": `ngrid` must be a non-negative integer value.")
357 end if
358 ! Dielectric permittivity constant of the solvent
359 read(100, *) eps
360 if(eps .lt. zero) then
361 call update_error(ddx_error, "Error on the 6th line of a config file " // &
362 & trim(fname) // ": `eps` must be a non-negative floating " // &
363 & "point value.")
364 end if
365 ! Shift of the regularized characteristic function
366 read(100, *) se
367 if((se .lt. -one) .or. (se .gt. one)) then
368 call update_error(ddx_error, "Error on the 7th line of a config file " // &
369 & trim(fname) // ": `se` must be a floating point value in a " // &
370 & " range [-1, 1].")
371 end if
372 ! Regularization parameter
373 read(100, *) eta
374 if((eta .lt. zero) .or. (eta .gt. one)) then
375 call update_error(ddx_error, "Error on the 8th line of a config file " // &
376 & trim(fname) // ": `eta` must be a floating point value " // &
377 & "in a range [0, 1].")
378 end if
379 ! Debye H\"{u}ckel parameter
380 read(100, *) kappa
381 if(kappa .lt. zero) then
382 call update_error(ddx_error, "Error on the 9th line of a config file " // &
383 & trim(fname) // ": `kappa` must be a non-negative floating " // &
384 & "point value.")
385 end if
386 ! whether the (sparse) matrices are precomputed and kept in memory (1)
387 ! or not (0).
388 read(100, *) matvecmem
389 if((matvecmem.lt. 0) .or. (matvecmem .gt. 1)) then
390 call update_error(ddx_error, "Error on the 10th line of a config " // &
391 & "file " // trim(fname) // ": `matvecmem` must be an " // &
392 & "integer value of a value 0 or 1.")
393 end if
394 ! Relative convergence threshold for the iterative solver
395 read(100, *) tol
396 if((tol .lt. 1d-14) .or. (tol .gt. one)) then
397 call update_error(ddx_error, "Error on the 12th line of a config " // &
398 & "file " // trim(fname) // ": `tol` must be a floating " // &
399 & "point value in a range [1d-14, 1].")
400 end if
401 ! Maximum number of iterations for the iterative solver
402 read(100, *) maxiter
403 if((maxiter .le. 0)) then
404 call update_error(ddx_error, "Error on the 13th line of a config " // &
405 & "file " // trim(fname) // ": `maxiter` must be a positive " // &
406 & " integer value.")
407 end if
408 ! Number of extrapolation points for Jacobi/DIIS solver
409 read(100, *) jacobi_ndiis
410 if((jacobi_ndiis .lt. 0)) then
411 call update_error(ddx_error, "Error on the 14th line of a config " // &
412 & "file " // trim(fname) // ": `jacobi_ndiis` must be a " // &
413 & "non-negative integer value.")
414 end if
415 ! Whether to compute (1) or not (0) forces as analytical gradients
416 read(100, *) force
417 if((force .lt. 0) .or. (force .gt. 1)) then
418 call update_error(ddx_error, "Error on the 17th line of a config " // &
419 & "file " // trim(fname) // ": `force` must be an integer " // &
420 "value of a value 0 or 1.")
421 end if
422 ! Whether to use (1) or not (0) the FMM to accelerate computations
423 read(100, *) fmm
424 if((fmm .lt. 0) .or. (fmm .gt. 1)) then
425 call update_error(ddx_error, "Error on the 18th line of a config " // &
426 & "file " // trim(fname) // ": `fmm` must be an integer " // &
427 & "value of a value 0 or 1.")
428 end if
429 ! Max degree of multipole spherical harmonics for the FMM
430 read(100, *) pm
431 if(pm .lt. 0) then
432 call update_error(ddx_error, "Error on the 19th line of a config " // &
433 & "file " // trim(fname) // ": `pm` must be a non-negative " // &
434 & "integer value.")
435 end if
436 ! Max degree of local spherical harmonics for the FMM
437 read(100, *) pl
438 if(pl .lt. 0) then
439 call update_error(ddx_error, "Error on the 20th line of a config " // &
440 & "file " // trim(fname) // ": `pl` must be a non-negative " // &
441 & "integer value.")
442 end if
443 ! Number of input spheres
444 read(100, *) nsph
445 if(nsph .le. 0) then
446 call update_error(ddx_error, "Error on the 21th line of a config " // &
447 & "file " // trim(fname) // ": `nsph` must be a positive " // &
448 & "integer value.")
449 end if
450
451 ! return in case of errors in the parameters
452 if (ddx_error % flag .ne. 0) return
453
454 ! Coordinates, radii and charges
455 allocate(charges(nsph), csph(3, nsph), radii(nsph), stat=istatus)
456 if(istatus .ne. 0) then
457 call update_error(ddx_error, "Could not allocate space for " // &
458 & "coordinates, radii and charges of atoms.")
459 return
460 end if
461 do i = 1, nsph
462 read(100, *) charges(i), csph(1, i), csph(2, i), csph(3, i), radii(i)
463 end do
464 ! Finish reading
465 close(100)
466 !! Convert Angstrom input into Bohr units
467 csph = csph * tobohr
468 radii = radii * tobohr
469 kappa = kappa / tobohr
470
471 ! adjust ngrid
472 call closest_supported_lebedev_grid(ngrid)
473
474 ! Initialize ddx_data object
475 call ddinit(model, nsph, csph, radii, eps, ddx_data, ddx_error, &
476 & force=force, kappa=kappa, eta=eta, shift=se, lmax=lmax, &
477 & ngrid=ngrid, incore=matvecmem, maxiter=maxiter, &
478 & jacobi_ndiis=jacobi_ndiis, enable_fmm=fmm, pm=pm, pl=pl, &
479 & nproc=nproc, logfile=output_filename)
480
481 if (ddx_error % flag .ne. 0) then
482 call update_error(ddx_error, "ddinit returned an error, exiting")
483 return
484 end if
485 !! Clean local temporary data
486 deallocate(radii, csph, stat=istatus)
487 if(istatus .ne. 0) then
488 call update_error(ddx_error, "Could not deallocate space for " // &
489 & "coordinates, radii and charges of atoms")
490 return
491 end if
492end subroutine ddfromfile_legacy
493
494end module ddx_input
subroutine ddinit(model, nsph, coords, radii, eps, ddx_data, ddx_error, force, kappa, eta, shift, lmax, ngrid, incore, maxiter, jacobi_ndiis, enable_fmm, pm, pl, nproc, logfile, adjoint, eps_int, switching)
Wrapper to the initialization routine which supports optional arguments. This will make future mainte...
Definition: ddx.f90:59
subroutine ddfromfile(fname, ddx_data, tol, charges, ddx_error)
Read the configuration from ddX input file and return a ddx_data structure.
Definition: ddx_input.f90:38
subroutine ddfromfile_legacy(fname, ddx_data, tol, charges, ddx_error)
Read the configuration from ddX input file and return a ddx_data structure.
Definition: ddx_input.f90:310
Module defining default parameters, for consistency between the various initialization routines.
Definition: ddx_defaults.f90:3
Compile-time constants and definitions.
Functionality to read input files for the standalone driver.
Definition: ddx_input.f90:3
High-level module of the ddX software.
Definition: ddx.f90:2
ddX type for containing ddx_error information
Definition: ddx_errors.f90:6