Example #1
1
    def do_molgrid(self):
        self.molgrid = Grid.from_prefix("molecule", self.work)
        if self.molgrid is not None:
            self.molgrid.weights = self.molgrid.load("weights")
        else:
            # we have to generate a new grid. The grid is constructed taking
            # into account the following considerations:
            # 1) Grid points within the cusp region are discarded
            # 2) The rest of the molecular and surrounding volume is sampled
            #    with spherical grids centered on the atoms. Around each atom,
            #    'scale_steps' of shells are placed with lebedev grid points
            #    (num_lebedev). The lebedev weights are used in the fit to
            #    avoid preferential directions within one shell.
            # 3) The radii of the shells start from scale_min*(cusp_radius+0.2)
            #    and go up to scale_max*(cusp_radius+0.2).
            # 4) Each shell will be randomly rotated around the atom to avoid
            #    global preferential directions in the grid.
            # 5) The default parameters for the grid should be sufficient for
            #    sane ESP fitting. The ESP cost function should discard points
            #    with a density larger than a threshold, i.e. 1e-5 a.u. A
            #    gradual transition between included and discarded points around
            #    this threshold will improve the quality of the fit.

            lebedev_xyz, lebedev_weights = get_lebedev_grid(50)
            self.do_noble_radii()

            scale_min = 1.5
            scale_max = 30.0
            scale_steps = 30
            scale_factor = (scale_max/scale_min)**(1.0/(scale_steps-1))
            scales = scale_min*scale_factor**numpy.arange(scale_steps)

            points = []
            weights = []
            pb = log.pb("Constructing molecular grid", scale_steps)
            for scale in scales:
                pb()
                radii = scale*self.noble_radii
                for i in xrange(self.molecule.size):
                    rot = Rotation.random()
                    for j in xrange(len(lebedev_xyz)):
                        my_point = radii[i]*numpy.dot(rot.r, lebedev_xyz[j]) + self.molecule.coordinates[i]
                        distances = numpy.sqrt(((self.molecule.coordinates - my_point)**2).sum(axis=1))
                        if (distances < scales[0]*self.noble_radii).any():
                            continue
                        points.append(my_point)
                        weights.append(lebedev_weights[j])
            pb()
            points = numpy.array(points)
            weights = numpy.array(weights)

            self.molgrid = Grid("molecule", self.work, points)
            self.molgrid.weights = weights
            self.molgrid.dump("weights", weights)
Example #2
0
    def do_atgrids_overlap_matrix(self):
        self.do_atgrids()
        self.do_atgrids_atweights()

        num_orbitals = self.wavefn.num_orbitals
        pb = log.pb("Computing matrices", self.molecule.size)
        for i in xrange(self.molecule.size):
            pb()
            atgrid = self.atgrids[i]
            suffix = "%s_overlap_matrix" % self.prefix
            overlap = atgrid.load(suffix)
            if overlap is None:
                rw = self.rgrid.get_weights().copy()
                rw *= 4*numpy.pi
                rw *= self.rgrid.rs
                rw *= self.rgrid.rs
                weights = numpy.outer(rw, self.agrid.lebedev_weights).ravel()
                weights *= atgrid.atweights
                overlap = self.wavefn.compute_atomic_overlap(atgrid, weights)
                atgrid.dump(suffix, overlap)
            else:
                overlap = overlap.reshape((num_orbitals, num_orbitals))
            atgrid.overlap_matrix = overlap
        pb()

        filename = "%s_overlap_matrices.txt" % self.prefix
        overlap_matrices = [atgrid.overlap_matrix for atgrid in self.atgrids]
        self.output.dump_overlap_matrices(filename, overlap_matrices)
