Ejemplo n.º 1
0
    def __init__(self, atomlist):
        # convert the list of atoms into a list of 3d points
        nat = len(atomlist)
        pos = XYZ.atomlist2vector(atomlist)
        points = []
        for i in range(0, nat):
            points.append(pos[3 * i:3 * (i + 1)])
        # QHull needs at least 4 point to construct the initial
        # simplex.
        if nat < 4:
            # If there are less than 4 atoms,
            # we add the center of mass as an additional point
            masses = AtomicData.atomlist2masses(atomlist)
            com = MolCo.center_of_mass(masses, pos)
            points.append(com)
        if nat < 3:
            # If there are less than 3 atoms we add
            # an arbitrary point on the x-axis
            points.append(com + np.array([0.0005, 0.0, 0.0]))
        if nat < 2:
            # If there is only one atom, we add another arbitrary
            # point on the y-axis
            points.append(com + np.array([0.0, 0.0005, 0.0]))
        # We add small random numbers to the input coordinates, so that
        # we get a 3D convex hull even if the molecule is planar
        points = np.array(points) + 0.0001 * np.random.rand(len(points), 3)
        # find the convex hull using the qhull code
        hull = ConvexHull(points, qhull_options="QbB Qt")

        # call the constructor of the parent class (MinimalEnclosingBox)
        super(MoleculeBox, self).__init__(hull)
Ejemplo n.º 2
0
def molecular_frame_transformation(atomlist):
    """
    The molecule is shifted to the center of mass and its principle axes of inertia are aligned
    with the coordinate axes. This standard orientation defines the molecular frame.
    The translation vector and Euler angles needed to transform the geometry from the
    molecular frame to the original frame are also returned.

    Returns:
    ========
    atomlist_std: molecular geometry in standard orientation
    (a,b,g): Euler angles in z-y-z convention
    cm: 3D vector with center of mass
    """
    pos = XYZ.atomlist2vector(atomlist)
    masses = AtomicData.atomlist2masses(atomlist)
    # shift center of mass to origin
    Nat = len(atomlist)
    pos_std = np.zeros(pos.shape)
    cm = center_of_mass(masses, pos)
    for i in range(0, Nat):
        pos_std[3*i:3*i+3] = pos[3*i:3*i+3] - cm
    (a,b,g) = euler_angles_inertia(masses, pos_std)
    R = EulerAngles2Rotation(a,b,g)
    for i in range(0, Nat):
        pos_std[3*i:3*i+3] = np.dot(R, pos_std[3*i:3*i+3])
    atomlist_std = XYZ.vector2atomlist(pos_std, atomlist)
    # The MolecularCoord module uses a strange convention for Euler angles.
    # Therefore we extract the Euler angles in z-y-z convention directly
    # from the rotation matrix, that rotates the molecule from the standard
    # orientation into the original orientation
    Rinv = R.transpose()
    a,b,g = rotation2EulerAngles(Rinv, convention="z-y-z")
    return atomlist_std, (a,b,g), cm
Ejemplo n.º 3
0
def partition_kinetic_energy(atomlists, dt):
    Nt = len(atomlists)
    # determine the indeces of the molecular fragments at the first
    # time-step.
    fragments_graph = MolecularGraph.atomlist2graph(atomlists[0])
    fragments_indeces = [
        MolecularGraph.graph2indeces(g) for g in fragments_graph
    ]
    fragment_atomlists = [[[atomlist[ii] for ii in I]
                           for atomlist in atomlists]
                          for I in fragments_indeces]
    # compute vibrational and center of mass kinetic energy for each fragment
    Nfrag = len(fragment_atomlists)
    ekin_com = np.zeros((Nt, Nfrag))
    ekin_tot = np.zeros((Nt, Nfrag))
    for f in range(0, Nfrag):
        masses = AtomicData.atomlist2masses(fragment_atomlists[f][0])
        print((fragment_atomlists[f][0]))
        # total mass
        M = np.sum(masses) / 3.0
        positions, velocities = velocities_finite_diff(fragment_atomlists[f],
                                                       dt)
        for i in range(0, Nt):
            vm = velocities[i] * masses
            vel_com = np.array(
                [np.sum(vm[0::3]),
                 np.sum(vm[1::3]),
                 np.sum(vm[2::3])]) / M
            ekin_com_f = 1.0 / 2.0 * M * np.sum(vel_com**2)
            ekin_tot_f = 1.0 / 2.0 * np.sum(masses * velocities[i]**2)

            ekin_com[i, f] += ekin_com_f
            ekin_tot[i, f] += ekin_tot_f
    return ekin_com, ekin_tot
Ejemplo n.º 4
0
def wigner_from_G09_hessian(g09_file, Nsample=100, zero_threshold=1.0e-9):
    """
    create Wigner ensemble based on hessian matrix from Gaussian 09 calculation
    """
    suffix = g09_file.split(".")[-1]
    if suffix in ["out", "log"]:
        print("Reading Gaussian 09 log file %s" % g09_file)
        atomlist = Gaussian.read_geometry(g09_file)
        forces = Gaussian.read_forces(g09_file)
        hess = Gaussian.read_force_constants(g09_file)
    elif suffix in ["fchk"]:
        print("Reading formatted Gaussian 09 checkpoint file %s" % g09_file)
        Data = Checkpoint.parseCheckpointFile(g09_file)
        # cartesian coordinates
        pos = Data["_Current_cartesian_coordinates"]
        atnos = Data["_Atomic_numbers"]
        # forces
        frc = -Data["_Cartesian_Gradient"]
        atomlist = []
        forces = []
        for i, Zi in enumerate(atnos):
            atomlist.append((Zi, tuple(pos[3 * i:3 * (i + 1)])))
            forces.append((Zi, tuple(frc[3 * i:3 * (i + 1)])))
        # Hessian
        hess = Data["_Cartesian_Force_Constants"]

    masses = np.array(AtomicData.atomlist2masses(atomlist))

    x0 = XYZ.atomlist2vector(atomlist)
    x0 = shift_to_com(x0, masses)

    grad = -XYZ.atomlist2vector(forces)

    grad_nrm = la.norm(grad)
    print("  gradient norm = %s" % grad_nrm)
    #    assert grad_nrm < 1.0e-3, "Gradient norm too large for minimum!"
    vib_freq, vib_modes = vibrational_analysis(hess,
                                               masses,
                                               zero_threshold=zero_threshold)
    Aw, Bw = wigner_distribution(x0,
                                 hess,
                                 masses,
                                 zero_threshold=zero_threshold)
    gw = GaussianWavepacket.Gaussian(Aw, Bw)
    qs, ps = gw.sample(Nsample)
    mx = np.outer(masses, np.ones(Nsample)) * qs
    avg_com = np.mean(np.sum(mx[::3, :], axis=0)), np.mean(
        np.sum(mx[1::3, :], axis=0)), np.mean(np.sum(mx[2::3, :], axis=0))
    print(avg_com)

    geometries = [
        XYZ.vector2atomlist(qs[:, i], atomlist) for i in range(0, Nsample)
    ]

    return geometries
 def __init__(self,
              symbols,
              coordinates,
              tstep,
              nstates,
              charge,
              sc_threshold=0.001):
     # build list of atoms
     atomlist = []
     for s, xyz in zip(symbols, coordinates):
         Z = AtomicData.atomic_number(s)
         atomlist.append((Z, xyz))
     self.dt_nuc = tstep  # nuclear time step in a.u.
     self.nstates = nstates  # number of electronic states including the ground state
     self.sc_threshold = sc_threshold  # threshold for coefficients that are included in the
     # computation of the scalar coupling
     self.Nat = len(atomlist)
     self.masses = AtomicData.atomlist2masses(atomlist)
     self.pes = PotentialEnergySurfaces(atomlist, nstates, charge=charge)
     # save results from last step
     self.last_step = None
Ejemplo n.º 6
0
def get_geometry(mol):
    """
    extract atomlist from block with title 'molecule'
    """
    atoms = mol.get("geometry")
    atomlist = []
    for atom in atoms:
        atname = atom.get("atom")
        atnumber = AtomicData.atomic_number(str(atname))
        coords = atom.get("xyz")
        if mol.get("units", "Angstrom") == "Angstrom":
            coords = [xyz/AtomicData.bohr_to_angs for xyz in coords]
        atomlist.append( (atnumber, coords) )

    return atomlist
Ejemplo n.º 7
0
def dynamics_in_format(atomlist, q, p, fname):
    atomlist_q = XYZ.vector2atomlist(q, atomlist)
    masses = AtomicData.atomlist2masses(atomlist)
    Nat = len(atomlist_q)
    txt = "%d\n" % Nat
    # positions in bohr
    for Zi, (x, y, z) in atomlist_q:
        atname = AtomicData.atom_names[Zi - 1]
        txt += "%4s    %+10.15f    %+10.15f    %+10.15f\n" % (atname, x, y, z)
    # velocities in bohr/s
    vel = p / masses
    for i in range(0, Nat):
        vx, vy, vz = vel[3 * i:3 * (i + 1)]
        txt += "   %+10.15f    %+10.15f   %+10.15f\n" % (vx, vy, vz)
    return txt
