Пример #1
0
 def grad(x):
     thomson_cap = XYZ.vector2atomlist(x, thomson_cap_ref)
     thomson_pts = thomson_tube + thomson_cap
     gr = XYZ.atomlist2vector(gradient_energy(thomson_pts))
     return gr
Пример #2
0
    parser = OptionParser(usage)
    parser.add_option(
        "--convert",
        dest="convert",
        help=
        "Type of conversion: 'b2a' - from bohr to Angstrom, 'a2b' - from Angstrom to bohr [default: %default]",
        default="b2a")
    (opts, args) = parser.parse_args()
    if len(args) < 2:
        print usage
        exit(-1)
    xyz_in = args[0]
    xyz_out = args[1]
    if xyz_in[-2:] == "in":
        # initial conditions file ###.in
        coords, vels = XYZ.read_initial_conditions(xyz_in, units="")
        structures = [coords]
    else:
        structures = XYZ.read_xyz(xyz_in, units="")
    unit_fac, units = unit_conversion[opts.convert]
    for i, atomlist_in in enumerate(structures):
        vec_in = XYZ.atomlist2vector(atomlist_in)
        vec_out = unit_fac * vec_in
        atomlist_out = XYZ.vector2atomlist(vec_out, atomlist_in)
        if i == 0:
            mode = "w"
        else:
            mode = "a"
        XYZ.write_xyz(xyz_out, [atomlist_out], units="", mode=mode)
Пример #3
0
def solve_constrained_Thomson(thomson_tube, thomson_cap, L, R):
    def potential_energy(thomson_pts):
        en = 0.0
        for i, (Zi, posi) in enumerate(thomson_pts):
            for j, (Zj, posj) in enumerate(thomson_pts):
                if i == j:
                    continue
                rij = la.norm(posi - posj)
                if rij == 0.0:
                    raise ValueError("rij = 0")
                en += 1.0 / rij
        en *= 0.5
        return en

    def gradient_energy(thomson_pts):
        grad = []
        for k, (Zk, posk) in enumerate(thomson_pts):
            grad_k = np.zeros(3)
            for j, (Zj, posj) in enumerate(thomson_pts):
                if j == k:
                    continue
                rjk = posk - posj
                grad_k += rjk / pow(la.norm(rjk), 3)
            grad.append((Zk, grad_k))
        return grad

    def enforce_constaints(thomson_cap):
        thomson_cap_rescaled = []
        for i, (Zi, posi) in enumerate(thomson_cap):
            x, y, z = posi
            z -= L
            if x >= 0:
                # keep point glued to a sphere of radius R
                r = np.sqrt(x * x + y * y + z * z)
                sc = R / r
                x, y, z = sc * x, sc * y, sc * z + L
            else:
                # point has moved into the tube
                # keep it stuck to a cylinder of radius R
                r = np.sqrt(x * x + y * y)
                sc = R / r
                x, y = sc * x, sc * y
                # z is left as it is
            thomson_cap_rescaled.append((Zi, np.array([x, y, z])))
        return thomson_cap_rescaled

    # function to minimize
    thomson_cap_ref = thomson_cap
    XYZ.write_xyz("/tmp/thomson_minimization.xyz", [thomson_cap_ref], mode="w")

    def f(x):
        thomson_cap = XYZ.vector2atomlist(x, thomson_cap_ref)
        #        thomson_cap = enforce_constaints(thomson_cap)
        print "thomson_cap"
        print thomson_cap
        thomson_pts = thomson_tube + thomson_cap
        XYZ.write_xyz("/tmp/thomson_minimization.xyz", [thomson_cap], mode="a")
        en = potential_energy(thomson_pts)
        # gradient of potential energy
        grad = XYZ.atomlist2vector(gradient_energy(thomson_pts))
        print "en = %s" % en
        return en  #, grad

    def grad(x):
        thomson_cap = XYZ.vector2atomlist(x, thomson_cap_ref)
        thomson_pts = thomson_tube + thomson_cap
        gr = XYZ.atomlist2vector(gradient_energy(thomson_pts))
        return gr

    x0 = XYZ.atomlist2vector(thomson_cap)
    err = optimize.check_grad(f, grad, x0)
    assert err < 1.0e-10, "err = %s" % err
    xmin, fmin, d = optimize.fmin_l_bfgs_b(f, x0)
    thomson_cap_min = XYZ.vector2atomlist(xmin, thomson_cap_ref)
    return thomson_cap_min