Example #3
0
    def do_charges(self):
        charges_name = "%s_charges" % self.prefix
        populations_name = "%s_populations" % self.prefix
        self.charges = self.work.load(charges_name)
        self.populations = self.work.load(populations_name)

        if self.charges is None or self.populations is None:
            self.do_atgrids()
            self.do_atgrids_moldens()
            self.do_atgrids_atweights()

            pb = log.pb("Computing charges", self.molecule.size)
            self.populations = numpy.zeros(self.molecule.size, float)
            self.charges = numpy.zeros(self.molecule.size, float)
            for i in xrange(self.molecule.size):
                pb()
                w = self.atgrids[i].atweights
                d = self.atgrids[i].moldens
                center = self.molecule.coordinates[i]
                self.populations[i] = self._spherint(d*w)
                self.charges[i] = self.wavefn.nuclear_charges[i] - self.populations[i]
            pb()
            if self.context.options.fix_total_charge:
                self.charges -= (self.charges.sum() - self.wavefn.charge)/self.molecule.size
            self.work.dump(charges_name, self.charges)
            self.work.dump(populations_name, self.populations)

        self.output.dump_atom_scalars("%s_charges.txt" % self.prefix, self.charges, "Charge")
Example #4
0
 def do_atgrids_molspindens(self):
     self.do_atgrids()
     pb = log.pb("Computing/Loading spin densities", self.molecule.size)
     for i in xrange(self.molecule.size):
         pb()
         self.wavefn.compute_spin_density(self.atgrids[i])
     pb()
Example #5
0
    def do_dipoles(self):
        dipoles_name = "%s_dipoles" % self.prefix
        self.dipoles = self.work.load(dipoles_name, (-1,3))

        if self.dipoles is None:
            self.do_atgrids()
            self.do_atgrids_moldens()
            self.do_atgrids_atweights()

            pb = log.pb("Computing dipoles", self.molecule.size)
            self.dipoles = numpy.zeros((self.molecule.size,3), float)
            for i in xrange(self.molecule.size):
                pb()
                atgrid = self.atgrids[i]
                w = atgrid.atweights
                d = atgrid.moldens
                center = self.molecule.coordinates[i]

                for j in 0,1,2:
                    integrand = -(atgrid.points[:,j] - center[j])*d*w
                    self.dipoles[i,j] = self._spherint(integrand)
            pb()
            self.work.dump(dipoles_name, self.dipoles)

        self.output.dump_atom_vectors("%s_dipoles.txt" % self.prefix, self.dipoles, "Dipoles")
Example #6
0
 def do_atgrids_orbitals(self):
     self.do_atgrids()
     self.wavefn.init_naturals(self.work)
     pb = log.pb("Computing/Loading orbitals", self.molecule.size)
     for i in xrange(self.molecule.size):
         pb()
         self.wavefn.compute_orbitals(self.atgrids[i])
     pb()
Example #7
0
def make_density_profiles(program, num_lebedev, r_low, r_high, steps,
                          atom_numbers, max_ion, do_work, do_random):
    # generate lebedev grid
    lebedev_xyz, lebedev_weights = get_grid(num_lebedev)

    # define radii
    rgrid = RLogIntGrid(r_low, r_high, steps)
    agrid = ALebedevIntGrid(num_lebedev, do_random)

    f_pro = file("densities.txt", "w")
    print >> f_pro, rgrid.get_description()
    charges = []

    # run over all directories, run cubegen, load cube data
    pb = log.pb("Density profiles", len(atom_numbers) * (2 * max_ion + 1))
    for number in atom_numbers:
        symbol = periodic[number].symbol
        for charge in xrange(-max_ion, max_ion + 1):
            charge_label = charge_to_label(charge)
            pb()
            dirname = os.path.join("%03i%s" % (number, symbol), charge_label,
                                   "gs")
            # get the grid
            if not os.path.isdir(dirname): continue
            if do_work:
                work = Work(dirname)
            else:
                work = Work()
            grid = AtomicGrid.from_prefix("grid", work)
            if grid is None:
                center = numpy.zeros(3, float)
                grid = AtomicGrid.from_parameters("grid", work, center, rgrid,
                                                  agrid)
            # compute densities
            program.compute_density(grid, dirname)
            # this is spherical averaging, i.e. integral/(4*pi)
            radrhos = agrid.integrate(grid.moldens) / (4 * numpy.pi)
            print >> f_pro, "%3i %+2i" % (number, charge),
            # leave out near zeros to save space and time
            print >> f_pro, " ".join("%22.16e" % rho for rho in radrhos
                                     if rho > 1e-100)
            check = rgrid.integrate(4 * numpy.pi * rgrid.rs * rgrid.rs *
                                    radrhos)
            charges.append((number, symbol, charge, check))

    pb()
    f_pro.close()

    counter = 0
    for number, symbol, charge, real_charge in charges:
        log("Total charge error: %3i %2s %+2i    % 10.5e" %
            (number, symbol, charge, -real_charge + number - charge))
        counter += 1
