Coverage for gpaw/solvation/calculator.py: 86%

88 statements  

« prev     ^ index     » next       coverage.py v7.7.1, created at 2025-07-14 00:18 +0000

1from ase.data import chemical_symbols 

2from ase.units import Bohr, Hartree 

3from gpaw import GPAW_NEW 

4from gpaw.calculator import GPAW as OldGPAW 

5from gpaw.io import Reader 

6from gpaw.solvation.hamiltonian import SolvationRealSpaceHamiltonian 

7 

8 

9def SolvationGPAW(*args, **kwargs): 

10 if GPAW_NEW: 

11 from gpaw.new.ase_interface import GPAW 

12 solvation = dict(name='solvation', 

13 cavity=kwargs.pop('cavity'), 

14 dielectric=kwargs.pop('dielectric'), 

15 interactions=kwargs.pop('interactions', None)) 

16 return GPAW(*args, **kwargs, environment=solvation) 

17 return OldSolvationGPAW(*args, **kwargs) 

18 

19 

20class OldSolvationGPAW(OldGPAW): 

21 """Subclass of gpaw.GPAW calculator with continuum solvent model. 

22 

23 See also Section III of 

24 A. Held and M. Walter, J. Chem. Phys. 141, 174108 (2014). 

25 """ 

26 

27 def __init__(self, restart=None, cavity=None, dielectric=None, 

28 interactions=None, **gpaw_kwargs): 

29 """Constructor for SolvationGPAW class. 

30 

31 Additional arguments not present in GPAW class: 

32 cavity -- A Cavity instance. 

33 dielectric -- A Dielectric instance. 

34 interactions -- A list of Interaction instances. 

35 """ 

36 if interactions is None: 

37 interactions = [] 

38 

39 # if not all([cavity, dielectric]): 

40 # raise IOError('Cavity and dielectric modules need to be ' 

41 # 'defined in the calculator') 

42 

43 self.stuff_for_hamiltonian = (cavity, dielectric, interactions) 

44 

45 OldGPAW.__init__(self, restart, **gpaw_kwargs) 

46 

47 self.log('Implicit solvation parameters:') 

48 for stuff in self.stuff_for_hamiltonian: 

49 if isinstance(stuff, list): 

50 for instuff in stuff: 

51 self.log(instuff) 

52 else: 

53 self.log(stuff) 

54 self.log() 

55 

56 def read(self, filename): 

57 """Read yourself from a file""" 

58 self.reader = reader = Reader(filename) 

59 if 'implicit_solvent' in reader: 

60 impl_in = reader.implicit_solvent 

61 if 'name' in impl_in.cavity.effective_potential: 

62 efpot = impl_in.cavity.effective_potential 

63 

64 atomic_radii = {} 

65 for Z, r in zip(reader.atoms.numbers, efpot.atomic_radii): 

66 symbol = chemical_symbols[Z] 

67 if symbol in atomic_radii: 

68 assert atomic_radii[symbol] == r 

69 else: 

70 atomic_radii[symbol] = r 

71 

72 if efpot.name == 'SJMPower12Potential': 

73 from gpaw.solvation.sjm import SJMPower12Potential 

74 

75 effective_potential = SJMPower12Potential( 

76 atomic_radii=atomic_radii, 

77 u0=efpot.u0, 

78 H2O_layer=efpot.H2O_layer, 

79 unsolv_backside=efpot.unsolv_backside) 

80 elif efpot.name == 'Power12Potential': 

81 from gpaw.solvation.sjm import Power12Potential 

82 effective_potential = Power12Potential( 

83 atomic_radii=atomic_radii, 

84 u0=efpot.u0) 

85 else: 

86 raise OSError('Reading the given effective potential ' 

87 'is not implemented yet') 

88 

89 if 'name' in impl_in.cavity.surface_calculator: 

90 suca = impl_in.cavity.surface_calculator 

91 if suca.name == 'GradientSurface': 

92 from gpaw.solvation.cavity import GradientSurface 

