def load(self, method): """Make sure all necessary attributes have been initialized""" assert method in ('real', 'recip_gauss', 'recip_ewald'),\ str(method) + ' is an invalid method name,\n' +\ 'use either real, recip_gauss, or recip_ewald' if method.startswith('recip'): if self.gd.comm.size > 1: raise RuntimeError("Cannot do parallel FFT, use method='real'") if not hasattr(self, 'k2'): self.k2, self.N3 = construct_reciprocal(self.gd) if method.endswith('ewald') and not hasattr(self, 'ewald'): # cutoff radius assert self.gd.orthogonal rc = 0.5 * np.average(self.gd.cell_cv.diagonal()) # ewald potential: 1 - cos(k rc) self.ewald = (np.ones(self.gd.n_c) - np.cos(np.sqrt(self.k2) * rc)) # lim k -> 0 ewald / k2 self.ewald[0, 0, 0] = 0.5 * rc**2 elif method.endswith('gauss') and not hasattr(self, 'ng'): gauss = Gaussian(self.gd) self.ng = gauss.get_gauss(0) / sqrt(4 * pi) self.vg = gauss.get_gauss_pot(0) / sqrt(4 * pi) else: # method == 'real' if not hasattr(self, 'solve'): if self.poisson is not None: self.solve = self.poisson.solve else: solver = PoissonSolver(nn=2) solver.set_grid_descriptor(self.gd) solver.initialize(load_gauss=True) self.solve = solver.solve
def initialize(self, gd, load_gauss=False): # XXX this won't work now, but supposedly this class will be deprecated # in favour of FFTPoissonSolver, no? self.gd = gd if self.gd.comm.size > 1: raise RuntimeError('Cannot do parallel FFT.') self.k2, self.N3 = construct_reciprocal(self.gd) if load_gauss: gauss = Gaussian(self.gd) self.rho_gauss = gauss.get_gauss(0) self.phi_gauss = gauss.get_gauss_pot(0)
def load_gauss(self, center=None): """Load compensating charge distribution for charged systems. See Appendix B of A. Held and M. Walter, J. Chem. Phys. 141, 174108 (2014). """ # XXX Check if update is needed (dielectric changed)? epsr, dx_epsr, dy_epsr, dz_epsr = self.dielectric.eps_gradeps gauss = Gaussian(self.gd, center=center) rho_g = gauss.get_gauss(0) phi_g = gauss.get_gauss_pot(0) x, y, z = gauss.xyz fac = 2. * np.sqrt(gauss.a) * np.exp(-gauss.a * gauss.r2) fac /= np.sqrt(np.pi) * gauss.r2 fac -= erf(np.sqrt(gauss.a) * gauss.r) / (gauss.r2 * gauss.r) fac *= 2.0 * 1.7724538509055159 dx_phi_g = fac * x dy_phi_g = fac * y dz_phi_g = fac * z sp = dx_phi_g * dx_epsr + dy_phi_g * dy_epsr + dz_phi_g * dz_epsr rho = epsr * rho_g - 1. / (4. * np.pi) * sp invnorm = np.sqrt(4. * np.pi) / self.gd.integrate(rho) self.phi_gauss = phi_g * invnorm self.rho_gauss = rho * invnorm
Nc = (N, N, N) # Number of grid points along each axis gd = GridDescriptor(Nc, (a, a, a), 0) # Grid-descriptor object solver = PoissonSolver(nn=3) # Numerical poisson solver solver.set_grid_descriptor(gd) solve = solver.solve xyz, r2 = coordinates(gd) # Matrix with the square of the radial coordinate print(r2.shape) r = np.sqrt(r2) # Matrix with the values of the radial coordinate nH = np.exp(-2 * r) / pi # Density of the hydrogen atom gauss = Gaussian(gd) # An instance of Gaussian # /------------------------------------------------\ # | Check if Gaussian densities are made correctly | # \------------------------------------------------/ for gL in range(2, 9): g = gauss.get_gauss(gL) # a gaussian of gL'th order print('\nGaussian of order', gL) for mL in range(9): m = gauss.get_moment(g, mL) # the mL'th moment of g print(' %s\'th moment = %2.6f' % (mL, m)) equal(m, gL == mL, 1e-4) # Check the moments of the constructed 1s density print('\nDensity of Hydrogen atom') for L in range(4): m = gauss.get_moment(nH, L) print(' %s\'th moment = %2.6f' % (L, m)) equal(m, (L == 0) / sqrt(4 * pi), 1.5e-3) # Check that it is removed correctly v = gauss.remove_moment(nH, 0)
def load_gauss(self): if not hasattr(self, 'rho_gauss'): gauss = Gaussian(self.gd) self.rho_gauss = gauss.get_gauss(0) self.phi_gauss = gauss.get_gauss_pot(0)
gd = GridDescriptor(Nc, (a, a, a), 0) # Grid-descriptor object solver = PoissonSolver(nn=3) # Numerical poisson solver solver.set_grid_descriptor(gd) solver.initialize() solve = solver.solve xyz, r2 = coordinates(gd) # Matrix with the square of the radial coordinate print(r2.shape) r = np.sqrt(r2) # Matrix with the values of the radial coordinate nH = np.exp(-2 * r) / pi # Density of the hydrogen atom gauss = Gaussian(gd) # An instance of Gaussian # /------------------------------------------------\ # | Check if Gaussian densities are made correctly | # \------------------------------------------------/ for gL in range(2, 9): g = gauss.get_gauss(gL) # a gaussian of gL'th order print("\nGaussian of order", gL) for mL in range(9): m = gauss.get_moment(g, mL) # the mL'th moment of g print(" %s'th moment = %2.6f" % (mL, m)) equal(m, gL == mL, 1e-4) # Check the moments of the constructed 1s density print("\nDensity of Hydrogen atom") for L in range(4): m = gauss.get_moment(nH, L) print(" %s'th moment = %2.6f" % (L, m)) equal(m, (L == 0) / sqrt(4 * pi), 1.5e-3) # Check that it is removed correctly v = gauss.remove_moment(nH, 0)
def load_gauss(self, center=None): if not hasattr(self, 'rho_gauss') or center is not None: gauss = Gaussian(self.gd, center=center) self.rho_gauss = gauss.get_gauss(0) self.phi_gauss = gauss.get_gauss_pot(0)
# solver = HelmholtzSolver(0.16) # Numerical poisson solver solver.set_grid_descriptor(gd) solver.initialize() xyz, r2 = coordinates(gd) # Matrix with the square of the radial coordinate gauss = Gaussian(gd, a=inv_width) # An instance of Gaussian test_screened_poisson = ScreenedPoissonGaussian(gd, a=inv_width) # /-------------------------------------------------\ # | Check if Gaussian potentials are made correctly | # \-------------------------------------------------/ # Array for storing the potential pot = gd.zeros(dtype=float, global_array=False) solver.load_gauss() vg = test_screened_poisson.get_phi(-coupling) # esp. for dampening # Get analytic functions ng = gauss.get_gauss(0) # vg = solver.phi_gauss # Solve potential numerically niter = solver.solve(pot, ng, charge=None, zero_initial_phi=True) # Determine residual # residual = norm(pot - vg) residual = gd.integrate((pot - vg)**2)**0.5 # print result print 'residual %s'%( residual) assert residual < 0.003 # mpirun -np 2 python gauss_func.py --gpaw-parallel --gpaw-debug
psolvers = (WeightedFDPoissonSolver, ADM12PoissonSolver, PolarizationPoissonSolver) # test neutral system with constant permittivity parprint('neutral, constant permittivity') epsinf = 80. eps = gd.zeros() eps.fill(epsinf) qs = (-1., 1.) shifts = (-1., 1.) rho = gd.zeros() phi_expected = gd.zeros() for q, shift in zip(qs, shifts): gauss_norm = q / np.sqrt(4 * np.pi) gauss = Gaussian(gd, center=(box / 2. + shift) * np.ones(3) / Bohr) rho += gauss_norm * gauss.get_gauss(0) phi_expected += gauss_norm * gauss.get_gauss_pot(0) / epsinf for ps in psolvers: phi = solve(ps, eps, rho) parprint(ps, np.abs(phi - phi_expected).max()) equal(phi, phi_expected, 1e-3) # test charged system with constant permittivity parprint('charged, constant permittivity') epsinf = 80. eps = gd.zeros() eps.fill(epsinf) q = -2. gauss_norm = q / np.sqrt(4 * np.pi) gauss = Gaussian(gd, center=(box / 2. + 1.) * np.ones(3) / Bohr)
Nc = (N, N, N) # Number of grid points along each axis gd = GridDescriptor(Nc, (a, a, a), 0) # Grid-descriptor object solver = PoissonSolver(nn=3, use_charge_center=True) solver.set_grid_descriptor(gd) solver.initialize() gauss = Gaussian(gd, a=inv_width, center=center_of_charge) test_poisson = Gaussian(gd, a=inv_width, center=center_of_charge) # /-------------------------------------------------\ # | Check if Gaussian potentials are made correctly | # \-------------------------------------------------/ # Array for storing the potential pot = gd.zeros(dtype=float, global_array=False) solver.load_gauss() vg = test_poisson.get_gauss_pot(0) # Get analytic functions ng = gauss.get_gauss(0) # vg = solver.phi_gauss # Solve potential numerically niter = solver.solve(pot, ng, charge=1.0, zero_initial_phi=False) # Determine residual # residual = norm(pot - vg) residual = gd.integrate((pot - vg)**2)**0.5 print('residual %s' % ( residual)) assert residual < 1e-5 # Better than 5.x # mpirun -np 2 python gauss_func.py --gpaw-parallel --gpaw-debug