Example #8
0
def make_inputs(program, lot, atom_numbers, max_ion):
    states = list(iter_states(atom_numbers, max_ion))
    pb = log.pb("Creating input files", len(states))
    dirnames = []

    for atom, charge, mult in states:
        pb()
        charge_label = charge_to_label(charge)
        dirname = os.path.join("%03i%s" % (atom.number, atom.symbol), charge_label, "mult%i" % mult)
        program.make_atom_input(dirname, atom.number, charge, mult, lot)
        dirnames.append(dirname)
    pb()
    return dirnames
Example #9
0
def make_inputs(program, lot, atom_numbers, max_ion):
    states = list(iter_states(atom_numbers, max_ion))
    pb = log.pb("Creating input files", len(states))
    dirnames = []

    for atom, charge, mult in states:
        pb()
        charge_label = charge_to_label(charge)
        dirname = os.path.join("%03i%s" % (atom.number, atom.symbol),
                               charge_label, "mult%i" % mult)
        program.make_atom_input(dirname, atom.number, charge, mult, lot)
        dirnames.append(dirname)
    pb()
    return dirnames
Example #10
0
    def do_atgrids_od_atweights(self):
        # od stands for off-diagonal
        self.do_atgrids_atweights()
        self._prepare_atweights()

        pb = log.pb("Computing off-diagonal atom weights", self.molecule.size**2)
        for i in xrange(self.molecule.size):
            atgrid = self.atgrids[i]
            atgrid.od_atweights = []
            for j in xrange(self.molecule.size):
                pb()
                w = self._compute_atweights(atgrid, j)
                atgrid.od_atweights.append(w)
        pb()
Example #11
0
def make_density_profiles(program, num_lebedev, r_low, r_high, steps, atom_numbers, max_ion, do_work, do_random):
    # generate lebedev grid
    lebedev_xyz, lebedev_weights = get_grid(num_lebedev)

    # define radii
    rgrid = RLogIntGrid(r_low, r_high, steps)
    agrid = ALebedevIntGrid(num_lebedev, do_random)

    f_pro = file("densities.txt", "w")
    print >> f_pro, rgrid.get_description()
    charges = []

    # run over all directories, run cubegen, load cube data
    pb = log.pb("Density profiles", len(atom_numbers)*(2*max_ion+1))
    for number in atom_numbers:
        symbol = periodic[number].symbol
        for charge in xrange(-max_ion, max_ion+1):
            charge_label = charge_to_label(charge)
            pb()
            dirname = os.path.join("%03i%s" % (number, symbol), charge_label, "gs")
            # get the grid
            if not os.path.isdir(dirname): continue
            if do_work:
                work = Work(dirname)
            else:
                work = Work()
            grid = AtomicGrid.from_prefix("grid", work)
            if grid is None:
                center = numpy.zeros(3,float)
                grid = AtomicGrid.from_parameters("grid", work, center, rgrid, agrid)
            # compute densities
            program.compute_density(grid, dirname)
            # this is spherical averaging, i.e. integral/(4*pi)
            radrhos = agrid.integrate(grid.moldens)/(4*numpy.pi)
            print >> f_pro, "%3i %+2i" % (number, charge),
            # leave out near zeros to save space and time
            print >> f_pro, " ".join("%22.16e" % rho for rho in radrhos if rho > 1e-100)
            check = rgrid.integrate(4*numpy.pi*rgrid.rs*rgrid.rs*radrhos)
            charges.append((number, symbol, charge, check))


    pb()
    f_pro.close()

    counter = 0
    for number, symbol, charge, real_charge in charges:
        log("Total charge error: %3i %2s %+2i    % 10.5e" % (number, symbol, charge, -real_charge+number-charge))
        counter += 1
