@wudawufanfan
2016-12-28T07:05:51.000000Z
字数 6089
阅读 526
未分类
在此输入正文
from __future__ import print_functionimport numpy as npfrom scipy.integrate import simpsfrom scipy import fftpackimport warningsimport collectionsclass Schrod:def __init__(self, x, V, n_basis=20):"""Parameters----------x : array_like, floatLength-N array of evenly spaced spatial coordinatesV : array_like, floatLength-N array giving the potential at each xn_basis : intThe number of square-well basis states used in the calculation (default=20)"""# Set the inputsself.x = xself.V = Vself.n_basis = n_basis# Validate the inputsN = self.x.sizeassert N > 1assert self.x.shape == (N,)assert (np.diff(x) >= 0).all()V_shape = self.V.shapeV_shape_len = len(V_shape)assert V_shape_len == 1 or V_shape_len == 2if V_shape_len == 1:assert V_shape == (N,)elif V_shape_len == 2:assert V_shape[1] == Nassert isinstance(n_basis, int)assert n_basis > 0# Set the derived quantitiesself._N = Nself.dx = x[1] - x[0]self._x_min = x[0]self._x_max = x[-1]self.box_size = np.abs(self._x_max - self._x_min)self._x_center = self._x_min + self.box_size / 2.self.dk = 2 * np.pi / self.box_sizeself.k = -0.5 * (self._N-1) * self.dk + self.dk * np.arange(self._N)# Allocate memory for eigenvalues and eigenvectorsself.eigs = np.zeros(shape=(V_shape[0], n_basis))self.vecs = np.zeros(shape=(V_shape[0], n_basis, n_basis))def solve(self, verbose=False):if verbose:print("Calculating the Hamiltonian matrices...")Hs = self._H(verbose)if verbose:print("Diagonalizing the Hamiltonian matrices...")self.eigs, self.vecs = np.linalg.eigh(Hs, UPLO='L')def solve_to_tol(self, n_eig, tol=1e-6, n_init=5, n_max=50, n_step=5, err_type = "max_mean", verbose=False):"""Increase the basis size until the maximum change among the lowest<n_eig> eigenvalues is less than <tol>:param n_eig: The number of eigenvalues (lowest) to check for convergence.:param tol: The desired maximum relative error in the output:param n_init: The initial number of basis states. Must satisfy n_init >= n_eig:param n_max: The maximum number of basis states to include before stopping.:param n_step: The number of basis states added in each convergence test.:return: A tuple with the following information (A, B, C):A: Boolean. True if maximum measured relative error less than or equal to <tol>B: The number of basis states included when convergence reached.C: The measured relative error:param err_type: The type of error to calculate, one of"max": the maximum error is no greater than :param tol:"mean": the mean error is no greater than :param tol:"max_mean" (default): the maximum of the mean error per eigenvalue is no greater than :param tol::param verbose: bool, whether to print diagnostic information"""# The initial truncationself.set_n_basis(n_init)self.solve()eigs = self.eigs[..., 0:n_eig]err = 1# The maximum relative error in the lowst <n_eig> eigenvaluesmeasured_tol = 1 # the max rel. err. of the eigenvaluesn = n_initpassed = Falsewhile (not passed) and (n+n_step <= n_max):n += n_stepself.set_n_basis(n)self.solve()eigs_new = self.eigs[..., 0:n_eig]err = np.abs((eigs - eigs_new) / 0.5 / (eigs + eigs_new))if err_type is "max":measured_tol = np.max(err)elif err_type is "mean":measured_tol = np.mean(err)elif err_type is "max_mean":measured_tol = np.max(np.mean(err, axis=0))else:warnings.warn("Invalid <err_type>. Must be one of 'max', 'mean' or 'max_mean'. Assuming 'max_mean'.")measured_tol = np.max(np.mean(err, axis=0))eigs = eigs_newif verbose:print("Number of basis states: %i \n Measured error: %.2e" % (n, measured_tol))if measured_tol <= tol:passed = Trueif not passed:warnings.warn("Unable to achieve desired tolerance of %.2e.\n""Achieved a tolerance of %.2e.\n""Try increasing the maximum number of basis states." % (tol, measured_tol))solution = collections.namedtuple('Solution',['eig_errs', 'n_basis_converged', 'passed'])return solution(err, n, passed)def psi_eig_x(self):basis_vec = np.arange(1, self.vecs.shape[-1] + 1)return np.tensordot(self.vecs,self._psi0(basis_vec, self.x), axes=(-2,0))def prob_eig_x(self):return self.psi_eig_x() ** 2def psi_tx(self, psi_0_x, t_vec):# Caculate the overlap of the initial wavefunction with all of the eigenstatespsis = self.psi_eig_x()coeffs = simps(x=self.x, y=psi_0_x * psis, axis=-1)# Calculate the complex phases at each timephases = np.exp(-1j * np.outer(t_vec, self.eigs))# Calculate the wavefunction on the grid at each time slicepsi_of_t = np.dot(phases * coeffs, psis)print(psi_of_t.shape)return psi_of_tdef prob_tx(self, psi_0, t_vec):return np.absolute(self.psi_tx(psi_0, t_vec)) ** 2def psi_tk(self, psi_0, t_vec):psitx = self.psi_tx(psi_0,t_vec)psitk = fftpack.fft(psitx, overwrite_x=True)return fftpack.fftshift(psitk, axes=-1)def prob_tk(self, psi_0, t_vec):return np.absolute(self.psi_tk(psi_0, t_vec)) ** 2def expected_E(self, psi):integrand = -0.5 * np.conj(psi) * np.gradient(np.gradient(psi, self.dx, axis=-1), self.dx, axis=-1) + \np.absolute(psi)**2 * self.Vreturn np.real(simps(x=self.x, y=integrand, axis=-1))# Set functionsdef set_x(self, x):self.x = xself._x_min = x[0]self._x_max = x[-1]self.box_size = self._x_max = self._x_minself._x_center = self._x_min + self.box_size / 2.def set_V(self, V):self.V = Vdef set_n_basis(self, n_basis):self.n_basis = n_basis# Private functions:def _H(self, verbose=False):n_matels = self.n_basis * (self.n_basis + 1) / 2# initialize an empty hamiltonian(s)h=0if len(self.V.shape) is 2:h = np.zeros((self.V.shape[0], self.n_basis, self.n_basis))elif len(self.V.shape) is 1:h = np.zeros(( self.n_basis, self.n_basis))for m in range(self.n_basis):for n in range(m + 1):h[..., m, n] = self._Vmn(m, n)# Print a statusn_sofar = (m + 1) * m / 2 + n + 1percent = n_sofar / n_matels * 100if verbose:print("\r Status: %0.2f %% complete" % percent, end='')if verbose:print("")return h + np.diag(self._E0(np.arange(1, self.n_basis + 1)))def _psi0(self, n, x):"""Evaluate the nth box state at x:param n: array-like, 1-indexed state labels:param x: array-like, positions between -1 and 1:return: an array of shape (len(n), len(x))"""kn = n * np.pi / self.box_sizereturn np.sqrt(2 / self.box_size) * \np.sin(np.outer(kn, x - self._x_center + self.box_size / 2))def _E0(self, n):"""The nth energy level in the box:param n: the state label:return: the energy"""return n ** 2 * np.pi ** 2 / (2. * self.box_size ** 2)def _matel_integrand(self, m, n):"""The n,m matrix element of the V potential evaluated at the x coordinates:param n: the row index:param m: the column index:param x: array-like, a vector of x coordinates:param V: array-like, an array of potential values. The rows correspond tothe entries in x. The columns correspond to different potentials:return: the integrand of the matrix element"""return self._psi0(m + 1, self.x) * self._psi0(n + 1, self.x) * self.Vdef _Vmn(self, m, n):return simps(x=self.x, y=self._matel_integrand(m, n), axis=-1)if __name__ == "__main__":x = np.linspace(-1,1,10)V = 1/2 * x**2eqn = Schrod(x, V)print(eqn.k)