Пример #4
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)
Пример #5
0
 def f(x):
     atomlist = XYZ.vector2atomlist(x, atomlist0)
     cavity.constructSAS(atomlist)
     gamma_solvent = cavity.constructCOSMO()
     return gamma_solvent[i, j]
Пример #6
0
 def save_xyz(x, mode="a"):
     atomlist_opt = XYZ.vector2atomlist(x, atomlist)
     XYZ.write_xyz(xyz_trace, [atomlist_opt],
                   title="charge=%s" % kwds.get("charge", 0),
                   mode=mode)
Пример #7
0
        coord_name = "DIHEDRAL(%d-%d-%d-%d)" % (I, J, K, L)
    else:
        raise ValueError("Format of scan '%s' not understood!" % scan)

    # shift indices to programmer's style (starting at 0)
    IJKL = map(lambda I: I - 1, IJKL)

    IC = InternalValenceCoords(atomlist0, freeze=freeze, verbose=opts.verbose)

    # cartesian coordinates along the scan
    scan_geometries = [atomlist0]
    # value of internal coordinate IJKL along the scan
    val0 = IC.coordinate_value(x0, IJKL)
    scan_coords = [val0]

    x1 = x0
    for i in range(0, nsteps):
        # cartesian coordinates at displaced geometry
        x1 = IC.internal_step(x1, IJKL, incr)
        # new value of internal coordinate, should be approximately
        #  val0 + incr
        val1 = IC.coordinate_value(x1, IJKL)

        atomlist1 = XYZ.vector2atomlist(x1, atomlist0)
        scan_geometries.append(atomlist1)
        scan_coords.append(val1)
        print "step %d  %s = %10.6f" % (i + 1, coord_name, val1)

    XYZ.write_xyz(xyz_out, scan_geometries, mode="w")
    print "displaced geometries were saved to '%s'" % xyz_out
Пример #8
0
 def save_xyz(x, mode="a", info=""):
     atomlist_opt = XYZ.vector2atomlist(x, atomlist)
     XYZ.write_xyz(xyz_opt, [atomlist_opt], \
                   title="charge=%s %s" % (kwds.get("charge",0), info),\
                   mode=mode)
Пример #9
0
def capped_uff_nanotube(cnt,
                        NcapN=0,
                        NcapS=0,
                        optimize_uff=1,
                        out_xyz="/tmp/cnt.xyz"):
    """
    build capped nanotube and optimize it with UFF
    """
    nC, nT = cnt.NrCapAtoms()
    print("Expected number of Thomson points nT = %s" % nT)
    print(
        "If the caps have holes or very close atoms, you should try increasing"
    )
    print(
        "or reducing the number of Thomson points (by changing the options NcapN or NcapS)"
    )
    if NcapN == 0:
        NcapN = nT
    if NcapS == 0:
        NcapS = nT
    """
    Different number of Thomson points are tested to find
    a pair (NcapN,NcapS)
    """
    dcap = []
    dnmin = 0
    dnmax = 1
    ntrial = 5
    for dnN in range(dnmin, dnmax):
        for dnS in range(dnmin, dnmax):
            # try for 5 times
            for n in range(0, ntrial):
                dcap.append((dnN, dnS))
    dcap = sorted(dcap, key=lambda a_b: abs(a_b[0]) + abs(a_b[1]))

    for (dnN, dnS) in dcap:
        print("north cap: %s points, south cap: %s points" %
              (NcapN + dnN, NcapS + dnS))
        atomlist_carbons = capped_nanotube(cnt, NcapN + dnN, NcapS + dnS)
        if optimize_uff == 1:
            print(
                "CNT will be optimized further with the Universal Force Field of G09"
            )
            tmp_dir = "/tmp"
            com_file = join(tmp_dir, "cnt_uff_opt.com")
            chk_file = join(tmp_dir, "cnt_uff_opt.chk")
            fchk_file = chk_file.replace(".chk", ".fchk")
            Gaussian.write_input(com_file, atomlist_carbons, \
                route="# UFF Opt", \
                title="Optimize (%s,%s)-Nanotube with UFF" % (cnt.n1,cnt.n2), chk=chk_file,mem=1000)
            try:
                Gaussian.run(com_file)
                # format checkpoint file
                Gaussian.formchk(chk_file, fchk_file)
                Data = Checkpoint.parseCheckpointFile(fchk_file)
                pos = Data["_Current_cartesian_coordinates"]
                atomlist_uff = XYZ.vector2atomlist(pos, atomlist_carbons)
                atomlist_carbons = atomlist_uff
            except Gaussian.GaussianError:
                print("UFF-optimization failed!")
                continue
        if check_connectivity(atomlist_carbons) == True:
            # no holes, correct connectivity
            break
        XYZ.write_xyz("/tmp/trials.xyz", [atomlist_carbons], mode="a")
    else:
        print("")

    XYZ.write_xyz(expandvars(expanduser(out_xyz)), [atomlist_carbons])
    print("Geometry of capped CNT written to %s" % out_xyz)