Example #12
0
        def do_one_kind(kind):
            # first check for restricted
            orbitals = getattr(self.wavefn, "%s_orbitals" % kind)
            if kind!="alpha" and self.wavefn.alpha_orbitals is orbitals:
                # simply make references to alpha data and return
                log("Cloning alpha results (%s)" % kind)
                for i in xrange(self.molecule.size):
                    setattr(self.atgrids[i], "%s_overlap_matrix_orb" % kind, self.atgrids[i].alpha_overlap_matrix_orb)
                return

            # then try to load the matrices
            some_failed = False
            num_orbitals = self.wavefn.num_orbitals
            for i in xrange(self.molecule.size):
                matrix = self.atgrids[i].load("%s_%s_overlap_matrix_orb" % (self.prefix, kind))
                if matrix is None:
                    some_failed = True
                else:
                    matrix = matrix.reshape((num_orbitals, num_orbitals))
                setattr(self.atgrids[i], "%s_overlap_matrix_orb" % kind, matrix)

            if some_failed:
                self.do_atgrids_orbitals()
                self.do_atgrids_atweights()

                pb = log.pb("Computing atomic overlap matrices (%s)" % kind, self.molecule.size)
                for i in xrange(self.molecule.size):
                    pb()
                    if getattr(self.atgrids[i], "%s_overlap_matrix_orb" % kind) is None:
                        orbitals = getattr(self.atgrids[i], "%s_orbitals" % kind)
                        w = self.atgrids[i].atweights
                        matrix = numpy.zeros((num_orbitals,num_orbitals), float)
                        for j1 in xrange(num_orbitals):
                            for j2 in xrange(j1+1):
                                integrand = orbitals[j1]*orbitals[j2]*w
                                value = self._spherint(integrand)
                                matrix[j1,j2] = value
                                matrix[j2,j1] = value
                        setattr(self.atgrids[i], "%s_overlap_matrix_orb" % kind, matrix)
                        self.atgrids[i].dump("%s_%s_overlap_matrix_orb" % (self.prefix, kind), matrix)
                pb()

            filename = "%s_%s_overlap_matrices_orb.txt" % (self.prefix, kind)
            overlap_matrices = [
                getattr(grid, "%s_overlap_matrix_orb" % kind)
                for grid in self.atgrids
            ]
            self.output.dump_overlap_matrices(filename, overlap_matrices)
Example #13
0
def run_jobs(program, dirnames):
    pb = log.pb("Atomic computations", len(dirnames))
    failed = []
    for dirname in dirnames:
        pb()
        succes = program.run(dirname)
        if not succes:
            failed.append(dirname)
    pb()
    if len(failed) == len(dirnames):
        log("Could not execute any job. Is %s in the PATH?" % program.executable)
        sys.exit(-1)
    if len(failed) > 0:
        log("Some jobs failed:")
        for dirname in failed:
            log("  %s" % dirname)
Example #14
0
    def do_atgrids(self):
        self.atgrids = []
        pb = log.pb("Computing/Loading atomic grids and distances", self.molecule.size)
        for i in xrange(self.molecule.size):
            pb()
            name = "atom%05i" % i
            atgrid = AtomicGrid.from_prefix(name, self.work)
            if atgrid is None:
                center = self.molecule.coordinates[i]
                atgrid = AtomicGrid.from_parameters(name, self.work, center, self.rgrid, self.agrid)
            self.atgrids.append(atgrid)

            # Compute and store all the distances from these grid points to the
            # nuclei.
            atgrid.distances = LazyDistances(self.molecule.coordinates, atgrid, self.context.options.save_mem)
        pb()
Example #15
0
def run_jobs(program, dirnames):
    pb = log.pb("Atomic computations", len(dirnames))
    failed = []
    for dirname in dirnames:
        pb()
        succes = program.run(dirname)
        if not succes:
            failed.append(dirname)
    pb()
    if len(failed) == len(dirnames):
        log("Could not execute any job. Is %s in the PATH?" %
            program.executable)
        sys.exit(-1)
    if len(failed) > 0:
        log("Some jobs failed:")
        for dirname in failed:
            log("  %s" % dirname)
