Coverage for gpaw/lcaotddft/__init__.py: 84%
126 statements
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-14 00:18 +0000
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-14 00:18 +0000
1from __future__ import annotations
3from typing import Optional
5import numpy as np
6from ase.units import Bohr, Hartree
8from gpaw import GPAW_NEW
9from gpaw.calculator import GPAW
10from gpaw.external import ConstantElectricField, ExternalPotential
11from gpaw.lcaotddft.hamiltonian import TimeDependentHamiltonian
12from gpaw.lcaotddft.logger import TDDFTLogger
13from gpaw.lcaotddft.propagators import create_propagator
14from gpaw.tddft.units import attosec_to_autime
15from gpaw.typing import Any, Vector
18def LCAOTDDFT(filename: str, **kwargs) -> Any:
19 if GPAW_NEW:
20 from gpaw.new.rttddft import RTTDDFT
21 assert kwargs.get('propagator', None) in [None, 'ecn'], \
22 'Not implemented yet'
23 assert kwargs.get('rremisison', None) in [None], 'Not implemented yet'
24 assert kwargs.get('fxc', None) in [None], 'Not implemented yet'
25 assert kwargs.get('scale', None) in [None], 'Not implemented yet'
26 assert kwargs.get('parallel', None) in [None], 'Not implemented yet'
27 assert kwargs.get('communicator', None) in [None], \
28 'Not implemented yet'
29 new_tddft = RTTDDFT.from_dft_file(filename)
30 return new_tddft
31 return OldLCAOTDDFT(filename, **kwargs)
34class OldLCAOTDDFT(GPAW):
35 """Real-time time-propagation TDDFT calculator with LCAO basis.
37 Parameters
38 ----------
39 filename
40 File containing ground state or time-dependent state to propagate
41 propagator
42 Time propagator for the Kohn-Sham wavefunctions
43 td_potential
44 External time-dependent potential
45 rremission
46 Radiation-reaction potential for Self-consistent Light-Matter coupling
47 fxc
48 Exchange-correlation functional used for
49 the dynamic part of Hamiltonian
50 scale
51 Experimental option (use carefully).
52 Scaling factor for the dynamic part of Hamiltonian
53 parallel
54 Parallelization options
55 communicator
56 MPI communicator
57 txt
58 Text output
59 """
60 def __init__(self, filename: str, *,
61 propagator: dict | None = None,
62 td_potential: dict | None = None,
63 rremission=None,
64 fxc: str | None = None,
65 scale: float | None = None,
66 parallel: dict | None = None,
67 communicator=None,
68 txt: str = '-'):
69 """"""
70 assert filename is not None
71 self.time = 0.0
72 self.niter = 0
73 # TODO: deprecate kick keywords (and store them as td_potential)
74 self.kick_strength = np.zeros(3)
75 self.kick_ext: Optional[ExternalPotential] = None
76 self.tddft_initialized = False
77 self.action = ''
78 tdh = TimeDependentHamiltonian(fxc=fxc, td_potential=td_potential,
79 scale=scale, rremission=rremission)
80 self.td_hamiltonian = tdh
82 self.propagator_set = propagator is not None
83 self.propagator = create_propagator(propagator)
84 GPAW.__init__(self, filename, parallel=parallel,
85 communicator=communicator, txt=txt)
86 if len(self.symmetry.op_scc) > 1:
87 raise ValueError('Symmetries are not allowed for LCAOTDDFT. '
88 'Run the ground state calculation with '
89 'symmetry={"point_group": False}.')
91 self.set_positions()
93 def write(self, filename, mode=''):
94 # This function is included here in order to generate
95 # documentation for LCAOTDDFT.write() with autoclass in sphinx
96 GPAW.write(self, filename, mode=mode)
98 def _write(self, writer, mode):
99 GPAW._write(self, writer, mode)
100 if self.tddft_initialized:
101 w = writer.child('tddft')
102 w.write(time=self.time,
103 niter=self.niter,
104 kick_strength=self.kick_strength,
105 propagator=self.propagator.todict())
106 self.td_hamiltonian.write(w.child('td_hamiltonian'))
108 def read(self, filename):
109 reader = GPAW.read(self, filename)
110 if 'tddft' in reader:
111 r = reader.tddft
112 self.time = r.time
113 self.niter = r.niter
114 self.kick_strength = r.kick_strength
115 if not self.propagator_set:
116 self.propagator = create_propagator(r.propagator)
117 else:
118 self.log('Note! Propagator possibly changed!')
119 self.td_hamiltonian.wfs = self.wfs
120 self.td_hamiltonian.read(r.td_hamiltonian)
122 def tddft_init(self):
123 if self.tddft_initialized:
124 return
126 self.log('-----------------------------------')
127 self.log('Initializing time-propagation TDDFT')
128 self.log('-----------------------------------')
129 self.log()
131 assert self.wfs.dtype == complex
133 self.timer.start('Initialize TDDFT')
135 # Initialize Hamiltonian
136 self.td_hamiltonian.initialize(self)
138 # Initialize propagator
139 self.propagator.initialize(self)
141 self.log('Propagator:')
142 self.log(self.propagator.get_description())
143 self.log()
145 # Add logger
146 TDDFTLogger(self)
148 # Call observers before propagation
149 self.action = 'init'
150 self.call_observers(self.niter)
152 self.tddft_initialized = True
153 self.timer.stop('Initialize TDDFT')
155 def absorption_kick(self, kick_strength: Vector,
156 gauge: str = 'length'):
157 """Kick with a weak electric field.
159 Parameters
160 ----------
161 kick_strength
162 Strength of the kick in atomic units
163 gauge
164 Either 'length' or 'velocity'
165 """
167 assert gauge in {'length', 'velocity'}
168 self.tddft_init()
170 self.timer.start('Kick')
172 self.kick_gauge = gauge
173 self.kick_strength = np.array(kick_strength, dtype=float)
174 magnitude = np.sqrt(np.sum(self.kick_strength**2))
175 direction = self.kick_strength / magnitude
177 self.log(f'---- Applying absorption kick in {gauge} gauge')
178 self.log('---- Magnitude: %.8f Hartree/Bohr' % magnitude)
179 self.log('---- Direction: %.4f %.4f %.4f' % tuple(direction))
181 if gauge == 'length':
182 # Create hamiltonian object for absorption kick
183 cef = ConstantElectricField(magnitude * Hartree / Bohr, direction)
185 # Propagate kick
186 self.propagator.kick(cef, self.time)
187 else:
188 self.propagator.velocity_gauge_kick(magnitude,
189 direction, self.time)
191 # Call observers after kick
192 self.action = 'kick'
193 self.call_observers(self.niter)
194 self.niter += 1
195 self.timer.stop('Kick')
197 def kick(self, ext):
198 """Kick with any external potential.
200 Parameters
201 ----------
202 ext
203 External potential
204 """
205 self.tddft_init()
207 self.timer.start('Kick')
209 self.log('---- Applying kick')
210 self.log('---- %s' % ext)
212 self.kick_ext = ext
214 # Propagate kick
215 self.propagator.kick(ext, self.time)
217 # Call observers after kick
218 self.action = 'kick'
219 self.call_observers(self.niter)
220 self.niter += 1
221 self.timer.stop('Kick')
223 def propagate(self, time_step: float = 10.0, iterations: int = 2000):
224 """Propagate the electronic system.
226 Parameters
227 ----------
228 time_step
229 Time step in attoseconds
230 iterations
231 Number of propagation steps
232 """
233 self.tddft_init()
235 time_step *= attosec_to_autime
236 self.maxiter = self.niter + iterations
238 self.log('---- About to do %d propagation steps' % iterations)
240 self.timer.start('Propagate')
241 while self.niter < self.maxiter:
242 # Propagate one step
243 self.time = self.propagator.propagate(self.time, time_step)
245 # Call registered callback functions
246 self.action = 'propagate'
247 self.call_observers(self.niter)
249 self.niter += 1
250 self.timer.stop('Propagate')
252 def replay(self, **kwargs):
253 # TODO: Consider deprecating this function?
254 self.propagator = create_propagator(**kwargs)
255 self.tddft_init()
256 self.propagator.control_paw(self)