93 surface_calculator = GradientSurface(suca.nn) 

94 else: 

95 raise OSError('Reading in the given used surface ' 

96 'calculator is not implemented') 

97 

98 T = impl_in.cavity.temperature 

99 

100 from gpaw.solvation.cavity import EffectivePotentialCavity 

101 cavity = EffectivePotentialCavity( 

102 effective_potential=effective_potential, 

103 temperature=T, 

104 surface_calculator=surface_calculator) 

105 

106 if impl_in.dielectric.name == 'LinearDielectric': 

107 from gpaw.solvation.dielectric import LinearDielectric 

108 dielectric = LinearDielectric(epsinf=impl_in.dielectric.epsinf) 

109 

110 if impl_in.interactions.name == 'SurfaceInteraction': 

111 suin = impl_in.interactions 

112 from gpaw.solvation.interactions import SurfaceInteraction 

113 interactions = [SurfaceInteraction(suin.surface_tension)] 

114 

115 self.stuff_for_hamiltonian = (cavity, dielectric, interactions) 

116 

117 reader = OldGPAW.read(self, filename) 

118 return reader 

119 

120 def _write(self, writer, mode): 

121 OldGPAW._write(self, writer, mode) 

122 stuff = self.stuff_for_hamiltonian 

123 writer.child('implicit_solvent').write(cavity=stuff[0], 

124 dielectric=stuff[1], 

125 interactions=stuff[2][0]) 

126 

127 def create_hamiltonian(self, realspace, mode, xc): 

128 if not realspace: 

129 raise NotImplementedError( 

130 'SolvationGPAW does not support ' 

131 'calculations in reciprocal space yet.') 

132 

133 dens = self.density 

134 self.hamiltonian = SolvationRealSpaceHamiltonian( 

135 *self.stuff_for_hamiltonian, 

136 gd=dens.gd, finegd=dens.finegd, 

137 nspins=dens.nspins, 

138 collinear=dens.collinear, 

139 setups=dens.setups, 

140 timer=self.timer, 

141 xc=xc, 

142 world=self.world, 

143 redistributor=dens.redistributor, 

144 vext=self.parameters.external, 

145 psolver=self.parameters.poissonsolver, 

146 stencil=mode.interpolation) 

147 

148 self.log(self.hamiltonian) 

149 

150 xc.set_grid_descriptor(self.hamiltonian.finegd) 

151 

152 def initialize_positions(self, atoms=None): 

153 spos_ac = OldGPAW.initialize_positions(self, atoms) 

154 self.hamiltonian.update_atoms(self.atoms, self.log) 

155 return spos_ac 

156 

157 def get_electrostatic_energy(self): 

158 """Return electrostatic part of the total energy. 

159 

160 The electrostatic part consists of everything except 

161 the short-range interactions defined in the interactions list. 

162 """ 

163 # Energy extrapolated to zero width: 

164 return Hartree * self.hamiltonian.e_el_extrapolated 

165 

166 def get_solvation_interaction_energy(self, subscript, atoms=None): 

167 """Return a specific part of the solvation interaction energy. 

168 

169 The subscript parameter defines which part is to be returned. 

170 It has to match the value of a subscript attribute of one of 

171 the interactions in the interactions list. 

172 """ 

173 return Hartree * getattr(self.hamiltonian, 'e_' + subscript) 

174 

175 def get_cavity_volume(self, atoms=None): 

176 """Return the cavity volume in Angstrom ** 3. 

177 

178 In case no volume calculator has been set for the cavity, None 

179 is returned. 

180 """ 

181 V = self.hamiltonian.cavity.V 

182 return V and V * Bohr ** 3 

183 

184 def get_cavity_surface(self, atoms=None): 

185 """Return the cavity surface area in Angstrom ** 2. 

186 

187 In case no surface calculator has been set for the cavity, 

188 None is returned. 

189 """ 

190 A = self.hamiltonian.cavity.A 

191 return A and A * Bohr ** 2