Example #16
0
    def do_net_overlap(self):
        net_overlap_name = "%s_net_overlap.bin" % self.prefix
        self.net_overlap = self.work.load(net_overlap_name, (self.molecule.size,self.molecule.size))

        if self.net_overlap is None:
            self.do_atgrids()
            self.do_atgrids_moldens()
            self.do_charges()
            self.do_atgrids_od_atweights()
            self.net_overlap = numpy.zeros((self.molecule.size, self.molecule.size))
            pb = log.pb("Integrating over products of stockholder weights", (self.molecule.size*(self.molecule.size+1))/2)
            for i in xrange(self.molecule.size):
                for j in xrange(i+1):
                    pb()
                    if i != j:
                        # Use Becke's integration scheme to split the integral
                        # over two atomic grids.
                        # 1) first part of the integral, using the grid on atom i
                        delta = (self.atgrids[i].distances[j].reshape((len(self.rgrid.rs),-1)) - self.rgrid.rs.reshape((-1,1))).ravel()
                        switch = delta/self.molecule.distance_matrix[i,j]
                        for k in xrange(3):
                            switch = (3 - switch**2)*switch/2
                        switch += 1
                        switch /= 2
                        integrand = switch*self.atgrids[i].od_atweights[j]*self.atgrids[i].atweights*self.atgrids[i].moldens
                        part1 = self._spherint(integrand)
                        # 2) second part of the integral
                        delta = (self.atgrids[j].distances[i].reshape((len(self.rgrid.rs),-1)) - self.rgrid.rs.reshape((-1,1))).ravel()
                        switch = delta/self.molecule.distance_matrix[i,j]
                        for k in xrange(3):
                            switch = (3 - switch**2)*switch/2
                        switch += 1
                        switch /= 2
                        integrand = switch*self.atgrids[j].od_atweights[i]*self.atgrids[j].atweights*self.atgrids[j].moldens
                        part2 = self._spherint(integrand)
                        # Add up and store
                        self.net_overlap[i,j] = part1 + part2
                        self.net_overlap[j,i] = part1 + part2
                    else:
                        integrand = self.atgrids[i].atweights**2*self.atgrids[i].moldens
                        self.net_overlap[i,i] = self._spherint(integrand)
            pb()
            self.work.dump(net_overlap_name, self.net_overlap)

        self.output.dump_atom_matrix("%s_net_overlap.txt" % self.prefix, self.net_overlap, "Net/Overlap")
Example #17
0
    def _prepare_atweights(self):
        # Compute the cell functions on all grids
        self.do_atgrids()
        radii = numpy.array([periodic[n].covalent_radius for n in self.molecule.numbers])
        N = self.molecule.size
        pb = log.pb("Computing/Loading cell functions", N)
        for i in xrange(N):
            pb()
            # working in the grid of atom i
            grid = self.atgrids[i]

            # first try to load. if it fails then compute.
            grid.cell_functions = grid.load("cell_functions")

            if grid.cell_functions is None:
                # load failed, so compute
                grid.cell_functions = numpy.ones((N, grid.size), float)
                for j0 in xrange(N):
                    for j1 in xrange(j0):
                        # working on the contribution from atom pair j0,j1
                        # determine the displacement of the cell boundary with
                        # respect to the center based on covalent radii
                        d = self.molecule.distance_matrix[j0,j1]
                        u = (radii[j0]-radii[j1])/(radii[j1]+radii[j0])
                        a = u/(u**2-1)
                        if a < -0.45: a = -0.45
                        elif a > 0.45: a = 0.45
                        # construct the switching function
                        switch = grid.distances[j0].copy()
                        switch -= grid.distances[j1]
                        switch /= d
                        switch = switch + a*(1-switch**2) # hetero
                        for k in xrange(self.k):
                            switch = 0.5*(3.0 - switch**2)*switch
                        switch += 1.0
                        switch /= 2.0
                        grid.cell_functions[j0] *= 1-switch
                        grid.cell_functions[j1] *= switch
                # dump cell functions
                grid.dump("cell_functions", grid.cell_functions)
            else:
                grid.cell_functions = grid.cell_functions.reshape((N, -1))
            grid.cell_sum = sum(grid.cell_functions)
        pb()
Example #18
0
    def do_spin_charges(self):
        spin_charges_name = "%s_spin_charges" % self.prefix
        self.spin_charges = self.work.load(spin_charges_name)

        if self.spin_charges is None:
            self.do_atgrids()
            self.do_atgrids_molspindens()
            self.do_atgrids_atweights()

            pb = log.pb("Computing spin charges", self.molecule.size)
            self.spin_charges = numpy.zeros(self.molecule.size, float)
            for i in xrange(self.molecule.size):
                pb()
                w = self.atgrids[i].atweights
                d = self.atgrids[i].molspindens
                center = self.molecule.coordinates[i]
                self.spin_charges[i] = self._spherint(d*w)
            pb()
            self.work.dump(spin_charges_name, self.spin_charges)

        self.output.dump_atom_scalars("%s_spin_charges.txt" % self.prefix, self.spin_charges, "Spin charge")