Ejemplo n.º 8
0
def load_hotbit_pseudoatom(elmfile):
    """
    load description of pseudo atom from a hotbit element (.elm) file

    Parameters:
    ===========
    elmfile: path to element description

    Returns:
    ========
    atom module with data stored as members
    """
    Data = parseHotbitParameters(elmfile)

    class Atom:
        pass

    atom = Atom()
    atom.Z = AtomicData.atomic_number(Data["atom_name"])
    atom.energies = []
    atom.valence_orbitals = []
    atom.nshell = []
    atom.angular_momenta = []
    atom.orbital_occupation = []
    atom.r = None
    atom.radial_wavefunctions = []
    spectr2l = {"s": 0, "p": 1, "d": 2, "f": 3, "g": 4, "h": 5, "i": 6}
    for k in Data.keys():
        if "orbital_energy" in k:
            atom.energies.append(float(Data[k]))
            dummy, spectr = k.rsplit("_", 1)
            n = int(spectr[0])
            l = int(spectr2l[spectr[1]])
            occnr = AtomicData.valence_occ_numbers[Data["atom_name"]]
            if occnr.has_key(spectr):
                atom.orbital_occupation.append(occnr[spectr])
            else:
                atom.orbital_occupation.append(0)
            atom.valence_orbitals.append(len(atom.valence_orbitals))
            atom.nshell.append(n)
            atom.angular_momenta.append(l)
            if Data.has_key("radial_wavefunction_%s" % spectr):
                atom.r = Data["radial_wavefunction_%s" % spectr][:, 0]
                atom.radial_wavefunctions.append(
                    Data["radial_wavefunction_%s" % spectr][:, 1])
            atom.hubbard_U = Data["hubbard_U"]
    return atom
Ejemplo n.º 9
0
def cut_sphere(atomlist, R=20.0):
    """
    remove all atoms outside sphere

    Parameters:
    ===========
    atomlist
    R: radius of sphere in bohr

    Returns:
    ========
    atomlist with atoms inside sphere
    """
    # shift to center of mass
    masses = AtomicData.atomlist2masses(atomlist)
    pos = XYZ.atomlist2vector(atomlist)
    pos_shifted = MolecularCoords.shift_to_com(pos, masses)
    atomlist = XYZ.vector2atomlist(pos_shifted, atomlist)

    Con = connectivity(atomlist)
    print("recursively remove connected atoms...")
    removed = [] # list of removed indeces
    for i,(Zi,posi) in enumerate(atomlist):
        print("i = %s" % i)
        if la.norm(posi) > R:
            if not (i in removed):
                print("remove %s%d" % (AtomicData.atom_names[Zi-1], i))
                removed.append(i)
                # remove connect atoms
                connected = find_connected(Con, i)
                print("and connected atoms %s" % connected)
                removed += connected
    removed = set(removed)
    cut_atomlist = []
    for i,(Zi,posi) in enumerate(atomlist):
        if i in removed:
            pass
        else:
            cut_atomlist.append( (Zi, posi) )
    return cut_atomlist
Ejemplo n.º 10
0
 def process(self, l):
     # Which kind of information should be read?
     l = l.strip()
     if self.read == None:
         if l == "coordinates":
             self.read = "C"
             return
     elif self.read == "C":
         if l == "velocities":
             self.read = "V"
             return
     # Read coordinates or velocities line by line
     if self.read == "C":
         atom, X, Y, Z = l.split()
         Zi = AtomicData.atomic_number(atom)
         # positions in bohr
         pos = float(X), float(Y), float(Z)
         self.atomlist.append((Zi, pos))
     elif self.read == "V":
         vx, vy, vz = l.split()
         vel = float(vx), float(vy), float(vz)
         self.velocities.append(vel)
Ejemplo n.º 11
0
def qmf3_in_format(atomlist, q, p, fname):
    """
    Save initial conditions in the format expected by Matthias' QMF3 program
    """
    atomlist_q = XYZ.vector2atomlist(q, atomlist)
    masses = AtomicData.atomlist2masses(atomlist)
    Nat = len(atomlist_q)
    txt = "$coordinates [\n"
    # positions in bohr
    for Zi, (x, y, z) in atomlist_q:
        atname = AtomicData.atom_names[Zi - 1]
        txt += "%4s    %+10.15f    %+10.15f    %+10.15f\n" % (atname, x, y, z)
    txt += "]\n"
    # velocities in bohr/s
    vel = p / masses
    txt += "$velocities [\n"
    for i in range(0, Nat):
        vx, vy, vz = vel[3 * i:3 * (i + 1)]
        txt += "   %+10.15f    %+10.15f   %+10.15f\n" % (vx, vy, vz)
    txt += "]\n"
    txt += "$end\n"
    return txt
Ejemplo n.º 12
0
    xyz_file = args[0]
    # ... Hessian of energy
    hessian_file = args[1]
    # output file
    state_file = args[2]

    # load minimum geometry and Hessian
    atomlist = XYZ.read_xyz(xyz_file)[-1]
    hess = np.loadtxt(hessian_file)

    print "optimized geometry read from '%s'" % xyz_file
    print "Hessian read from '%s'" % hessian_file

    # compute normal modes and frequencies
    xopt = XYZ.atomlist2vector(atomlist)
    masses = AtomicData.atomlist2masses(atomlist)
    # setting zero_threshold to -0.1 makes sure that all frequencies are returned
    # even if some of them are imaginary
    vib_freq, vib_modes = HarmonicApproximation.vibrational_analysis(xopt, hess, masses, \
                                                                     zero_threshold=-0.1, is_molecule=True)

    # sort frequencies in ascending order
    vib_freq = vib_freq.real
    sort_index = np.argsort(abs(vib_freq)**2)
    vib_freq = vib_freq[sort_index]
    vib_modes = vib_modes[:, sort_index]

    # remove lowest 6 vibrations (3 translation + 3 rotation) assuming the molecule
    # is not linear
    nat = len(atomlist)
    nvib = 3 * nat - 6
