Coverage for gpaw/dft.py: 84%
435 statements
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-08 00:17 +0000
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-08 00:17 +0000
1from __future__ import annotations
3import importlib
4import warnings
5from pathlib import Path
6from typing import IO, TYPE_CHECKING, Any, Sequence, Union, Literal
8import numpy as np
9from ase import Atoms
10from ase.calculators.calculator import kpts2sizeandoffsets
11from numpy.typing import DTypeLike
13from gpaw.mpi import MPIComm
14from gpaw.new.calculation import DFTCalculation
15from gpaw.new.logger import Logger
16from gpaw.new.symmetry import Symmetries, create_symmetries_object
17from gpaw.new.pwfd.davidson import Davidson as DavidsonEigensolver
18from gpaw.new.pwfd.rmmdiis import RMMDIIS as RMMDIISEigensolver
20if TYPE_CHECKING:
21 from gpaw.new.ase_interface import ASECalculator
23PARAMETER_NAMES = [
24 'mode', 'basis', 'charge', 'convergence', 'eigensolver', 'environment',
25 'experimental', 'extensions', 'gpts', 'h', 'hund',
26 'interpolation', 'kpts', 'magmoms', 'maxiter', 'mixer', 'nbands',
27 'occupations', 'parallel', 'poissonsolver', 'random', 'setups', 'soc',
28 'spinpol', 'symmetry', 'xc']
31class DeprecatedParameterWarning(FutureWarning):
32 """Warning class for when a parameter or its value is deprecated."""
35class Parameter:
36 def __repr__(self):
37 args = ', '.join(f'{k}={v!r}' for k, v in self.todict().items())
38 return f'{self.__class__.__name__}({args})'
40 def _not_none(self, *keys: str) -> dict:
41 dct = {}
42 for key in keys:
43 value = self.__dict__[key]
44 if value is not None:
45 dct[key] = value
46 return dct
49class Mode(Parameter):
50 qspiral = None
52 def __init__(self,
53 *,
54 dtype: DTypeLike | None = None,
55 force_complex_dtype: bool = False):
56 self.dtype = dtype
57 self.force_complex_dtype = force_complex_dtype
58 self.name = self.__class__.__name__.lower()
60 def todict(self) -> dict:
61 dct = self._not_none('dtype')
62 if self.force_complex_dtype:
63 dct['force_complex_dtype'] = True
64 return dct
66 @classmethod
67 def from_param(cls, mode) -> Mode:
68 if isinstance(mode, str):
69 mode = {'name': mode}
70 if isinstance(mode, dict):
71 mode = mode.copy()
72 return {'pw': PW,
73 'lcao': LCAO,
74 'fd': FD,
75 'tb': TB}[mode.pop('name')](**mode)
76 return mode
78 def dft_components_builder(self, atoms, params, *, log=None, comm=None):
79 module = importlib.import_module(f'gpaw.new.{self.name}.builder')
80 return getattr(module, f'{self.name.upper()}DFTComponentsBuilder')(
81 atoms, params, log=log, comm=comm)
84class PW(Mode):
85 def __init__(self,
86 ecut: float = 340,
87 *,
88 qspiral=None,
89 dedecut=None,
90 dtype: DTypeLike | None = None,
91 force_complex_dtype: bool = False):
92 """PW-mode.
94 Parameters
95 ==========
96 ecut:
97 Plane-wave cutoff energy in eV.
98 """
99 self.ecut = ecut
100 self.qspiral = qspiral
101 self.dedecut = dedecut
102 super().__init__(dtype=dtype,
103 force_complex_dtype=force_complex_dtype)
105 def todict(self):
106 dct = super().todict()
107 dct |= self._not_none('ecut', 'qspiral', 'dedecut')
108 return dct
111class LCAO(Mode):
112 distribution = '?'
114 def __init__(self,
115 *,
116 dtype: DTypeLike | None = None,
117 force_complex_dtype: bool = False):
118 super().__init__(dtype=dtype,
119 force_complex_dtype=force_complex_dtype)
122class FD(Mode):
123 def __init__(self,
124 *,
125 nn=3,
126 dtype: DTypeLike | None = None,
127 force_complex_dtype: bool = False):
128 self.nn = nn
129 super().__init__(dtype=dtype,
130 force_complex_dtype=force_complex_dtype)
132 def todict(self):
133 dct = super().todict()
134 if self.nn != 3:
135 dct['nn'] = self.nn
136 return dct
139class TB(Mode):
140 distribution = '?'
143class Eigensolver(Parameter):
144 @classmethod
145 def from_param(cls, eigensolver):
146 if isinstance(eigensolver, str):
147 eigensolver = {'name': eigensolver}
148 elif not isinstance(eigensolver, dict):
149 return eigensolver
150 if 'name' in eigensolver:
151 eigensolver = eigensolver.copy()
152 name = eigensolver.pop('name')
153 if name == 'dav':
154 name = 'davidson'
155 warnings.warn('Please use "davidson" instead of "dav"')
156 if name in eigensolvers:
157 return eigensolvers[name](**eigensolver)
158 raise ValueError(f'Unknown eigensolver: {name}')
159 return DefaultEigensolver(eigensolver)
162class DefaultEigensolver(Eigensolver):
163 def __init__(self, params: dict):
164 self.params = params
166 def todict(self):
167 return self.params
170class PWFDEigensolverParamater(Eigensolver):
171 def __init__(self,
172 niter: int = 2,
173 max_buffer_mem: int = 200 * 1024**2):
174 self.niter = niter
175 self.max_buffer_mem = max_buffer_mem
177 def todict(self):
178 return {'niter': self.niter}
180 def build(self,
181 nbands,
182 wf_desc,
183 band_comm,
184 hamiltonian,
185 converge_bands,
186 setups,
187 atoms):
188 return self.cls(
189 nbands,
190 wf_desc,
191 band_comm,
192 hamiltonian,
193 converge_bands,
194 niter=self.niter,
195 max_buffer_mem=self.max_buffer_mem)
198class Davidson(PWFDEigensolverParamater):
199 name = 'davidson'
200 cls = DavidsonEigensolver
203class RMMDIIS(PWFDEigensolverParamater):
204 name = 'rmm-diis'
205 cls = RMMDIISEigensolver
207 def __init__(self,
208 niter: int = 1,
209 max_buffer_mem: int = 200 * 1024**2,
210 trial_step: float | None = None):
211 self.niter = niter
212 self.max_buffer_mem = max_buffer_mem
213 self.trial_step = trial_step
215 def todict(self):
216 return {'niter': self.niter,
217 'max_buffer_mem': self.max_buffer_mem,
218 'trial_step': self.trial_step}
220 def build(self,
221 nbands,
222 wf_desc,
223 band_comm,
224 create_preconditioner,
225 converge_bands,
226 setups,
227 atoms):
228 return self.cls(
229 nbands,
230 wf_desc,
231 band_comm,
232 create_preconditioner,
233 converge_bands,
234 niter=self.niter,
235 max_buffer_mem=self.max_buffer_mem,
236 trial_step=self.trial_step)
239class LCAOEigensolver(Eigensolver):
240 name = 'lcao'
242 def build_lcao(self, basis, relpos_ac, cell_cv, symmetries):
243 from gpaw.new.lcao.eigensolver import LCAOEigensolver as LCAOES
244 return LCAOES(basis)
247class HybridLCAOEigensolver(LCAOEigensolver):
248 def build_lcao(self, basis, relpos_ac, cell_cv, symmetries):
249 from gpaw.new.lcao.hybrids import HybridLCAOEigensolver as HLCAOES
250 return HLCAOES(basis, relpos_ac, cell_cv)
253class Scissors(LCAOEigensolver):
254 name = 'scissors'
256 def __init__(self, shifts: list):
257 self.shifts = shifts
259 def todict(self):
260 return {'shifts': self.shifts}
262 def build_lcao(self, basis, relpos_ac, cell_cv, symmetries):
263 from gpaw.lcao.scissors import ScissorsLCAOEigensolver
264 return ScissorsLCAOEigensolver(basis,
265 self.shifts,
266 symmetries)
269eigensolvers = {
270 'davidson': Davidson,
271 'rmm-diis': RMMDIIS,
272 'lcao': LCAOEigensolver,
273 'hybrid-lcao': HybridLCAOEigensolver,
274 'scissors': Scissors}
277class Extension(Parameter):
278 @classmethod
279 def from_param(self, extension):
280 if isinstance(extension, dict):
281 dct = extension.copy()
282 name = dct.pop('name')
283 if name == 'd3':
284 from gpaw.new.extensions import D3
285 return D3(**dct)
286 raise ValueError(name)
287 return extension
290class Environment(Parameter):
291 @classmethod
292 def from_param(self, env):
293 if env is None:
294 return Environment()
295 if isinstance(env, dict):
296 dct = env.copy()
297 name = dct.pop('name')
298 if name == 'sjm':
299 from gpaw.new.sjm import SJM
300 return SJM(**dct)
301 if name == 'solvation':
302 from gpaw.new.solvation import Solvation
303 return Solvation(**dct)
304 raise ValueError(f'Unknown environment: {name}')
305 return env
307 def build(self,
308 setups,
309 grid,
310 relpos_ac,
311 log,
312 comm):
313 from gpaw.new.environment import Environment as Env
314 return Env(len(setups))
317class Mixer(Parameter):
318 def __init__(self, params: dict):
319 self.params = params
321 def todict(self):
322 return self.params
324 @classmethod
325 def from_param(cls, mixer):
326 if isinstance(mixer, Mixer):
327 return mixer
328 return Mixer(mixer)
331class Occupations(Parameter):
332 def __init__(self, params: dict):
333 self.params = params
335 def todict(self):
336 return self.params
338 @classmethod
339 def from_param(cls, occupations):
340 if isinstance(occupations, dict):
341 return Occupations(occupations)
342 return occupations
345class PoissonSolver(Parameter):
346 def __init__(self, params: dict):
347 self.params = params
349 def todict(self):
350 return self.params
352 @classmethod
353 def from_param(cls, ps):
354 if isinstance(ps, dict):
355 return PoissonSolver(ps)
356 return ps
358 def build(self, *, grid, xp=np):
359 from gpaw.poisson import PoissonSolver as make_poisson_solver
360 solver = make_poisson_solver(**self.params, xp=xp)
361 return solver.build(grid, xp)
364def array_or_none(a):
365 if a is None:
366 return None
367 return np.array(a)
370class Symmetry(Parameter):
371 def __init__(self,
372 *,
373 rotations: np.ndarray | None = None,
374 translations: np.ndarray | None = None,
375 atommaps: np.ndarray | None = None,
376 extra_ids: Sequence[int] | None = None,
377 tolerance: float | None = None, # Å
378 point_group: bool = True,
379 symmorphic: bool = True,
380 time_reversal: bool = True):
381 self.rotations = array_or_none(rotations)
382 self.translations = array_or_none(translations)
383 self.atommaps = array_or_none(atommaps)
384 self.extra_ids = array_or_none(extra_ids)
385 self.tolerance = tolerance
386 self.point_group = point_group
387 self.symmorphic = symmorphic
388 self.time_reversal = time_reversal
390 @classmethod
391 def from_param(cls, s):
392 if isinstance(s, Symmetry):
393 return s
394 if isinstance(s, str):
395 if s == 'off':
396 return Symmetry(point_group=False, time_reversal=False)
397 if s == 'on':
398 return Symmetry()
399 raise ValueError()
400 if 'name' in s:
401 s = s.copy()
402 del s['name']
403 return Symmetry(**(s or {}))
405 def todict(self):
406 dct = self._not_none('rotations', 'translations', 'atommaps',
407 'extra_ids', 'tolerance')
408 for name in ['point_group', 'symmorphic', 'time_reversal']:
409 if not getattr(self, name):
410 dct[name] = False
411 return dct
413 def build(self,
414 atoms: Atoms,
415 *,
416 setup_ids: Sequence | None = None,
417 magmoms: np.ndarray | None = None,
418 _backwards_compatible=False) -> Symmetries:
419 return create_symmetries_object(
420 atoms,
421 setup_ids=setup_ids,
422 magmoms=magmoms,
423 rotations=self.rotations,
424 translations=self.translations,
425 atommaps=self.atommaps,
426 extra_ids=self.extra_ids,
427 tolerance=self.tolerance,
428 point_group=self.point_group,
429 symmorphic=self.symmorphic,
430 _backwards_compatible=_backwards_compatible)
433class BZSampling(Parameter):
434 @classmethod
435 def from_param(cls, kpts):
436 if isinstance(kpts, BZSampling):
437 return kpts
438 if hasattr(kpts, 'kpts'):
439 return KPoints(kpts.kpts)
440 if isinstance(kpts, dict):
441 if 'kpts' in kpts:
442 return KPoints(kpts['kpts'])
443 if 'path' in kpts:
444 return BandPath(**kpts)
445 kpts = kpts.copy()
446 kpts.pop('name', '')
447 else:
448 kpts = np.array(kpts)
449 if kpts.ndim == 1:
450 kpts = {'size': kpts}
451 else:
452 return KPoints(kpts)
453 return MonkhorstPack(**kpts)
456class KPoints(BZSampling):
457 def __init__(self,
458 kpts: Sequence[Sequence[float]]):
459 self.kpts = kpts
461 def todict(self):
462 return {'kpts': self.kpts}
464 def build(self, atoms):
465 from gpaw.new.brillouin import BZPoints
466 return BZPoints(self.kpts)
469class MonkhorstPack(BZSampling):
470 def __init__(self,
471 size: Sequence[int] | None = None,
472 density: float | None = None,
473 gamma: bool | None = None):
474 self.size = size
475 self.density = density
476 self.gamma = gamma
478 def todict(self):
479 dct = {}
480 if self.size is not None:
481 dct['size'] = self.size
482 if self.density is not None:
483 dct['density'] = self.density
484 if self.gamma is not None:
485 dct['gamma'] = self.gamma
486 return dct
488 def build(self, atoms):
489 from gpaw.new.brillouin import MonkhorstPackKPoints
490 size, offset = kpts2sizeandoffsets(**self.todict(), atoms=atoms)
491 for n, periodic in zip(size, atoms.pbc):
492 if not periodic and n != 1:
493 raise ValueError('K-points can only be used with PBCs!')
494 return MonkhorstPackKPoints(size, offset)
497class BandPath(BZSampling):
498 def __init__(self,
499 path: str,
500 npoints: int):
501 self.path = path
502 self.npoints = npoints
504 def todict(self):
505 return {'path': self.path, 'npoints': self.npoints}
507 def build(self, atoms):
508 from gpaw.new.brillouin import BZBandPath
509 return BZBandPath(atoms.cell.bandpath(self.path,
510 npoints=self.npoints,
511 pbc=atoms.pbc))
514class XC(Parameter):
515 def __init__(self, name, **kwargs):
516 self.name = name
517 self.kwargs = kwargs
519 def todict(self):
520 return {'name': self.name, **self.kwargs}
522 def functional(self, collinear):
523 from gpaw.xc import XC as xc
524 return xc({'name': self.name, **self.kwargs},
525 collinear=collinear)
527 @classmethod
528 def from_param(cls, xc):
529 if isinstance(xc, XC):
530 return xc
531 if isinstance(xc, str):
532 xc = {'name': xc}
533 return XC(**xc)
536KptsType = Union[Sequence[int], dict, Sequence[Sequence[float]]]
538PARALLEL_KEYS = {
539 'kpt', 'domain', 'band', 'order', 'stridebands', 'augment_grids',
540 'sl_auto', 'sl_default', 'sl_diagonalize', 'sl_inverse_cholesky',
541 'sl_lcao', 'sl_lrtddft', 'use_elpa', 'elpasolver', 'buffer_size', 'gpu'}
544class Parameters:
545 def __init__(
546 self,
547 *,
548 mode: str | dict | Mode,
549 basis: str | dict[str | int | None, str] | None = None,
550 charge: float | None = None,
551 convergence: dict | None = None,
552 eigensolver: str | dict | Eigensolver | None = None,
553 environment=None,
554 experimental: dict | None = None,
555 extensions: Sequence[Extension] | None = None,
556 gpts: Sequence[int] | None = None,
557 h: float | None = None,
558 hund: bool | None = None,
559 interpolation: int | Literal['fft'] | None = None,
560 kpts: KptsType | MonkhorstPack | None = None,
561 magmoms: Sequence[float] | Sequence[Sequence[float]] | None = None,
562 maxiter: int | None = None,
563 mixer: dict | Mixer | None = None,
564 nbands: int | str | None = None,
565 occupations: dict | Occupations | None = None,
566 parallel: dict | None = None,
567 poissonsolver: dict | PoissonSolver | None = None,
568 random: bool | None = None,
569 setups: str | dict | None = None,
570 soc: bool | None = None,
571 spinpol: bool | None = None,
572 symmetry: str | dict | Symmetry | None = None,
573 xc: str | dict | XC | None = None):
574 """DFT-parameters object.
576 >>> p = Parameters(mode=PW(400))
577 >>> p
578 mode=PW(ecut=400)
579 >>> p.charge
580 0.0
581 >>> p.xc
582 XC(name='LDA')
583 >>> from ase.build import molecule
584 >>> atoms = molecule('H2', vacuum=3.0)
585 >>> dft = p.dft_calculation(atoms, txt='h2.txt')
586 >>> atoms.calc = dft.ase_calculator()
588 Parameters
589 ==========
590 mode:
591 PW, LCAO or FD mode.
592 basis:
593 Basis-set. Used for LCAO calculations and wave-function initial
594 guess for PW and FD calculations. Default is to use the PAW
595 pseudo partial-waves.
596 charge:
597 Total charge of the system in units of `|e|`.
598 convergence:
599 SCF-convergence criteria.
600 eigensolver:
601 Eigensolver. Default for PW and FD mode is ``'davidson'``.
602 environment:
603 ...
604 gpts:
605 Number of real-space grid-points for wave-functions
606 (three integers).
607 h:
608 grid-spaving for wave-function grid (Å).
609 hund:
610 Use Hund's rule for initial magnetic moments.
611 experimental:
612 Experimental stuff.
613 extensions:
614 Extensions (D3, ...).
615 interpolation:
616 ...
617 kpts:
618 Brilluin-zone sampling. Default is Γ-point only.
619 magmoms:
620 Initial magnetic moments for non-collinear calculations.
621 maxiter:
622 Maximum number of allowed SCF-iterations. Default is 333.
623 mixer:
624 Density-mixing scheme.
625 nbands:
626 Number of bands.
627 occupations:
628 ...
629 parallel:
630 Parallelization strategy. Example: Force parallelization
631 over ``'kpt`` with ``{'band': 1, 'domain': 1}``.
632 poissonsolver:
633 ...
634 random:
635 Use random numbers for initial wave functions.
636 setups:
637 ...
638 soc:
639 Enable spin-orbit coupling.
640 spinpol:
641 Force spin-polarized calculation.
642 symmetry:
643 Use of symmetry. Default is to use ...
644 xc:
645 XC-functional. Default is PZ-LDA.
646 """
647 soc, magmoms = _parse_experimental(experimental, soc, magmoms)
648 self._non_defaults = [
649 key for key, value in locals().items()
650 if value is not None and key != 'self']
652 if h is not None and gpts is not None:
653 raise ValueError("""You can't use both "gpts" and "h"!""")
655 self.mode = Mode.from_param(mode)
656 basis = basis or {}
657 self.basis = ({'default': basis} if not isinstance(basis, dict)
658 else basis)
659 self.charge = charge or 0.0
660 self.convergence = convergence or {}
661 self.eigensolver = Eigensolver.from_param(eigensolver or {})
662 self.environment = Environment.from_param(environment)
663 self.experimental = experimental or {}
664 self.extensions = [Extension.from_param(ext)
665 for ext in extensions or []]
666 self.gpts = np.array(gpts) if gpts is not None else None
667 self.h = h
668 self.hund = hund or False
669 self.interpolation = interpolation
670 self.kpts = BZSampling.from_param((1, 1, 1) if kpts is None else kpts)
671 self.magmoms = np.array(magmoms) if magmoms is not None else None
672 self.maxiter = maxiter or 333
673 self.mixer = Mixer.from_param(mixer or {})
674 self.nbands = nbands
675 self.occupations = Occupations.from_param(occupations or {})
676 self.parallel = parallel or {}
677 self.poissonsolver = PoissonSolver.from_param(poissonsolver or {})
678 self.random = random or False
679 setups = setups or 'paw'
680 self.setups = ({'default': setups} if isinstance(setups, str)
681 else setups)
682 self.soc = soc or False
683 self.spinpol = spinpol or False
684 self.symmetry = Symmetry.from_param(symmetry or 'on')
685 self.xc = XC.from_param(xc or 'LDA')
687 _fix_legacy_stuff(self)
689 for key in self.parallel:
690 if key not in PARALLEL_KEYS:
691 raise ValueError(
692 f'Unknown key: {key!r}. '
693 f'Must be one of {", ".join(PARALLEL_KEYS)}')
695 def __repr__(self) -> str:
696 lines = []
697 for key in self._non_defaults:
698 value = self._value(key)
699 lines.append(f'{key}={value!r}')
700 return ',\n'.join(lines)
702 def todict(self) -> dict:
703 dct = {}
704 for key in self._non_defaults:
705 value = self._value(key)
706 if hasattr(value, 'todict'):
707 name = getattr(value, 'name', None)
708 value = value.todict()
709 if name is not None:
710 value['name'] = name
711 elif key == 'extensions':
712 value = [{'name': x.name, **x.todict()}
713 for x in self.extensions]
714 dct[key] = value
715 return dct
717 def _value(self, key: str) -> Any:
718 value = self.__dict__[key]
719 if key == 'basis':
720 if list(value) == [None]:
721 value = value[None]
722 return value
724 def dft_component_builder(self, atoms, *, comm=None, log=None):
725 return self.mode.dft_components_builder(
726 atoms, self, comm=comm, log=log)
728 def dft_calculation(self,
729 atoms,
730 txt: str | Path | IO[str] | None = '-',
731 communicator: MPIComm | Sequence[int] | None = None
732 ) -> DFTCalculation:
733 log = Logger(txt, communicator)
734 return DFTCalculation.from_parameters(atoms, self, log.comm, log)
736 def dft_info(self, atoms):
737 ...
740def _parse_experimental(experimental: dict | None,
741 soc: bool | None,
742 magmoms) -> tuple:
743 if experimental is None:
744 return soc, magmoms
745 if experimental.pop('niter_fixdensity', None) is not None:
746 warnings.warn('Ignoring "niter_fixdensity".')
747 if 'reuse_wfs_method' in experimental:
748 del experimental['reuse_wfs_method']
749 warnings.warn('Ignoring "reuse_wfs_method".')
750 if 'soc' in experimental:
751 warnings.warn('Please use new "soc" parameter.',
752 DeprecatedParameterWarning)
753 assert soc is None
754 soc = experimental.pop('soc')
755 if 'magmoms' in experimental:
756 warnings.warn('Please use new "magmoms" parameter.',
757 DeprecatedParameterWarning)
758 assert magmoms is None
759 magmoms = experimental.pop('magmoms')
760 unknown = experimental.keys() - {'backwards_compatible',
761 'ccirs',
762 'fast_pw_init'}
763 if unknown:
764 warnings.warn(f'Unknown experimental keyword(s): {unknown}',
765 stacklevel=3)
766 return soc, magmoms
769def _fix_legacy_stuff(params: Parameters) -> None:
770 if not isinstance(params.mode, Mode):
771 dct = params.mode.todict()
772 if 'interpolation' in dct:
773 params.interpolation = dct.pop('interpolation')
774 params.mode = Mode.from_param(dct)
775 if not isinstance(params.eigensolver, Eigensolver):
776 params.eigensolver = Eigensolver.from_param(
777 params.eigensolver.todict())
778 if not isinstance(params.mixer, Mixer):
779 params.mixer = Mixer.from_param(params.mixer.todict())
782def DFT(
783 atoms: Atoms,
784 *,
785 mode: str | dict | Mode,
786 basis: str | dict[str | int | None, str] | None = None,
787 charge: float | None = None,
788 convergence: dict | None = None,
789 eigensolver: str | dict | Eigensolver | None = None,
790 environment=None,
791 experimental: dict | None = None,
792 extensions: Sequence[Extension] | None = None,
793 gpts: Sequence[int] | None = None,
794 h: float | None = None,
795 hund: bool | None = None,
796 interpolation: int | None = None,
797 kpts: KptsType | MonkhorstPack | None = None,
798 magmoms: Sequence[float] | Sequence[Sequence[float]] | None = None,
799 maxiter: int | None = None,
800 mixer: dict | Mixer | None = None,
801 nbands: int | str | None = None,
802 occupations: dict | Occupations | None = None,
803 parallel: dict | None = None,
804 poissonsolver: dict | PoissonSolver | None = None,
805 random: bool | None = None,
806 setups: str | dict | None = None,
807 soc: bool | None = None,
808 spinpol: bool | None = None,
809 symmetry: str | dict | Symmetry | None = None,
810 xc: str | dict | XC | None = None,
811 txt: str | Path | IO[str] | None = '-',
812 communicator: MPIComm | Sequence[int] | None = None) -> DFTCalculation:
813 """Create a DFTCalculation object.
815 See :class:`gpaw.dft.Parameters` for the complete list of parameters.
817 Parameters
818 ==========
819 atoms:
820 ASE-Atoms object.
821 txt:
822 Text log-file. Use ``None`` for no loggin and ``'-'`` for using
823 standard out.
824 communicator:
825 MPI-communicator. Default is to use ``gpaw.mpi.world``.
827 """
828 params = Parameters(**{k: v for k, v in locals().items()
829 if k in PARAMETER_NAMES})
830 return params.dft_calculation(atoms, txt, communicator)
833def GPAW(
834 filename: str | Path | IO[str] | None = None,
835 *,
836 basis: str | dict[str | int | None, str] | None = None,
837 charge: float | None = None,
838 convergence: dict | None = None,
839 eigensolver: str | dict | Eigensolver | None = None,
840 environment=None,
841 experimental: dict | None = None,
842 extensions: Sequence[Extension] | None = None,
843 gpts: Sequence[int] | None = None,
844 h: float | None = None,
845 hund: bool | None = None,
846 interpolation: int | None = None,
847 kpts: KptsType | MonkhorstPack | None = None,
848 magmoms: Sequence[float] | Sequence[Sequence[float]] | None = None,
849 maxiter: int | None = None,
850 mixer: dict | Mixer | None = None,
851 mode: str | dict | Mode | None = None,
852 nbands: int | str | None = None,
853 occupations: dict | Occupations | None = None,
854 parallel: dict | None = None,
855 poissonsolver: dict | PoissonSolver | None = None,
856 random: bool | None = None,
857 setups: str | dict | None = None,
858 soc: bool | None = None,
859 spinpol: bool | None = None,
860 symmetry: str | dict | Symmetry | None = None,
861 xc: str | dict | XC | None = None,
862 txt: str | Path | IO[str] | None = '?',
863 communicator: MPIComm | Sequence[int] | None = None,
864 object_hooks=None) -> ASECalculator:
865 """Create ASE-compatible GPAW calculator.
867 See :class:`gpaw.dft.Parameters` for the complete list of parameters.
869 Parameters
870 ==========
871 filename:
872 Name of gpw-file to restart from.
873 txt:
874 Text log-file. Use ``None`` for no loggin and ``'-'``
875 for using standard out.
876 communicator:
877 MPI-communicator. Default is to use ``gpaw.mpi.world``.
878 object_hooks:
879 Dictionart of hook-functions to create custom parameter-objects.
880 """
881 from gpaw.new.ase_interface import ASECalculator
882 from gpaw.new.gpw import read_gpw
884 if txt == '?':
885 txt = '-' if filename is None else None
887 log = Logger(txt, communicator)
889 if mode is None:
890 del mode
892 kwargs = {key: value for key, value in locals().items()
893 if key in PARAMETER_NAMES}
895 if filename is not None:
896 args = Parameters(mode='pw', **kwargs)._non_defaults
897 if set(args) > {'mode', 'parallel'}:
898 raise ValueError(
899 'Illegal argument(s) when reading from a file: '
900 f'{", ".join(args)}')
901 atoms, dft, params, _ = read_gpw(filename,
902 log=log,
903 parallel=parallel,
904 object_hooks=object_hooks)
905 return ASECalculator(params,
906 log=log, dft=dft, atoms=atoms)
908 params = Parameters(**kwargs)
909 return ASECalculator(params, log=log)