Example #19
0
    def do_bond_orders(self):
        # first try to load the results from the work dir
        bond_orders_name = "%s_bond_orders" % self.prefix
        valences_name = "%s_valences" % self.prefix
        self.bond_orders = self.work.load(bond_orders_name, (self.molecule.size, self.molecule.size))
        self.valences = self.work.load(valences_name)

        if self.bond_orders is None or self.valences is None:
            self.do_charges()
            self.do_atgrids_overlap_matrix()

            self.bond_orders = numpy.zeros((self.molecule.size, self.molecule.size))
            self.valences = numpy.zeros(self.molecule.size)
            num_dof = self.wavefn.num_orbitals

            full = numpy.zeros((num_dof, num_dof), float)
            dmat_to_full(self.wavefn.density_matrix, full)
            if self.wavefn.spin_density_matrix is None:
                full_alpha = 0.5*full
                full_beta = full_alpha
            else:
                full_alpha = numpy.zeros((num_dof, num_dof), float)
                full_beta = numpy.zeros((num_dof, num_dof), float)
                dmat_to_full(
                    0.5*(self.wavefn.density_matrix +
                    self.wavefn.spin_density_matrix), full_alpha
                )
                dmat_to_full(
                    0.5*(self.wavefn.density_matrix -
                    self.wavefn.spin_density_matrix), full_beta
                )

            pb = log.pb("Computing bond orders", (self.molecule.size*(self.molecule.size+1))/2)
            for i in xrange(self.molecule.size):
                for j in xrange(i+1):
                    pb()
                    if i==j:
                        # compute valence
                        tmp = numpy.dot(full, self.atgrids[i].overlap_matrix)
                        self.valences[i] = 2*self.populations[i] - (tmp*tmp.transpose()).sum()
                    else:
                        # compute bond order
                        bo = (
                            numpy.dot(full_alpha, self.atgrids[i].overlap_matrix)*
                            numpy.dot(full_alpha, self.atgrids[j].overlap_matrix).transpose()
                        ).sum()
                        if full_alpha is full_beta:
                            bo *= 2
                        else:
                            bo += (
                                numpy.dot(full_beta, self.atgrids[i].overlap_matrix)*
                                numpy.dot(full_beta, self.atgrids[j].overlap_matrix).transpose()
                            ).sum()
                        bo *= 2
                        self.bond_orders[i,j] = bo
                        self.bond_orders[j,i] = bo
            pb()
            self.work.dump(bond_orders_name, self.bond_orders)
            self.work.dump(valences_name, self.valences)
        self.free_valences = self.valences - self.bond_orders.sum(axis=1)

        self.output.dump_atom_matrix("%s_bond_orders.txt" % self.prefix, self.bond_orders, "Bond order")
        self.output.dump_atom_scalars("%s_valences.txt" % self.prefix, self.valences, "Valences")
        self.output.dump_atom_scalars("%s_free_valences.txt" % self.prefix, self.free_valences, "Free valences")