Ejemplo n.º 13
0
    def minimize(self):
        I = self.state

        # convert geometry to a vector
        x0 = XYZ.atomlist2vector(self.atomlist)

        # This member variable holds the last energy of the state
        # of interest.
        self.enI = 0.0
        # last available energies of all electronic states that were
        # calculated
        self.energies = None

        # FIND ENERGY MINIMUM
        # f is the objective function that should be minimized
        # it returns (f(x), f'(x))
        def f_cart(x):
            #
            if I == 0 and type(self.pes.tddftb.XmY) != type(None):
                # Only ground state is needed. However, at the start
                # a single TD-DFT calculation is performed to initialize
                # all variables (e.g. X-Y), so that the program does not
                # complain about non-existing variables.
                enI, gradI = self.pes.getEnergyAndGradient_S0(x)
                energies = np.array([enI])
            else:
                energies, gradI = self.pes.getEnergiesAndGradient(x, I)
                enI = energies[I]
            self.enI = enI
            self.energies = energies
            print("E = %2.7f     |grad| = %2.7f" % (enI, la.norm(gradI)))
            #
            # also save geometries from line searches
            save_xyz(x)

            return enI, gradI

        print("Intermediate geometries will be written to %s" % self.xyz_opt)

        # This is a callback function that is executed for each optimization step.
        # It appends the current geometry to an xyz-file.
        def save_xyz(x, mode="a"):
            self.atomlist = XYZ.vector2atomlist(x, self.atomlist)
            XYZ.write_xyz(self.xyz_opt, [self.atomlist], \
                          title="charge=%s energy= %s" % (self.geom_kwds.get("charge",0), self.enI),\
                          mode=mode)
            return x

        Nat = len(self.atomlist)

        if self.coord_system == "cartesian":
            print(
                "optimization is performed directly in cartesian coordinates")
            q0 = x0
            objective_func = f_cart
            save_geometry = save_xyz
            max_steplen = None
        elif self.coord_system == "internal":
            print(
                "optimization is performed in redundant internal coordinates")
            # transform cartesian to internal coordinates, x0 ~ q0
            q0 = self.IC.cartesian2internal(x0)

            # define functions that wrap the cartesian<->internal transformations
            def objective_func(q):
                # transform back from internal to cartesian coordinates
                x = self.IC.internal2cartesian(q)
                self.IC.cartesian2internal(x)
                # compute energy and gradient in cartesian coordinates
                en, grad_cart = f_cart(x)
                # transform gradient to internal coordinates
                grad = self.IC.transform_gradient(x, grad_cart)

                return en, grad

            def save_geometry(q, **kwds):
                # transform back from internal to cartesian coordinates
                x = self.IC.internal2cartesian(q)
                # save cartesian coordinates
                save_xyz(x, **kwds)
                return x

            def max_steplen(q0, v):
                """
                find a step size `a` such that the internal->cartesian
                transformation converges for the point q = q0+a*v
                """
                a = 1.0
                for i in range(0, 7):
                    q = q0 + a * v
                    try:
                        x = self.IC.internal2cartesian(q)
                    except NotConvergedError as e:
                        # reduce step size by factor of 1/2
                        a /= 2.0
                        continue
                    break
                else:
                    raise RuntimeError(
                        "Could not find a step size for which the transformation from internal to cartesian coordinates would work for q=q0+a*v! Last step size a= %e  |v|= %e  |a*v|= %e"
                        % (a, la.norm(v), la.norm(a * v)))
                return a

        else:
            raise ValueError("Unknown coordinate system '%s'!" %
                             self.coord_system)
        # save initial energy and geometry
        objective_func(q0)
        save_geometry(q0, mode="w")

        options = {
            'gtol': self.grad_tol,
            'maxiter': self.maxiter,
            'gtol': self.grad_tol,
            'norm': 2
        }
        if self.method == 'CG':
            # The "BFGS" method is probably better than "CG", but the line search in BFGS is expensive.
            res = optimize.minimize(objective_func,
                                    q0,
                                    method="CG",
                                    jac=True,
                                    callback=save_geometry,
                                    options=options)
            #res = optimize.minimize(objective_func, q0, method="BFGS", jac=True, callback=save_geometry, options=options)

        elif self.method in ['Steepest Descent', 'Newton', 'BFGS']:
            # My own implementation of optimization algorithms
            res = minimize(
                objective_func,
                q0,
                method=self.method,
                #line_search_method="largest",
                callback=save_geometry,
                max_steplen=max_steplen,
                maxiter=self.maxiter,
                gtol=self.grad_tol,
                ftol=self.func_tol)
        else:
            raise ValueError("Unknown optimization algorithm '%s'!" %
                             self.method)

        # save optimized geometry
        qopt = res.x
        Eopt = res.fun
        xopt = save_geometry(qopt)
        print("Optimized geometry written to %s" % self.xyz_opt)

        if self.calc_hessian == 1:
            # COMPUTE HESSIAN AND VIBRATIONAL MODES
            # The hessian is calculated by numerical differentiation of the
            # analytical cartesian gradients
            def grad(x):
                en, grad_cart = f_cart(x)
                return grad_cart

            print("Computing Hessian")
            hess = HarmonicApproximation.numerical_hessian_G(grad, xopt)
            np.savetxt("hessian.dat", hess)
            masses = AtomicData.atomlist2masses(atomlist)
            vib_freq, vib_modes = HarmonicApproximation.vibrational_analysis(xopt, hess, masses, \
                                                                             zero_threshold=1.0e-9, is_molecule=True)
            # compute thermodynamic quantities and write summary
            thermo = Thermochemistry.Thermochemistry(
                atomlist, Eopt, vib_freq,
                self.pes.tddftb.dftb2.getSymmetryGroup())
            thermo.calculate()

            # write vibrational modes to molden file
            molden = MoldenExporterSectioned(self.pes.tddftb.dftb2)
            atomlist_opt = XYZ.vector2atomlist(xopt, atomlist)
            molden.addVibrations(atomlist_opt, vib_freq.real,
                                 vib_modes.transpose())
            molden.export("vib.molden")

        ## It's better to use the script initial_conditions.py for sampling from the Wigner
        ## distribution
        """
Ejemplo n.º 14
0
    (opts, args) = parser.parse_args()
    if len(args) < 2:
        print usage
        exit(-1)

    xyz_file = args[0]
    hess_file = args[1]
    # should be options

    # optimized geometry
    atomlist = XYZ.read_xyz(xyz_file)[-1]
    xopt = XYZ.atomlist2vector(atomlist)
    # load hessian
    hess = np.loadtxt(hess_file)

    masses = AtomicData.atomlist2masses(atomlist)
    vib_freq, vib_modes = HarmonicApproximation.vibrational_analysis(xopt, hess, masses, \
                                 zero_threshold=opts.zero_threshold, is_molecule=True)

    # SAMPLE INITIAL CONDITIONS FROM WIGNER DISTRIBUTION
    qs, ps = HarmonicApproximation.initial_conditions_wigner(
        xopt,
        hess,
        masses,
        Nsample=opts.Nsample,
        zero_threshold=opts.zero_threshold)
    # make hydrogens slower
    for i in range(0, opts.Nsample):
        for A, (Z, pos) in enumerate(atomlist):
            if Z == 1:
                ps[3 * A:3 * (A + 1), i] *= 0.001
Ejemplo n.º 15
0
def read_slakoformat_par(parfile, atom_order="AB"):
    """
    read table for S and H in the format used by hotbit (https://trac.cc.jyu.fi/projects/hotbit)

    Paramters:
    ==========
    parfile: path to <atom1>_<atom2>.par

    Returns:
    ========
    sk

    The Slater Koster data is stored in attributes of sk:
    sk.Z1, sk.Z2, sk.d, sk.S, sk.H    
    """

    from os.path import basename
    at1, at2 = basename(parfile).replace(".par", "").split("_")
    if atom_order == "BA":
        tmp = at1
        at1 = at2
        at2 = tmp
    data_blocks = parseHotbitParameters(parfile)
    data_AB = data_blocks["slako_integrals_%s_%s" % (at1, at2)]
    data_BA = data_blocks["slako_integrals_%s_%s" % (at2, at1)]
    m, n = data_AB.shape
    assert data_AB.shape == data_BA.shape

    d = data_AB[:, 0]
    assert np.all(data_AB[:, 0] == data_BA[:, 0])

    sk = SlakoModule()
    sk.Z1 = AtomicData.atomic_number(at1)
    sk.Z2 = AtomicData.atomic_number(at2)
    sk.d = d
    sk.S = {}
    sk.H = {}

    S = sk.S
    H = sk.H
    for pos, (tausym_AB, tausym_BA) in enumerate(
            zip(T.tausymbols_AB[-n:], T.tausymbols_BA[-n:])):
        try:
            tau_AB = T.symbol2tau[tausym_AB]
            tau_BA = T.symbol2tau[tausym_BA]
        except KeyError:
            continue
        # AB
        l1, l2 = tau_AB[0], tau_AB[2]

        H[(l1, l2, T.tau2index[tau_AB])] = data_AB[:, pos + 1]
        S[(l1, l2,
           T.tau2index[tau_AB])] = data_AB[:,
                                           len(T.tausymbols_AB) + pos + 1]
        # BA
        l1, l2 = tau_BA[0], tau_BA[2]

        ##        if sk.Z1 == sk.Z2: # I think this is the right condition
        if sk.Z1 > 1 and sk.Z2 > 1:  # but with this illogical condition I can reproduce Roland's results for HCNO compounds
            orbital_parity = pow(-1, l1 + l2)
        else:
            orbital_parity = 1
        H[(l1, l2, T.tau2index[tau_BA])] = orbital_parity * data_BA[:, pos + 1]
        S[(l1, l2, T.tau2index[tau_BA]
           )] = orbital_parity * data_BA[:, len(T.tausymbols_BA) + pos + 1]

    return sk
Ejemplo n.º 16
0
def read_slakoformat_skf(skfile, sk=None, atom_order="AB"):
    """
    read table for S and H in the format used by DFTB+ 
    see http://www.dftb.org/fileadmin/DFTB/public/misc/slakoformat.pdf

    Parameters:
    ===========
    skfile: path to <atom 1>-<atom 2>.skf
    sk: slako_module loaded from a different file, to which the data from current table
      is appended.
    atom_order: Which of the atoms in the SK table is to be treated as the first atom?
      "AB": The first orbital belongs to the first atom
      "BA": The first orbital belongs to the second atom

    
    Returns:
    ========
    sk

    The Slater Koster data is stored in attributes of sk:
    sk.Z1, sk.Z2, sk.d, sk.S, sk.H    
    """
    from os.path import basename
    at1, at2 = basename(skfile).replace(".skf", "").split("-")
    fh = open(skfile, "r")
    # line 1
    parts = process_slako_line(fh.readline())
    gridDist, nGridPoints = float(parts[0]), int(parts[1])
    if at1 == at2:
        # line 2
        # homonuclear case
        Ed, Ep, Es, SPE, Ud, Up, Us, fd, fp, fs = map(
            float, process_slako_line(fh.readline()))
    # line 2 or 3
    mass, c2, c3, c4, c5, c6, c7, c8, c9, rcut, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10 = \
        map(float, process_slako_line(fh.readline()))

    d = linspace(0.0, gridDist * (nGridPoints - 1), nGridPoints)
    if sk == None:
        sk = SlakoModule()
        sk.Z1 = AtomicData.atomic_number(at1)
        sk.Z2 = AtomicData.atomic_number(at2)
        sk.d = d
        sk.S = {}
        sk.H = {}
    S = sk.S
    H = sk.H

    if atom_order == "BA":
        tausymbols = T.tausymbols_BA
    else:
        tausymbols = T.tausymbols_AB

    lines = fh.readlines()
    parts = process_slako_line(lines[0])
    n = len(parts) / 2
    assert n == 10  # for orbitals up to d functions there are 10 Slater Koster integrals
    for tausym in tausymbols[-n:]:
        try:
            tau = T.symbol2tau[tausym]
        except KeyError:
            continue


#        print "found %s integrals" % (T.tau2symbol[tau])
        H[(tau[0], tau[2], T.tau2index[tau])] = array(
            [0.0 for i in range(0, int(nGridPoints))])
        S[(tau[0], tau[2], T.tau2index[tau])] = array(
            [0.0 for i in range(0, int(nGridPoints))])
    # line 4 to (4 + nGridPoints -1)
    for i in range(0, nGridPoints):
        parts = process_slako_line(lines[i])
        #Hdd0 Hdd1 Hdd2 Hpd0 Hpd1 Hpp0 Hpp1 Hsd0 Hsp0 Hss0 Sdd0 Sdd1 Sdd2 Spd0 Spd1 Spp0 Spp1 Ssd0 Ssp0 Sss0
        for pos, tausym in enumerate(tausymbols[-n:]):
            try:
                tau = T.symbol2tau[tausym]
            except KeyError:
                continue
            l1, l2 = tau[0], tau[2]
            if atom_order == "BA":
                orbital_parity = pow(-1, l1 + l2)
            else:
                orbital_parity = 1

            H[(l1, l2,
               T.tau2index[tau])][i] = orbital_parity * float(parts[pos])
            S[(l1, l2, T.tau2index[tau])][i] = orbital_parity * float(
                parts[len(tausymbols) + pos])

    return sk
Ejemplo n.º 17
0
    def __init__(self, atomlist, freeze=[], explicit_bonds=[], verbose=0):
        """
        setup system of internal coordinates using
        valence bonds, angles and dihedrals

        Parameters
        ----------
        atomlist   :  list of tuples (Z,[x,y,z]) with molecular
                      geometry, connectivity defines the valence
                      coordinates

        Optional
        --------
        freeze          :  list of tuples of atom indices (starting at 0) corresponding
                           to internal coordinates that should be frozen
        explicit_bonds :   list of pairs of atom indices (starting at 0) between which artificial
                           bonds should be inserted, i.e. [(0,1), (10,20)].
                           This allows to connect separate fragments.
        verbose        :   write out additional information if > 0
        """
        self.verbose = verbose
        self.atomlist = atomlist

        self.masses = AtomicData.atomlist2masses(self.atomlist)
        # Bonds, angles and torsions are constructed by the force field.
        # Atom types, partial charges and lattice vectors
        # all don't matter, so we assign atom type 6 (C_R, carbon in resonance)
        # to all atoms.
        atomtypes = [6 for atom in atomlist]
        partial_charges = [0.0 for atom in atomlist]
        lattice_vectors = [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]
        # But since the covalent radii are wrong, we have to provide
        # the connectivity matrix
        conmat = XYZ.connectivity_matrix(atomlist, hydrogen_bonds=True)

        # insert artificial bonds
        for (I, J) in explicit_bonds:
            print("explicit bond between atoms %d-%d" % (I + 1, J + 1))
            conmat[I, J] = 1

        # Internal coordinates only work if the molecule does not
        # contain disconnected fragments, since there is no way how the
        # interfragment distance could be expressed in terms of internal coordinates.
        # We need to check that there is only a single fragment.
        fragment_graphs = MolecularGraph.atomlist2graph(self.atomlist,
                                                        conmat=conmat)
        nr_fragments = len(fragment_graphs)
        error_msg = "The molecule consists of %d disconnected fragments.\n" % nr_fragments
        error_msg += "Internal coordinates only work if all atoms in the molecular graph are connected.\n"
        error_msg += "Disconnected fragments may be joined via an artificial bond using the\n"
        error_msg += "`explicit_bonds` option.\n"
        assert nr_fragments == 1, error_msg

        # Frozen degrees of freedom do not necessarily correspond to physical bonds
        # or angles. For instance we can freeze the H-H distance in water although there
        # is no bond between the hydrogens. To allow the definition of such 'unphysical'
        # internal coordinates, we have to modify the connectivity matrix and introduce
        # artificial bonds.
        for IJKL in freeze:
            if len(IJKL) == 2:
                I, J = IJKL
                # create artificial bond between atoms I and J
                conmat[I, J] = 1
            elif len(IJKL) == 3:
                I, J, K = IJKL
                # create artifical bonds I-J and J-K so that the valence angle I-J-K exists
                conmat[I, J] = 1
                conmat[J, K] = 1
            elif len(IJKL) == 4:
                I, J, K, L = IJKL
                # create artifical bonds I-J, J-K and K-L so that the dihedral angle I-J-K-L
                # exists
                conmat[I, J] = 1
                conmat[J, K] = 1
                conmat[K, L] = 1

        # cutoff for small singular values when solving the
        # linear system of equations B.dx = dq in a least square
        # sense.
        self.cond_threshold = 1.0e-10

        self.force_field = PeriodicForceField(atomlist,
                                              atomtypes,
                                              partial_charges,
                                              lattice_vectors, [],
                                              connectivity_matrix=conmat,
                                              verbose=1)
        x0 = XYZ.atomlist2vector(atomlist)
        # shift molecule to center of mass
        self.x0 = MolCo.shift_to_com(x0, self.masses)

        self._selectActiveInternals(freeze=freeze)
Ejemplo n.º 18
0
    def __init__(self, xyz_file, dyson_file=None):
        super(Main, self).__init__()
        self.settings = Settings({
            "Continuum Orbital": {
                "Ionization transitions":
                [0, ["only intra-atomic", "inter-atomic"]]
            },
            "Averaging": {
                "Euler angle grid points": 5,
                "polar angle grid points": 1000,
                "sphere radius Rmax": 300.0,
            },
            "Scan": {
                "nr. points": 20
            },
            "Cube": {
                "extra space / bohr": 15.0,
                "points per bohr": 3.0
            }
        })
        # perform DFTB calculation

        # BOUND ORBITAL = H**O
        self.atomlist = XYZ.read_xyz(xyz_file)[0]
        # shift molecule to center of mass
        print "shift molecule to center of mass"
        pos = XYZ.atomlist2vector(self.atomlist)
        masses = AtomicData.atomlist2masses(self.atomlist)
        pos_com = MolCo.shift_to_com(pos, masses)
        self.atomlist = XYZ.vector2atomlist(pos_com, self.atomlist)

        self.tddftb = LR_TDDFTB(self.atomlist)
        self.tddftb.setGeometry(self.atomlist, charge=0)
        options = {"nstates": 1}
        try:
            self.tddftb.getEnergies(**options)
        except DFTB.Solver.ExcitedStatesNotConverged:
            pass

        self.valorbs, radial_val = load_pseudo_atoms(self.atomlist)

        if dyson_file == None:
            # Kohn-Sham orbitals are taken as Dyson orbitals
            self.H**O, self.LUMO = self.tddftb.dftb2.getFrontierOrbitals()
            self.bound_orbs = self.tddftb.dftb2.getKSCoefficients()
            self.orbe = self.tddftb.dftb2.getKSEnergies()
            orbital_names = []
            norb = len(self.orbe)
            for o in range(0, norb):
                if o < self.H**O:
                    name = "occup."
                elif o == self.H**O:
                    name = "H**O"
                elif o == self.LUMO:
                    name = "LUMO "
                else:
                    name = "virtual"
                name = name + "  " + str(o).rjust(4) + (
                    "   %+10.3f eV" % (self.orbe[o] * 27.211))
                orbital_names.append(name)
            initially_selected = self.H**O
        else:
            # load coefficients of Dyson orbitals from file
            names, ionization_energies, self.bound_orbs = load_dyson_orbitals(
                dyson_file)
            self.orbe = np.array(ionization_energies) / 27.211
            orbital_names = []
            norb = len(self.orbe)
            for o in range(0, norb):
                name = names[o] + "  " + str(o).rjust(4) + (
                    "   %4.2f eV" % (self.orbe[o] * 27.211))
                orbital_names.append(name)
            initially_selected = 0

        self.photo_kinetic_energy = slako_tables_scattering.energies[0]
        self.epol = np.array([15.0, 0.0, 0.0])

        # Build Graphical User Interface
        main = QtGui.QWidget()
        mainLayout = QtGui.QHBoxLayout(main)
        #
        selectionFrame = QtGui.QFrame()
        selectionFrame.setSizePolicy(QtGui.QSizePolicy.Fixed,
                                     QtGui.QSizePolicy.Preferred)
        mainLayout.addWidget(selectionFrame)
        selectionLayout = QtGui.QVBoxLayout(selectionFrame)
        #
        label = QtGui.QLabel(selectionFrame)
        label.setText("Select bound MO:")
        selectionLayout.addWidget(label)

        # bound orbitals
        self.orbitalSelection = QtGui.QListWidget(selectionFrame)
        self.orbitalSelection.itemSelectionChanged.connect(
            self.selectBoundOrbital)
        norb = len(self.orbe)
        self.orbital_dict = {}
        for o in range(0, norb):
            name = orbital_names[o]
            self.orbital_dict[name] = o
            item = QtGui.QListWidgetItem(name, self.orbitalSelection)
            if o == initially_selected:
                selected_orbital_item = item
            selectionLayout.addWidget(self.orbitalSelection)

        ### VIEWS
        center = QtGui.QWidget()
        mainLayout.addWidget(center)
        centerLayout = QtGui.QGridLayout(center)
        #
        boundFrame = QtGui.QFrame()

        centerLayout.addWidget(boundFrame, 1, 1)
        boundLayout = QtGui.QVBoxLayout(boundFrame)
        # "Bound Orbital"
        label = QtGui.QLabel(boundFrame)
        label.setText("Bound Orbital")
        boundLayout.addWidget(label)
        #
        self.boundOrbitalViewer = QCubeViewerWidget(boundFrame)
        boundLayout.addWidget(self.boundOrbitalViewer)

        # continuum orbital
        continuumFrame = QtGui.QFrame()
        centerLayout.addWidget(continuumFrame, 1, 2)
        continuumLayout = QtGui.QVBoxLayout(continuumFrame)
        # "Dipole-Prepared Continuum Orbital"
        label = QtGui.QLabel(continuumFrame)
        label.setText("Dipole-Prepared Continuum Orbital")
        continuumLayout.addWidget(label)

        self.continuumOrbitalViewer = QCubeViewerWidget(continuumFrame)
        continuumLayout.addWidget(self.continuumOrbitalViewer)

        self.efield_objects = []
        self.efield_actors = []
        self.selected = None
        # picker
        self.picker = self.continuumOrbitalViewer.visualization.scene.mayavi_scene.on_mouse_pick(
            self.picker_callback)
        self.picker.tolerance = 0.01

        # PHOTO KINETIC ENERGY
        sliderFrame = QtGui.QFrame(continuumFrame)
        continuumLayout.addWidget(sliderFrame)
        sliderLayout = QtGui.QHBoxLayout(sliderFrame)
        # label
        self.pke_label = QtGui.QLabel()
        self.pke_label.setText("PKE: %6.4f eV" %
                               (self.photo_kinetic_energy * 27.211))
        sliderLayout.addWidget(self.pke_label)
        # Slider for changing the PKE
        self.pke_slider = QtGui.QSlider(QtCore.Qt.Horizontal)
        self.pke_slider.setMinimum(0)
        self.pke_slider.setMaximum(len(slako_tables_scattering.energies) - 1)
        self.pke_slider.setValue(0)
        self.pke_slider.sliderReleased.connect(self.changePKE)
        self.pke_slider.valueChanged.connect(self.searchPKE)
        sliderLayout.addWidget(self.pke_slider)

        #

        # molecular frame photoangular distribution
        mfpadFrame = QtGui.QFrame()
        centerLayout.addWidget(mfpadFrame, 2, 1)
        mfpadLayout = QtGui.QVBoxLayout(mfpadFrame)
        mfpadLayout.addWidget(QtGui.QLabel("Molecular Frame PAD"))
        mfpadTabs = QtGui.QTabWidget()
        mfpadLayout.addWidget(mfpadTabs)
        # 2D map
        mfpadFrame2D = QtGui.QFrame()
        mfpadTabs.addTab(mfpadFrame2D, "2D")
        mfpadLayout2D = QtGui.QVBoxLayout(mfpadFrame2D)
        self.MFPADfig2D = Figure()
        self.MFPADCanvas2D = FigureCanvas(self.MFPADfig2D)
        mfpadLayout2D.addWidget(self.MFPADCanvas2D)
        self.MFPADCanvas2D.draw()
        NavigationToolbar(self.MFPADCanvas2D, mfpadFrame2D, coordinates=True)
        # 3D
        mfpadFrame3D = QtGui.QFrame()
        mfpadTabs.addTab(mfpadFrame3D, "3D")
        mfpadLayout3D = QtGui.QVBoxLayout(mfpadFrame3D)
        self.MFPADfig3D = Figure()
        self.MFPADCanvas3D = FigureCanvas(self.MFPADfig3D)
        mfpadLayout3D.addWidget(self.MFPADCanvas3D)
        self.MFPADCanvas3D.draw()
        NavigationToolbar(self.MFPADCanvas3D, mfpadFrame3D, coordinates=True)

        # orientation averaged photoangular distribution
        avgpadFrame = QtGui.QFrame()
        centerLayout.addWidget(avgpadFrame, 2, 2)
        avgpadLayout = QtGui.QVBoxLayout(avgpadFrame)
        self.activate_average = QtGui.QCheckBox("Orientation Averaged PAD")
        self.activate_average.setToolTip(
            "Check this box to start averaging of the molecular frame PADs over all orientations. This can take a while."
        )
        self.activate_average.setCheckState(QtCore.Qt.Unchecked)
        self.activate_average.stateChanged.connect(self.activateAveragedPAD)
        avgpadLayout.addWidget(self.activate_average)

        avgpadTabs = QtGui.QTabWidget()
        avgpadLayout.addWidget(avgpadTabs)
        # 1D map
        avgpadFrame1D = QtGui.QFrame()
        avgpadTabs.addTab(avgpadFrame1D, "1D")
        avgpadLayout1D = QtGui.QVBoxLayout(avgpadFrame1D)
        self.AvgPADfig1D = Figure()
        self.AvgPADCanvas1D = FigureCanvas(self.AvgPADfig1D)
        avgpadLayout1D.addWidget(self.AvgPADCanvas1D)
        self.AvgPADCanvas1D.draw()
        NavigationToolbar(self.AvgPADCanvas1D, avgpadFrame1D, coordinates=True)
        # 2D map
        avgpadFrame2D = QtGui.QFrame()
        avgpadFrame2D.setToolTip(
            "The averaged PAD should have no phi-dependence anymore. A phi-dependence is a sign of incomplete averaging."
        )
        avgpadTabs.addTab(avgpadFrame2D, "2D")
        avgpadLayout2D = QtGui.QVBoxLayout(avgpadFrame2D)
        self.AvgPADfig2D = Figure()
        self.AvgPADCanvas2D = FigureCanvas(self.AvgPADfig2D)
        avgpadLayout2D.addWidget(self.AvgPADCanvas2D)
        self.AvgPADCanvas2D.draw()
        NavigationToolbar(self.AvgPADCanvas2D, avgpadFrame2D, coordinates=True)
        # Table
        avgpadFrameTable = QtGui.QFrame()
        avgpadTabs.addTab(avgpadFrameTable, "Table")
        avgpadLayoutTable = QtGui.QVBoxLayout(avgpadFrameTable)
        self.avgpadTable = QtGui.QTableWidget(0, 6)
        self.avgpadTable.setToolTip(
            "Activate averaging and move the PKE slider above to add a new row with beta values. After collecting betas for different energies you can save the table or plot a curve beta(PKE) for the selected orbital."
        )
        self.avgpadTable.setHorizontalHeaderLabels(
            ["PKE / eV", "sigma", "beta1", "beta2", "beta3", "beta4"])
        avgpadLayoutTable.addWidget(self.avgpadTable)
        # Buttons
        buttonFrame = QtGui.QFrame()
        avgpadLayoutTable.addWidget(buttonFrame)
        buttonLayout = QtGui.QHBoxLayout(buttonFrame)
        deleteButton = QtGui.QPushButton("Delete")
        deleteButton.setToolTip("clear table")
        deleteButton.clicked.connect(self.deletePADTable)
        buttonLayout.addWidget(deleteButton)
        buttonLayout.addSpacing(3)
        scanButton = QtGui.QPushButton("Scan")
        scanButton.setToolTip(
            "fill table by scanning automatically through all PKE values")
        scanButton.clicked.connect(self.scanPADTable)
        buttonLayout.addWidget(scanButton)
        saveButton = QtGui.QPushButton("Save")
        saveButton.setToolTip("save table as a text file")
        saveButton.clicked.connect(self.savePADTable)
        buttonLayout.addWidget(saveButton)
        plotButton = QtGui.QPushButton("Plot")
        plotButton.setToolTip("plot beta2 column as a function of PKE")
        plotButton.clicked.connect(self.plotPADTable)
        buttonLayout.addWidget(plotButton)
        """
        # DOCKS
        self.setDockOptions(QtGui.QMainWindow.AnimatedDocks | QtGui.QMainWindow.AllowNestedDocks)
        #
        selectionDock = QtGui.QDockWidget(self)
        selectionDock.setWidget(selectionFrame)
        selectionDock.setFeatures(QtGui.QDockWidget.DockWidgetFloatable | QtGui.QDockWidget.DockWidgetMovable)
        self.addDockWidget(QtCore.Qt.DockWidgetArea(1), selectionDock)
        #
        boundDock = QtGui.QDockWidget(self)
        boundDock.setWidget(boundFrame)
        boundDock.setFeatures(QtGui.QDockWidget.DockWidgetFloatable | QtGui.QDockWidget.DockWidgetMovable)
        boundDock.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred)
        self.addDockWidget(QtCore.Qt.DockWidgetArea(2), boundDock)
        # 
        continuumDock = QtGui.QDockWidget(self)
        continuumDock.setWidget(continuumFrame)
        continuumDock.setFeatures(QtGui.QDockWidget.DockWidgetFloatable | QtGui.QDockWidget.DockWidgetMovable)
        continuumDock.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred)
        self.addDockWidget(QtCore.Qt.DockWidgetArea(2), continuumDock)
        """
        self.setCentralWidget(main)

        self.status_bar = QtGui.QStatusBar(main)
        self.setStatusBar(self.status_bar)
        self.default_message = "Click on the tip of the green arrow in the top right figure to change the orientation of the E-field"
        self.statusBar().showMessage(self.default_message)

        # Menu bar
        menubar = self.menuBar()
        exitAction = QtGui.QAction('&Exit', self)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.setStatusTip('Exit program')
        exitAction.triggered.connect(exit)
        fileMenu = menubar.addMenu('&File')
        fileMenu.addAction(exitAction)

        settingsMenu = menubar.addMenu('&Edit')
        settingsAction = QtGui.QAction('&Settings...', self)
        settingsAction.setStatusTip('Edit settings')
        settingsAction.triggered.connect(self.editSettings)
        settingsMenu.addAction(settingsAction)

        self.loadContinuum()
        # select H**O
        selected_orbital_item.setSelected(True)
Ejemplo n.º 19
0
    def __init__(self,
                 atomlist,
                 E0,
                 vib_freq,
                 symmetry_group,
                 pressure=AtomicData.atm_pressure,
                 temperature=AtomicData.satp_temperature):
        """
        temperature in Kelvin
        """
        self.E0 = E0
        self.P = pressure
        self.T = temperature
        self.vib_freq = vib_freq
        self.symmetry_group = symmetry_group

        self.atomlist = atomlist
        Nat = len(atomlist)
        self.masses = AtomicData.atomlist2masses(self.atomlist)
        # compute rotational constants from tensor of inertia
        x0 = XYZ.atomlist2vector(atomlist)
        # shift the origin to the center of mass
        x0_shift = MolCo.shift_to_com(x0, self.masses)
        # diagonalize the tensor of inertia to obtain the principal moments and
        # the normalized eigenvectors of I
        Inert = MolCo.inertial_tensor(self.masses, x0_shift)
        principle_moments, X = la.eigh(Inert)
        Iaa, Ibb, Icc = np.sort(abs(principle_moments))
        print("principle moments of inertia (in a.u.): %s %s %s" %
              (Iaa, Ibb, Icc))
        # In a linear triatomic molecule we have Icc = Ibb > Iaa = 0
        self.is_linear = False
        if abs(Icc - Ibb) / abs(Icc) < 1.0e-6 and abs(Iaa) / abs(Icc) < 1.0e-6:
            self.is_linear = True
            print("Molecule is linear")
        # Determine the type of rotor
        if self.is_linear == True:
            self.rotor_type = "linear rotor"
        else:
            if abs(Icc - Ibb) / abs(Icc) < 1.0e-4 and abs(Icc - Iaa) / abs(
                    Icc) < 1.0e-4:
                # three equal moments of inertia
                self.rotor_type = "spherical rotor"
            elif abs(Icc - Ibb) / abs(Icc) < 1.0e-4 or abs(Ibb - Iaa) / abs(
                    Icc) < 1.0e-4:
                # two equal moments of inertia
                self.rotor_type = "symmetric rotor"
            else:
                self.rotor_type = "asymmetric rotor"

        # rotational constants
        self.rotational_constants = 1.0 / (
            2.0 * principle_moments + 1.0e-20
        )  # avoid division by zero error for a linear molecule, the invalid values are not actually used
        # symmetry number
        if symmetry_group == None:
            print(
                "Symmetry group unknown, setting rotational symmetry number to 1"
            )
            self.sigma_rot = 1
        else:
            self.sigma_rot = symmetry_group.rotational_symmetry_number()
        # beta = 1/(kB*T)
        kB = AtomicData.kBoltzmann
        self.beta = 1.0 / (kB * self.T)
Ejemplo n.º 20
0
    def _create_visualization(self, atomlist):
        mlab = self.scene.mlab
        # draw atoms as balls
        vec = XYZ.atomlist2vector(atomlist)
        x, y, z = vec[::3], vec[1::3], vec[2::3]
        Zs = np.array([Z for (Z,pos) in atomlist])
        atom_names = [AtomicData.atom_names[Z-1] for (Z,pos) in atomlist]
        rads = np.array([AtomicData.covalent_radii.get(atname) for atname in atom_names])
        atom_radii = np.sqrt(rads)*1.8

        s = atom_radii
        if self.show_flags["atoms"] == False:
            # make atoms so small that one does not see them
            scale_factor = 0.01
            self.shown_atoms = False
        else:
            scale_factor = 0.45
            self.shown_atoms = True
        atoms = mlab.quiver3d(x,y,z, s,s,s, scalars=Zs.astype(float),
                              mode="sphere", scale_factor=scale_factor, resolution=20,
                              figure=self.scene.mayavi_scene)

        # atoms are coloured by their atomic number
        atoms.glyph.color_mode = "color_by_scalar"
        atoms.glyph.glyph_source.glyph_source.center = [0,0,0]
        self.lut = atoms.module_manager.scalar_lut_manager.lut.table.to_array()
        for atname in set(atom_names):
            Z = AtomicData.atomic_number(atname)
            self.lut[Z,0:3] = ( np.array( atom_colours_rgb.get(atname, (0.0, 0.75, 0.75)) )*255.0 ).astype('uint8')
        atoms.module_manager.scalar_lut_manager.lut.table = self.lut
        atoms.module_manager.scalar_lut_manager.data_range = (0.0, 255.0)
        
        # draw bonds
        C = XYZ.connectivity_matrix(atomlist)
        Nat = len(atomlist)
        connections = []
        bonds = mlab.points3d(x,y,z, scale_factor=0.15, resolution=20,
                              figure=self.scene.mayavi_scene)
        for i in range(0, Nat):
            Zi, posi = atomlist[i]
            for j in range(i+1,Nat):
                if C[i,j] == 1:
                    Zj, posj = atomlist[j]
                    connections.append( (i,j) )
        bonds.mlab_source.dataset.lines = np.array(connections)
        bonds.mlab_source.dataset.modified()
        
        tube = mlab.pipeline.tube(bonds, tube_radius=0.15, #tube_radius=0.05,
                                  figure=self.scene.mayavi_scene)
        tube.filter.radius_factor = 1.0
        surface = mlab.pipeline.surface(tube, color=(0.8,0.8,0.0),
                                        opacity=0.7,
                                        figure=self.scene.mayavi_scene)

        self.atoms = atoms
        self.bonds = bonds
#        self.tube = tube
#        self.surface = surface
        self.atom_radii = s
        self.Zs = Zs.astype(float)
        self.primitives.append(atoms)
        self.primitives.append(bonds)
        self.primitives.append(tube)
        self.primitives.append(surface)
        # Lewis structure
        if self.show_flags["Lewis structure"] == True:
            bondsTuples, bond_orders, lone_pairs, formal_charges = BondOrders.assign_bond_orders(atomlist, C, charge=0)
            # plot DOUBLE bonds
            double_bonds = mlab.points3d(x,y,z, scale_factor=0.15, resolution=20, figure=self.scene.mayavi_scene)
            connections_double = []
            for i,bond in enumerate(bondsTuples):
                if bond_orders[i] == 2:
                    # double bond between atoms I and J
                    connections_double.append( bond )
            double_bonds.mlab_source.dataset.lines = np.array(connections_double)
            double_bonds.mlab_source.dataset.modified()
            
            tube = mlab.pipeline.tube(double_bonds, tube_radius=0.35, figure=self.scene.mayavi_scene)
            tube.filter.radius_factor = 1.0
            surface = mlab.pipeline.surface(tube, color=(0.8,0.8,0.0), figure=self.scene.mayavi_scene)
            #
            self.double_bonds = double_bonds
            self.primitives.append(double_bonds)
            self.primitives.append(tube)
            self.primitives.append(surface)
            #
            # plot TRIPLE bonds
            triple_bonds = mlab.points3d(x,y,z, scale_factor=0.15, resolution=20, figure=self.scene.mayavi_scene)
            connections_triple = []
            for i,bond in enumerate(bondsTuples):
                if bond_orders[i] == 3:
                    # triple bond between atoms I and J
                    connections_triple.append( bond )
            triple_bonds.mlab_source.dataset.lines = np.array(connections_triple)
            triple_bonds.mlab_source.dataset.modified()
            
            tube = mlab.pipeline.tube(triple_bonds, tube_radius=0.35, figure=self.scene.mayavi_scene)
            tube.filter.radius_factor = 1.0
            surface = mlab.pipeline.surface(tube, color=(0.8,0.8,0.0), figure=self.scene.mayavi_scene)
            #
            self.triple_bonds = triple_bonds
            self.primitives.append(triple_bonds)
            self.primitives.append(tube)
            self.primitives.append(surface)

            self.shown_lewis_structure = True
Ejemplo n.º 21
0
    def _update_visualization(self, atomlist, charges, fragments):
        """
        only change the underlying data but do not recreate visualization.
        This is faster and avoids jerky animations.
        """
        mlab = self.scene.mlab
        if self.show_flags["charges"] == True:
            i = 0
            for q,(Z,pos) in zip(charges, atomlist):
                x,y,z = pos
                if q < 0.0:
                    color = (0.,0.,1.)
                elif q > 0.0:
                    color = (1.,0.,0.)
                else:
                    color = (1.,1.,1.)
                # color does not work so far
                txt = "%+2.3f" % q
                if self.show_flags["labels"] == True:
                    # maybe charges should not overlap with labels
                    txt = "%s" % txt
                # update label position and text
                label = self.charge_labels[i]
                label.remove()
                label = mlab.text(x,y, txt, z=z, figure=self.scene.mayavi_scene)
                self.charge_labels[i] = label
                label.actor.set(text_scale_mode='none', width=0.05, height=0.1)
                label.property.set(justification='centered', vertical_justification='centered')

                i += 1
        if self.show_flags["frag. charges"] == True:
            i = 0
            for ifrag,(fragment_indeces, fragment_atomlist) in enumerate(fragments):
                # compute the charges on fragment 
                qfrag = np.sum(charges[fragment_indeces])
                print "Fragment charges: %s" % charges[fragment_indeces]
                # compute the center of the molecule,
                pos_frag = XYZ.atomlist2vector(fragment_atomlist)
                masses_frag = AtomicData.atomlist2masses(fragment_atomlist)
                com = MolCo.center_of_mass(masses_frag, pos_frag)
                #
                print "Fragment %d  charge = %s" % (ifrag, qfrag)
                txt = "%+2.3f" % qfrag
                label = self.frag_charge_labels[i]
                label.remove()
                label = mlab.text(com[0],com[1], txt, z=com[2], line_width=0.8, figure=self.scene.mayavi_scene)
                self.frag_charge_labels[i] = label
                label.actor.set(text_scale_mode='none', width=0.05, height=0.1)
                label.property.set(justification='centered', vertical_justification='centered')

                i += 1
        if self.show_flags["charge clouds"] == True:

            vec = XYZ.atomlist2vector(atomlist)
            x, y, z = vec[::3], vec[1::3], vec[2::3]
            s = abs(charges)

            # The charge clouds represent surfaces of equal charge density around each atoms.
            # In DFTB the charge fluctuations are modelled by a Gaussian:
            #  F(r) = 1/(2*pi*sA^2)^(3/2) * exp(-r^2/(2*sA^2))
            # The radii of the charge clouds are scaled by the charge on the atom:
            #  r = q * r0
            # The radius r0 belongs to a charge cloud containing exactly 1 electron, it depends
            # on the hubbard parameter through sA and on the isoValue:
            #  F(r0) = F(0) * isoValue
            #
            r0s = self.charge_cloud_radii.getRadii(atomlist)
            s *= r0s

            self.cloud.mlab_source.set(x=x,y=y,z=z,u=s,v=s,w=s, scalars=charges, scale_factor=1.0)
            # atoms are coloured by their atomic number
            self.cloud.glyph.color_mode = "color_by_scalar"
            self.cloud.glyph.glyph_source.glyph_source.center = [0,0,0]
            self.cloud.module_manager.scalar_lut_manager.lut.table = self.lut
            self.cloud.module_manager.scalar_lut_manager.data_range = (-1.0, 1.0)
Ejemplo n.º 22
0
    def _create_visualization(self, atomlist, charges, fragments):
        mlab = self.scene.mlab
        
        if self.show_flags["charges"] == True and type(charges) != type(None):
            self.charge_labels = []
            for q,(Z,pos) in zip(charges, atomlist):
                x,y,z = pos
                if q < 0.0:
                    color = (0.,0.,1.)
                elif q > 0.0:
                    color = (1.,0.,0.)
                else:
                    color = (1.,1.,1.)
                # color does not work so far
                txt = "%+2.3f" % q
                if self.show_flags["labels"] == True:
                    # maybe charges should not overlap with labels
                    txt = "%s" % txt
                label = mlab.text(x,y, txt, z=z, figure=self.scene.mayavi_scene)
                label.actor.set(text_scale_mode='none', width=0.05, height=0.1)
                label.property.set(justification='centered', vertical_justification='centered')
                self.charge_labels.append( label )
            self.shown_charges = True
        else:
            self.shown_charges = False
            
        if self.show_flags["frag. charges"] == True and type(charges) != type(None):
            self.frag_charge_labels = []
            for ifrag,(fragment_indeces, fragment_atomlist, fragment_box) in enumerate(fragments):
                # compute the charges on fragment 
                qfrag = np.sum(charges[fragment_indeces])
                print "Fragment charges: %s" % charges[fragment_indeces]
                # compute the center of the molecule,
                pos_frag = XYZ.atomlist2vector(fragment_atomlist)
                masses_frag = AtomicData.atomlist2masses(fragment_atomlist)
                com = MolCo.center_of_mass(masses_frag, pos_frag)
                #
                print "Fragment %d  charge = %s" % (ifrag, qfrag)
                txt = "%+2.3f" % qfrag
                label = mlab.text(com[0],com[1], txt, z=com[2], line_width=0.8, figure=self.scene.mayavi_scene)
                label.actor.set(text_scale_mode='none', width=0.05, height=0.1)
                label.property.set(justification='centered', vertical_justification='centered')
                self.frag_charge_labels.append( label )
            self.shown_frag_charges = True
        else:
            self.shown_frag_charges = False
            
        if self.show_flags["charge clouds"] == True and type(charges) != type(None):
            self.charge_clouds = []

            vec = XYZ.atomlist2vector(atomlist)
            x, y, z = vec[::3], vec[1::3], vec[2::3]
            s = abs(charges)

            # The charge clouds represent surfaces of equal charge density around each atoms.
            # In DFTB the charge fluctuations are modelled by a Gaussian:
            #  F(r) = 1/(2*pi*sA^2)^(3/2) * exp(-r^2/(2*sA^2))
            # The radii of the charge clouds are scaled by the charge on the atom:
            #  r = q * r0
            # The radius r0 belongs to a charge cloud containing exactly 1 electron, it depends
            # on the hubbard parameter through sA and on the isoValue:
            #  F(r0) = F(0) * isoValue
            #
            r0s = self.charge_cloud_radii.getRadii(atomlist)
            s *= r0s
            
            cloud = mlab.quiver3d(x,y,z,s,s,s, scalars=charges, 
                                           mode="sphere", scale_factor=1.0, resolution=20,
                                           opacity = 0.4, 
                                           figure=self.scene.mayavi_scene)

            # atoms are coloured by their atomic number
            cloud.glyph.color_mode = "color_by_scalar"
            cloud.glyph.glyph_source.glyph_source.center = [0,0,0]
            self.lut = cloud.module_manager.scalar_lut_manager.lut.table.to_array()
            red = np.array((255.0, 0.0, 0.0)).astype('uint8')
            blue = np.array((0.0, 0.0, 255.0)).astype('uint8')
            for i in range(0, 255):
                if i < 128:
                    color = blue
                else:
                    color = red
                self.lut[i,0:3] = color
            cloud.module_manager.scalar_lut_manager.lut.table = self.lut
            cloud.module_manager.scalar_lut_manager.data_range = (-1.0, 1.0)

            self.cloud = cloud
            self.charge_clouds.append( cloud )
            
            self.shown_charge_clouds = True
            
        else:
            self.shown_charge_clouds = False

        if self.show_flags["dipole moment"] == True and type(charges) != type(None):
            self.dipole_vectors = []
            # The dipole vector is placed at the center of mass
            pos = XYZ.atomlist2vector(atomlist)
            masses = AtomicData.atomlist2masses(atomlist)
            com = MolCo.center_of_mass(masses, pos)
            # compute dipole moment from charge distribution
            dipole = np.zeros(3)
            for i,q in enumerate(charges):
                dipole += q * pos[3*i:3*(i+1)]
            print "Dipole moment  D = %s a.u." % dipole
            # For plotting the dipole vector is converted to Debye
            dipole *= AtomicData.ebohr_to_debye
            print "Dipole moment  D = %s Debye" % dipole
            print "Length of dipole moment |D| = %s Debye" % la.norm(dipole)
            
            quiver3d = mlab.quiver3d(com[0],com[1],com[2],
                                     dipole[0], dipole[1], dipole[2], line_width=5.0,
                                     scale_mode='vector',
                                     color=(0,0,1), 
                                     scale_factor=1.0)
            
            self.dipole_vectors.append(quiver3d)
        else:
            self.shown_dipole_moment = False    
            
        if self.show_flags["enclosing box"] == True:
            self.frag_enclosing_boxes = []
            for ifrag,(fragment_indeces, fragment_atomlist, fragment_box) in enumerate(fragments):
                box = fragment_box
                # plot edges of the enclosing box
                for edge in box.edges:
                    l = mlab.plot3d(box.vertices[edge,0], box.vertices[edge,1], box.vertices[edge,2],
                                    color=(1,0,0),
                                    figure=self.scene.mayavi_scene)
                    self.frag_enclosing_boxes.append(l)
                # plot axes
                for axis, color in zip(box.axes, [(1.,0.,0.),(0.,1.,0.), (0.,0.,1.)]):
                    ax = mlab.quiver3d(float(box.center[0]), float(box.center[1]), float(box.center[2]),
                                       float(axis[0]), float(axis[1]), float(axis[2]),
                                       color=color, scale_factor=3.0, mode='arrow', resolution=20,
                                       figure=self.scene.mayavi_scene)
                    self.frag_enclosing_boxes.append(ax)
            self.shown_enclosing_boxes = True
        else:
            self.shown_enclosing_boxes = False    

        if self.show_flags["screening charges (COSMO)"] == True:
            # read parameter for solvent model from command line
            # or configuration file
            parser = OptionParserFuncWrapper([ImplicitSolvent.SolventCavity.__init__], "",
                                             unknown_options="ignore")
            # extract only arguments for constructor of solvent cavity
            (solvent_options, args) = parser.parse_args(ImplicitSolvent.SolventCavity.__init__)
            # 
            cavity = ImplicitSolvent.SolventCavity(**solvent_options)
            cavity.constructSAS(atomlist)
            area = cavity.getSurfaceArea()
            print "solvent accessible surface area: %8.6f bohr^2   %8.6f Ang^2" % (area, area*AtomicData.bohr_to_angs**2)
            points = cavity.getSurfacePoints()
            x,y,z = points[:,0], points[:,1], points[:,2]
            if type(charges) != type(None):
                # If there are Mulliken charges we can compute the
                # induced charges on the surface of the cavity
                # according to COSMO
                cavity.constructCOSMO()
                induced_charges = cavity.getInducedCharges(charges)
                screening_energy = cavity.getScreeningEnergy(charges)
                print "screening energy: %10.6f Hartree  %10.6f kcal/mol" \
                    % (screening_energy, screening_energy * AtomicData.hartree_to_kcalmol)
                # The surface points are colored and scaled
                # according to their charge
                #  negative -> blue   positive -> red
                points3d = mlab.points3d(x,y,z,induced_charges,
                                         colormap="blue-red",
                                         mode='2dcross',
                                         scale_factor=10)
#                                         mode='2dvertex')
                points3d.glyph.color_mode = "color_by_scalar"
            else:
                # 
                points3d = mlab.points3d(x,y,z,color=(0.0,0.0,0.8), mode='2dvertex')
            self.sas_points.append( points3d )
        else:
            self.shown_solvent_screening_charges = False

        if self.show_flags["non-adiab. coupling vectors"] == True:
            vec = XYZ.atomlist2vector(atomlist)
            x, y, z = vec[::3], vec[1::3], vec[2::3]
            
            # assume that charges are transition charges
            transition_charges = charges
            
            # Because we don't know the energy here, the energy difference
            # in the denominator is set to 1, so only the direction and relative
            # lengths are correct.
            nac = NACsApprox.coupling_vector(atomlist, transition_charges, 1.0)
            # directions of NAC vectors
            u,v,w = nac[0,:], nac[1,:], nac[2,:]
            # Add a NAC vector at each atom
            quiver3d = mlab.quiver3d(x,y,z, u,v,w, line_width=5.0)
            
            self.nac_vectors.append(quiver3d)
            
        else:
            self.shown_nacs = False
def averaged_pad_scan(xyz_file, dyson_file,
                      selected_orbitals, npts_euler, npts_theta, nskip, inter_atomic, sphere_radius):
    molecule_name = os.path.basename(xyz_file).replace(".xyz", "")
    atomlist = XYZ.read_xyz(xyz_file)[-1]
    # shift molecule to center of mass
    print "shift molecule to center of mass"
    pos = XYZ.atomlist2vector(atomlist)
    masses = AtomicData.atomlist2masses(atomlist)
    pos_com = MolCo.shift_to_com(pos, masses)
    atomlist = XYZ.vector2atomlist(pos_com, atomlist)
    # compute molecular orbitals with DFTB
    tddftb = LR_TDDFTB(atomlist)
    tddftb.setGeometry(atomlist, charge=0)
    options={"nstates": 1}
    try:
        tddftb.getEnergies(**options)
    except DFTB.Solver.ExcitedStatesNotConverged:
        pass

    valorbs, radial_val = load_pseudo_atoms(atomlist)

    if dyson_file == None:
        print "tight-binding Kohn-Sham orbitals are taken as Dyson orbitals"
        H**O, LUMO = tddftb.dftb2.getFrontierOrbitals()
        bound_orbs = tddftb.dftb2.getKSCoefficients()
        if selected_orbitals == None:
            # all orbitals
            selected_orbitals = range(0,bound_orbs.shape[1])
        else:
            selected_orbitals = eval(selected_orbitals, {}, {"H**O": H**O+1, "LUMO": LUMO+1})
            print "Indeces of selected orbitals (counting from 1): %s" % selected_orbitals
        orbital_names = ["orb_%s" % o for o in selected_orbitals]
        selected_orbitals = np.array(selected_orbitals, dtype=int)-1 # counting from 0
        dyson_orbs = bound_orbs[:,selected_orbitals]
        ionization_energies = -tddftb.dftb2.getKSEnergies()[selected_orbitals]
    else:
        print "coeffients for Dyson orbitals are read from '%s'" % dyson_file
        orbital_names, ionization_energies, dyson_orbs = load_dyson_orbitals(dyson_file)
        ionization_energies = np.array(ionization_energies) / AtomicData.hartree_to_eV

    print ""
    print "*******************************************"
    print "*  PHOTOELECTRON ANGULAR DISTRIBUTIONS    *"
    print "*******************************************"
    print ""
    
    # determine the radius of the sphere where the angular distribution is calculated. It should be
    # much larger than the extent of the molecule
    (xmin,xmax),(ymin,ymax),(zmin,zmax) = Cube.get_bbox(atomlist, dbuff=0.0)
    dx,dy,dz = xmax-xmin,ymax-ymin,zmax-zmin
    Rmax = max([dx,dy,dz]) + sphere_radius
    Npts = max(int(Rmax),1) * 50
    print "Radius of sphere around molecule, Rmax = %s bohr" % Rmax
    print "Points on radial grid, Npts = %d" % Npts
    
    nr_dyson_orbs = len(orbital_names)
    # compute PADs for all selected orbitals
    for iorb in range(0, nr_dyson_orbs):
        print "computing photoangular distribution for orbital %s" % orbital_names[iorb]
        data_file = "betas_" + molecule_name + "_" + orbital_names[iorb] + ".dat"
        pad_data = []
        print "  SCAN"
        nskip = max(1, nskip)
        # save table
        fh = open(data_file, "w")
        print "  Writing table with betas to %s" % data_file
        print>>fh, "# ionization from orbital %s   IE = %6.3f eV" % (orbital_names[iorb], ionization_energies[iorb]*AtomicData.hartree_to_eV)
        print>>fh, "# inter_atomic: %s  npts_euler: %s  npts_theta: %s  rmax: %s" % (inter_atomic, npts_euler, npts_theta, Rmax)
        print>>fh, "# PKE/eV     sigma          beta1          beta2      beta3          beta4"
        for i,E in enumerate(slako_tables_scattering.energies):
            if i % nskip != 0:
                continue
            print "    PKE = %6.6f Hartree  (%4.4f eV)" % (E, E*AtomicData.hartree_to_eV)
            k = np.sqrt(2*E)
            wavelength = 2.0 * np.pi/k
            bs_free = AtomicScatteringBasisSet(atomlist, E, rmin=0.0, rmax=Rmax+2*wavelength, Npts=Npts)
            SKT_bf, SKT_ff = load_slako_scattering(atomlist, E)
            Dipole = ScatteringDipoleMatrix(atomlist, valorbs, SKT_bf, inter_atomic=inter_atomic).real

            orientation_averaging = PAD.OrientationAveraging_small_memory(Dipole, bs_free, Rmax, E, npts_euler=npts_euler, npts_theta=npts_theta)

            pad,betasE = orientation_averaging.averaged_pad(dyson_orbs[:,iorb])
            pad_data.append( [E*AtomicData.hartree_to_eV] + list(betasE) )
            # save PAD for this energy
            print>>fh, "%10.6f   %10.6e  %+10.6e  %+10.6f  %+10.6e  %+10.6e" % tuple(pad_data[-1])
            fh.flush()
        fh.close()
def read_repulsive_potential(filename):
    """
    read repulsive potential in the format used by Hotbit, DFTBaby or DFTB+
    """
    from os.path import exists, basename, dirname, join
    if ".par" in filename:
        # Hotbit's format
        at1, at2 = basename(filename).replace(".par", "").split("_")
        if not exists(filename):
            # file file A_B.par does not exist, look for file B_A.par
            filename_BA = join(dirname(filename), "%s_%s.par" % (at2, at1))
            print "parameter file %s does not exist -> try to load %s" % (
                filename, filename_BA)
            filename = filename_BA
        data_blocks = hotbit_format.parseHotbitParameters(filename)
        mod = ReppotModule()
        mod.d = data_blocks["repulsive_potential"][:, 0]
        mod.Vrep = data_blocks["repulsive_potential"][:, 1]
        mod.Z1 = AtomicData.atomic_number(at1)
        mod.Z2 = AtomicData.atomic_number(at2)
        return mod
    elif ".py" in filename:
        # DFTBaby format
        # access dictionary by .-notation
        mod = utils.dotdic()
        execfile(filename, mod)
        return mod
    elif ".skf" in filename:
        # DFTB+ format, only the Splines part is read
        at1, at2 = basename(filename).replace(".skf", "").split("-")
        if not exists(filename):
            # file file A-B.skf does not exist, look for file B-A.skf
            filename_BA = join(dirname(filename), "%s-%s.skf" % (at2, at1))
            print "parameter file %s does not exist -> try to load %s" % (
                filename, filename_BA)
            filename = filename_BA
        fh = open(filename)
        lines = fh.readlines()
        fh.close()
        # find section with repulsive potential
        for i, l in enumerate(lines):
            if l.strip() == "Spline":
                break
        else:
            raise Exception("No 'Spline' section found in parameter file %s" %
                            filename)
        # Line 2:
        nInt, cutoff = lines[i + 1].strip().split()
        nInt, cutoff = int(nInt), float(cutoff)
        # Line 3: V(r < r0) = exp(-a1*r+a2) + a3   is r too small to be covered by the spline
        a1, a2, a3 = map(float, lines[i + 2].strip().split())
        # Line 4 to 4+nInt-2
        rs = np.zeros(nInt)
        cs = np.zeros((4, nInt))
        for j in range(0, nInt):
            # spline for the range [rj=start,r_(j+1)=end]
            # V(r_j <= r < r_(j+1)) = c0 + c1*(r-r0) + c2*(r-r0)^2 + c3*(r-r0)^3
            start, end, c0, c1, c2, c3 = map(
                float, lines[i + 3 + j].strip().split()[:6])
            rs[j] = start
            cs[:, j] = np.array([c0, c1, c2, c3])
        # V(r_nInt < r) = 0.0
        assert end == cutoff
        # Now we evaluate the spline on a equidistant grid
        Npts = 100
        d = np.linspace(0.0, cutoff, Npts)
        Vrep = np.zeros(Npts)
        j = 0
        for i, di in enumerate(d):
            if (di < rs[0]):
                Vrep[i] = np.exp(-a1 * di + a2) + a3
            else:
                # find interval such that r[j] <= di < r[j+1]
                while (di >= rs[j + 1]) and j < nInt - 2:
                    j += 1
                if j < nInt - 2:
                    assert rs[j] <= di < rs[j + 1]
                    c0, c1, c2, c3 = cs[:, j]
                    Vrep[i] = c0 + c1 * (di - rs[j]) + c2 * (
                        di - rs[j])**2 + c3 * (di - rs[j])**3
                else:
                    Vrep[i] = 0.0
        # create python module
        mod = ReppotModule()
        mod.d = d
        mod.Vrep = Vrep
        mod.Z1 = AtomicData.atomic_number(at1)
        mod.Z2 = AtomicData.atomic_number(at2)
        return mod
    else:
        raise Exception("Format of %s not understood" % filename)