Пример #10
0
    def relaxed_scan(self, IJKL, nsteps, incr):
        """
        perform a relaxed scan along the internal coordinate IJKL. The coordinate is
        incremented from its initial value by `nsteps` steps of size `incr`. In each
        step the value of the scan coordinate is kept constant while all other degrees
        of freedom are relaxed.

        Parameters
        ----------
        IJKL    :  tuple of 2, 3 or 4 atom indices (starting at 0)
                   (I,J)     -   bond between atoms I and J
                   (I,J,K)   -   valence angle I-J-K
                   (I,J,K,L) -   dihedral angle between the bonds I-J, J-K and K-L
        nsteps  :  number of steps
        incr    :  increment in each step, in bohr for bond lengths, in radians
                   for angles
        """
        # length of tuple IJKL determines type of internal coordinate
        typ = len(IJKL)
        coord_type = {2: "bond", 3: "angle", 4: "dihedral"}
        conv_facs = {
            2: AtomicData.bohr_to_angs,
            3: 180.0 / np.pi,
            4: 180.0 / np.pi
        }
        units = {2: "Angs", 3: "degs", 4: "degs"}
        #
        assert self.coord_system == "internal"
        # freeze scan coordinate at its current value
        self.IC.freeze(IJKL)
        print("  ============ ")
        print("  RELAXED SCAN ")
        print("  ============ ")
        print("  The internal coordinate defined by the atom indices")
        print("    IJKL = %s  " % [I + 1 for I in IJKL])
        print("  is scanned in %d steps of size %8.5f %s." %
              (nsteps, incr * conv_facs[typ], units[typ]))

        def save_step():
            """function is called after each minimization"""
            scan_coord = self.IC.coordinate_value(xi, IJKL)

            print("current value of scan coordinate : %s" % scan_coord)

            if i == 0:
                mode = "w"
            else:
                mode = "a"
            # save relaxed geometry of step i
            XYZ.write_xyz(self.xyz_scan, [atomlist],
                          title="charge=%s energy=%s" %
                          (self.geom_kwds.get("charge", 0), self.enI),
                          mode=mode)

            # save table with energies along scan
            fh = open(self.dat_scan, mode)
            if i == 0:
                # write header
                print("# Relaxed scan along %s defined by atoms %s" %
                      (coord_type[typ], [I + 1 for I in IJKL]),
                      file=fh)
                print("# state of interest: %d" % self.state, file=fh)
                print("# ", file=fh)
                print("#  Scan coordinate     Energies ", file=fh)
                print("#    %s              Hartree " % units[typ], file=fh)

            print("  %8.5f     " % scan_coord, end=' ', file=fh)
            for en in self.energies:
                print("   %e " % en, end=' ', file=fh)
            print("", file=fh)
            fh.close()

        for i in range(0, nsteps):
            print("Step %d of relaxed scan" % i)
            # relax all other coordinates
            self.minimize()
            # optimized geometry of i-th step
            atomlist = self.getGeometry()
            xi = XYZ.atomlist2vector(atomlist)
            # save geometry
            save_step()
            # take a step of size `incr` along the scan coordinate
            xip1 = self.IC.internal_step(xi, IJKL, incr)
            # update geometry
            atomlist = XYZ.vector2atomlist(xip1, atomlist)
            self.setGeometry(atomlist, geom_kwds=self.geom_kwds)

        print("Scan geometries were written to %s" % self.xyz_scan)
        print("Table with scan energies was written to %s" % self.dat_scan)