Example #20
0
    def do_multipoles(self):
        regular_solid_harmonics = [
            lambda x,y,z: 1.0, # (0,0)
            lambda x,y,z: z, # (1,0)
            lambda x,y,z: x, # (1,1+)
            lambda x,y,z: y, # (1,1-)
            lambda x,y,z: 1.0*z**2 - 0.5*x**2 - 0.5*y**2, # (2,0)
            lambda x,y,z: 1.7320508075688772935*x*z, # (2,1+)
            lambda x,y,z: 1.7320508075688772935*y*z, # (2,1-)
            lambda x,y,z: 0.86602540378443864676*x**2 - 0.86602540378443864676*y**2, # (2,2+)
            lambda x,y,z: 1.7320508075688772935*x*y, # (2,2-)
            lambda x,y,z: -1.5*z*x**2 - 1.5*z*y**2 + z**3, # (3,0)
            lambda x,y,z: 2.4494897427831780982*x*z**2 - 0.61237243569579452455*x*y**2 - 0.61237243569579452455*x**3, # (3,1+)
            lambda x,y,z: 2.4494897427831780982*y*z**2 - 0.61237243569579452455*y*x**2 - 0.61237243569579452455*y**3, # (3,1-)
            lambda x,y,z: 1.9364916731037084426*z*x**2 - 1.9364916731037084426*z*y**2, # (3,2+)
            lambda x,y,z: 3.8729833462074168852*x*y*z, # (3,2-)
            lambda x,y,z: -2.371708245126284499*x*y**2 + 0.790569415042094833*x**3, # (3,3+)
            lambda x,y,z: 2.371708245126284499*y*x**2 - 0.790569415042094833*y**3, # (3,3-)
            lambda x,y,z: 0.75*x**2*y**2 - 3.0*x**2*z**2 - 3.0*y**2*z**2 + z**4 + 0.375*x**4 + 0.375*y**4, # (4,0)
            lambda x,y,z: -2.371708245126284499*x*z*y**2 + 3.162277660168379332*x*z**3 - 2.371708245126284499*z*x**3, # (4,1+)
            lambda x,y,z: -2.371708245126284499*y*z*x**2 + 3.162277660168379332*y*z**3 - 2.371708245126284499*z*y**3, # (4,1-)
            lambda x,y,z: 3.3541019662496845446*x**2*z**2 - 3.3541019662496845446*y**2*z**2 + 0.5590169943749474241*y**4 - 0.5590169943749474241*x**4, # (4,2+)
            lambda x,y,z: 6.7082039324993690892*x*y*z**2 - 1.1180339887498948482*x*y**3 - 1.1180339887498948482*y*x**3, # (4,2-)
            lambda x,y,z: -6.2749501990055666098*x*z*y**2 + 2.0916500663351888699*z*x**3, # (4,3+)
            lambda x,y,z: 6.2749501990055666098*y*z*x**2 - 2.0916500663351888699*z*y**3, # (4,3-)
            lambda x,y,z: -4.4370598373247120319*x**2*y**2 + 0.73950997288745200532*x**4 + 0.73950997288745200532*y**4, # (4,4+)
            lambda x,y,z: 2.9580398915498080213*y*x**3 - 2.9580398915498080213*x*y**3, # (4,4-)
        ]
        labels = [
            '(0,0)', '(1,0)', '(1,1+)', '(1,1-)', '(2,0)', '(2,1+)', '(2,1-)',
            '(2,2+)', '(2,2-)', '(3,0)', '(3,1+)', '(3,1-)', '(3,2+)', '(3,2-)',
            '(3,3+)', '(3,3-)', '(4,0)', '(4,1+)', '(4,1-)', '(4,2+)', '(4,2-)',
            '(4,3+)', '(4,3-)', '(4,4+)', '(4,4-)'
        ]

        multipoles_name = "%s_multipoles.bin" % self.prefix
        num_polys = len(regular_solid_harmonics)
        shape = (self.molecule.size,num_polys)
        self.multipoles = self.work.load(multipoles_name, shape)

        if self.multipoles is None:
            self.do_atgrids()
            self.do_atgrids_moldens()
            self.do_atgrids_atweights()

            pb = log.pb("Computing multipoles", self.molecule.size)
            num_polys = len(regular_solid_harmonics)
            self.multipoles = numpy.zeros(shape, float)
            for i in xrange(self.molecule.size):
                pb()
                atgrid = self.atgrids[i]
                w = atgrid.atweights
                d = atgrid.moldens
                center = self.molecule.coordinates[i]

                cx = atgrid.points[:,0] - center[0]
                cy = atgrid.points[:,1] - center[1]
                cz = atgrid.points[:,2] - center[2]
                for j in xrange(num_polys):
                    poly = regular_solid_harmonics[j]
                    self.multipoles[i,j] = self._spherint(-poly(cx,cy,cz)*d*w)
                self.multipoles[i,0] += self.wavefn.nuclear_charges[i]
            pb()
            self.work.dump(multipoles_name, self.multipoles)

        self.output.dump_atom_fields("%s_multipoles.txt" % self.prefix, self.multipoles, labels, "Multipoles")