Пример #11
0
            return gradI

        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, pes.tddftb.dftb2.getSymmetryGroup())
        thermo.calculate()

        # write vibrational modes to molden file
        molden = MoldenExporterSectioned(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
        """
        # SAMPLE INITIAL CONDITIONS FROM WIGNER DISTRIBUTION
        qs,ps = HarmonicApproximation.initial_conditions_wigner(xopt, hess, masses, Nsample=200)
        HarmonicApproximation.save_initial_conditions(atomlist, qs, ps, ".", "dynamics")
        """

    # timing
    print T
Пример #12
0
 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
Пример #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
        """
Пример #14
0
    x = XYZ.atomlist2vector(atomlist)
    pes.getEnergies(x)

    for i, atomlist in enumerate(XYZ.read_xyz(geom_file)):
        # compute electronic ground state forces with DFTB
        x = XYZ.atomlist2vector(atomlist)
        en = pes.getEnergy_S0(x)
        # total ground state energy including repulsive potential
        en_tot = en[0]
        print "Structure %d   enTot= %s Hartree" % (i, en_tot)
        # electronic energy without repulsive potential
        en_elec = pes.tddftb.dftb2.getEnergies()[2]
        gradVrep, gradE0, gradExc = pes.grads.gradient(I=0)
        # exclude repulsive potential from forces
        grad = gradE0 + gradExc
        forces = XYZ.vector2atomlist(-grad, atomlist)
        if i == 0:
            mode = 'w'
        else:
            mode = 'a'
        print >> fh_en, "%2.7f" % en_elec

        XYZ.write_xyz(
            force_file, [forces],
            title="forces with DFTB, total_energy=%2.7f  elec_energy=%2.7f" %
            (en_tot, en_elec),
            units="hartree/bohr",
            mode=mode)

    fh_en.close()
Пример #15
0
    def opt(self, max_iter=5000, step_size=1.0, gtol=0.005):
        """
        optimize MECI by following the gradient downhill

        Optional
        --------
        max_iter  :  maximum number of steps
        step_size :  The geometry is updated by making a step
                       x -> x - step_size * grad
        gtol      :  tolerance for the gradient norm
        """
        print("optimize MECI by following the gradient")
        print("  lower state :  %d" % state1)
        print("  upper state :  %d" % state2)
        print("Intermediate geometries are written to 'meci_path.xyz'")
        print("and a table with energies is written to 'meci_energies.dat'")

        # initial geometry
        x = XYZ.atomlist2vector(self.atomlist0)
        # overwrite geometries from previous run
        mode = "w"
        en_fh = open("meci_energies.dat", "w")
        print("# optimization of MECI", file=en_fh)
        print(
            "# STEP     ENERGY(1)/Hartree    ENERGY(2)/Hartree    ENERGY(3)-EPS/Hartree",
            file=en_fh)

        for i in range(0, max_iter):
            e1, e2, grad = self.getGradient(x)
            # energy gap
            en_gap = e2 - e1
            self.adjust_shift(en_gap)

            # save intermediate steps and energies
            atomlist = XYZ.vector2atomlist(x, self.atomlist0)
            XYZ.write_xyz("meci_path.xyz", [atomlist],
                          title="ENERGY= %e  GAP= %e" % (e2, en_gap),
                          mode=mode)
            print(" %4.1d      %+15.10f      %+15.10f      %+15.10f" %
                  (i, e1, e2, e2 - self.epsilon),
                  file=en_fh)
            en_fh.flush()

            # append to trajectory file
            mode = "a"
            #

            gnorm = la.norm(grad)
            print(" %4.1d    e2= %e  e2-e1= %e   |grad|= %e  (tolerance= %e)" %
                  (i, e2, en_gap, gnorm, gtol))
            if gnorm < gtol:
                break

            if self.coord_system == "cartesian":
                # descend along gradient directly in cartesian coordinates
                x -= step_size * grad
            elif self.coord_system == "internal":
                # use internal redundant coordinates
                # 1) transform cartesian to internal coordinates x -> q
                q = self.ic.cartesian2internal(x)
                # 2) transform cartesian gradient to internal coordinates
                # dE/dx -> dE/dq
                grad_intern = self.ic.transform_gradient(x, grad)
                # 3) take step along gradient in internal coordinates
                q = q - step_size * grad_intern
                # 4) deduce new cartesian coordinates from new internal coordinates q
                x = self.ic.internal2cartesian(q)

        else:
            print("exceeded maximum number of steps")

        en_fh.close()
Пример #16
0
def cartesian2internal(masses, pos, ref_atomlist, debug=0):
    """
    convert cartesian coordinates to internal coordinates + translation + rotation.
    Because global rotation and translation are included the number
    of internal coordinates equals the number of cartesian coordinates.

    Parameters:
    ===========
    masses: list with masses of each atom in atomic units
    pos: pos[3*i:3*i+3] are the cartesian positions of atom i
    ref_atomlist: list of tuples (Zi, (xi,yi,zi), positions do not have to be
       the same as in pos, only the atomic numbers are needed

    Optional:
    =========
    debug: if > 0,

    Returns:
    ========
    numpy array with zmat, center of mass and Euler angles
    """
    atomlist = XYZ.vector2atomlist(pos, ref_atomlist)
    Nat = len(atomlist)

    print("Original positions")
    print("==================")
    for i in range(0, Nat):
        print("%s   " % i, end=' ')
        print("%.4f %.4f %.4f" % tuple(pos[3*i:3*i+3]))
    cm2 = center_of_mass(masses, pos)
    pos1 = zeros(pos.shape)
    for i in range(0, Nat):
        pos1[3*i:3*i+3] = pos[3*i:3*i+3] - cm2
    (a2,b2,g2) = euler_angles_inertia(masses, pos1)
    # test transformation
    pos_std1 = molpro_standard_orientation(masses, pos)
    pos = copy(pos)
    zmat = cartesian2Zmatrix(atomlist)
    pos2 = Zmatrix2cartesian(zmat)
    pos_std2 = molpro_standard_orientation(masses, pos2)
#    XYZ.write_xyz("std1.xyz", [XYZ.vector2atomlist(pos_std1, ref_atomlist)])
#    XYZ.write_xyz("std2.xyz", [XYZ.vector2atomlist(pos_std2, ref_atomlist)])
    pos2 = pos_std2
#    R2 = inv(EulerAngles2Rotation(a2,b2,g2))
    R2 = inv(EulerAngles2Rotation(a2,b2,g2))
    posrecovered = zeros(pos.shape)
    for i in range(0, Nat):
        posrecovered[3*i:3*i+3] = dot(R2, pos2[3*i:3*i+3]) + cm2
#    XYZ.write_xyz("recovered.xyz", [XYZ.vector2atomlist(posrecovered, ref_atomlist)])
    cm3 = center_of_mass(masses, posrecovered)
    (a3,b3,g3) = euler_angles_inertia(masses, posrecovered)

    err = sum(abs(posrecovered - pos))
    assert(err < 1.0e-5)
    #
    cm = list(center_of_mass(masses, pos))
    # shift center of mass to origin
    for i in range(0, len(ref_atomlist)):
        pos[3*i:3*i+3] -= cm
    (a,b,g) = euler_angles_inertia(masses, pos)
    internal = zmat + cm + [a,b,g]
    return array(internal)
Пример #17
0
def opt(xyzfile, optionfile):
    """performs an optimization"""

    outputfile = open("output_dftb.txt", "a")  # redirect output to file
    sys.stdout = outputfile

    try:
        I = 0  # index of electronic state (ground state)

        atomlist = XYZ.read_xyz(xyzfile)[0]  # read atomlist
        kwds = XYZ.extract_keywords_xyz(
            xyzfile)  # read keywords from xyz-file (charge)
        options = read_options(optionfile)  # read options
        scf_options = extract_options(options,
                                      SCF_OPTIONLIST)  # get scf-options

        # optimization (taken from optimize.py)
        pes = MyPES(atomlist, options, Nst=max(I + 1, 2), **kwds)

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

        def f(x):
            save_xyz(x)  # also save geometries from line searches

            if I == 0 and type(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 = pes.getEnergyAndGradient_S0(x)
            else:
                energies, gradI = pes.getEnergiesAndGradient(x, I)
                enI = energies[I]
            print "E = %2.7f" % (enI)
            return enI, gradI

        xyz_trace = xyzfile.replace(".xyz", "_trace.xyz")

        # This is a callback function that is executed by numpy for each optimization step.
        # It appends the current geometry to an xyz-file.
        def save_xyz(x, mode="a"):
            atomlist_opt = XYZ.vector2atomlist(x, atomlist)
            XYZ.write_xyz(xyz_trace, [atomlist_opt],
                          title="charge=%s" % kwds.get("charge", 0),
                          mode=mode)

        save_xyz(x0, mode="w")  # write original geometry

        Nat = len(atomlist)
        min_options = {'gtol': 1.0e-7, 'norm': 2}
        # The "BFGS" method is probably better than "CG", but the line search in BFGS is expensive.
        res = optimize.minimize(f,
                                x0,
                                method="CG",
                                jac=True,
                                callback=save_xyz,
                                options=min_options)
        # res = optimize.minimize(f, x0, method="BFGS", jac=True, callback=save_xyz, options=options)
        xopt = res.x
        save_xyz(xopt)

        print "Intermediate geometries written into file {}".format(xyz_trace)

        # write optimized geometry into file
        atomlist_opt = XYZ.vector2atomlist(xopt, atomlist)
        xyz_opt = xyzfile.replace(".xyz", "_opt.xyz")
        XYZ.write_xyz(xyz_opt, [atomlist_opt],
                      title="charge=%s" % kwds.get("charge", 0),
                      mode="w")

        # calculate energy for optimized geometry
        dftb2 = DFTB2(atomlist_opt, **options)  # create dftb object
        dftb2.setGeometry(atomlist_opt, charge=kwds.get("charge", 0.0))

        dftb2.getEnergy(**scf_options)
        energies = list(dftb2.getEnergies())  # get partial energies

        if dftb2.long_range_correction == 1:  # add long range correction to partial energies
            energies.append(dftb2.E_HF_x)

        return str(energies)

    except:
        print sys.exc_info()
        return "error"
Пример #18
0
 def setInternalCoordinates(self, internal):
     pos = internal2cartesian(self.masses, internal)
     self.atomlist = XYZ.vector2atomlist(pos, self.atomlist)
Пример #19
0
    import sys

    atomlist0 = XYZ.read_xyz(sys.argv[1])[-1]
    x0 = XYZ.atomlist2vector(atomlist0)

    IC = InternalValenceCoords(atomlist0, verbose=3)

    test_wilson_bmatrix(IC.force_field, x0)

    # 1) transform cartesian to internal coordinates, x0 ~ q0
    q0 = IC.cartesian2internal(x0)

    # 2) Take a step along the last internal coordinate,
    # this will only work if the coordinate is not coupled
    # to any other.
    dq = np.zeros(len(q0))
    dq[-1] = 0.1
    q = q0 + dq

    # 3) and transform back x ~ q
    x = IC.internal2cartesian(q)

    # veriy that really x corresponds to internal coordinate q
    q_test = IC.cartesian2internal(x)
    err = la.norm(q - q_test)
    #    assert err < 1.0e-10, "|q(x(q)) - q|= %e" % err

    # 4) save initial and displaced geometries
    atomlist = XYZ.vector2atomlist(x, atomlist0)
    XYZ.write_xyz("/tmp/test_internal_coords.xyz", [atomlist0, atomlist])
Пример #20
0
    parser = optparse.OptionParser(usage)
    parser.add_option("--n1", dest="n1", help="Chiral index n1 [default: %default]", default=6, type=int)
    parser.add_option("--n2", dest="n2", help="Chiral index n2 [default: %default]", default=5, type=int)
    parser.add_option("--L", dest="L", help="Length of CNT in bohr [default: %default]", default=100.0, type=int)
    parser.add_option("--NcapN", dest="NcapN", help="Number of Thomson points on the northern cap, if set to 0 the number is estimated from the density of carbon atoms in graphene [default: %default]", default=0, type=int)
    parser.add_option("--NcapS", dest="NcapS", help="Number of Thomson points on the southern cap, if set to 0 the number is estimated from the density of carbon atoms in graphene [default: %default]", default=0, type=int)
    parser.add_option("--out_xyz", dest="out_xyz", help="Save the CNT geometry to this xyz-file [default: %default]", default="/tmp/cnt.xyz")
    parser.add_option("--optimize_uff", dest="optimize_uff", help="Optimize the built CNT with the UFF force field. This requires that the program g09 is installed. [default: %default]", default=1, type=int)

    (opts,args) = parser.parse_args()

    atomlist_carbons = capped_nanotube(opts.n1,opts.n2,opts.L,NcapN=opts.NcapN, NcapS=opts.NcapS)
    if opts.optimize_uff == 1:
        print("CNT will be optimized further with the Universal Force Field of G09")
        tmp_dir="/tmp"
        com_file = join(tmp_dir, "cnt_uff_opt.com")
        chk_file = join(tmp_dir, "cnt_uff_opt.chk")
        fchk_file = chk_file.replace(".chk", ".fchk")
        Gaussian.write_input(com_file, atomlist_carbons, \
            route="# UFF Opt", title="Optimize (%s,%s)-Nanotube with UFF" % (opts.n1,opts.n2), chk=chk_file,mem=1000)
        Gaussian.run(com_file)
        # format checkpoint file
        Gaussian.formchk(chk_file, fchk_file)
        Data = Checkpoint.parseCheckpointFile(fchk_file)
        pos = Data["_Current_cartesian_coordinates"]
        atomlist_uff = XYZ.vector2atomlist(pos, atomlist_carbons)
        atomlist_carbons = atomlist_uff

    XYZ.write_xyz(opts.out_xyz, [atomlist_carbons])
    print("Geometry of capped CNT written to %s" % opts.out_xyz)
Пример #21
0
                ps[3 * A:3 * (A + 1), i] *= 0.001
    #
    # STATISTICS
    # kinetic energy
    Ekin = np.zeros(opts.Nsample)
    for i in range(0, opts.Nsample):
        Ekin[i] = np.sum(ps[:, i]**2 / (2 * np.array(masses)))
    # temperature  (3*N-6)/2 k T = Ekin
    f = len(masses) - 6  # degrees of freedom minus rotation and translation
    T = Ekin * 2.0 / (f * AtomicData.kBoltzmann)
    print "Initial Condition Statistics"
    print "============================"
    print "averages are over %d trajectories" % opts.Nsample
    print "kinetic energy = (%2.7f +- %2.7f) Hartree" % (np.mean(Ekin),
                                                         np.std(Ekin))
    print "temperature    = (%2.7f +- %2.7f) Kelvin" % (np.mean(T), np.std(T))
    print ""

    # geometries only
    wigner_xyz = join(opts.outdir, "wigner.xyz")
    geometries = [
        XYZ.vector2atomlist(qs[:, i], atomlist)
        for i in range(0, opts.Nsample)
    ]
    XYZ.write_xyz(wigner_xyz, geometries)
    print "Wigner ensemble written to file %s" % wigner_xyz

    # initial conditions
    HarmonicApproximation.save_initial_conditions(atomlist, qs, ps,
                                                  opts.outdir, "dynamics")
    atomlist0 = XYZ.read_xyz(xyz0)[0]
    x0 = XYZ.atomlist2vector(atomlist0)

    # read final geometry
    atomlist1 = XYZ.read_xyz(xyz1)[0]
    x1 = XYZ.atomlist2vector(atomlist1)

    # interpolation parameter
    rs = np.linspace(0.0, 1.0, N)

    geometries_interp = []

    if opts.coord_system == "cartesian":
        for r in rs:
            xr = x0 + r * (x1 - x0)
            geometries_interp.append(XYZ.vector2atomlist(xr, atomlist0))
    elif opts.coord_system == "internal":
        IC = InternalValenceCoords(atomlist0)
        # initial and final geometry in internal coordinates
        q1 = IC.cartesian2internal(x1)
        q0 = IC.cartesian2internal(x0)
        for r in rs:
            qr = q0 + r * (q1 - q0)
            xr = IC.internal2cartesian(qr)
            geometries_interp.append(XYZ.vector2atomlist(xr, atomlist0))
    else:
        raise ValueError(
            "Coordinate system '%s' not understood, valid options are 'internal' and 'cartesian'"
            % opts.coord_system)

    XYZ.write_xyz(xyz_interp, geometries_interp)