Example #1
0
    def __init__(self,
                 calculator=None,
                 nbr_of_evals=5,
                 terms=["pair", "T1", "T2", "T3", "T4", "T5", "T6", "T7"],
                 r_cut=None,
                 mu=None,
                 mu_c=None,
                 dim=3,
                 c_stab=0.1,
                 force_stab=False,
                 reinitialize=False,
                 array_convention='C',
                 solver="auto",
                 solve_tol=1e-9,
                 apply_positions=True,
                 apply_cell=True,
                 logfile=None):
        """Initialise an Hessian_EAM preconditioner with given parameters.

        Args:
            calculator: matscipy eam calculator
                The calculator that calculates the hessian of the system.
            nbr_of_evals: int
                The number of evaluation after the matrix is calculated.
            terms: list of strings
                The list which terms of the analytic eam hessian should be
                calculated.
            r_cut, mu, c_stab, dim, recalc_mu, array_convention: see
                precon.__init__()
        """
        super().__init__(r_cut=r_cut,
                         mu=mu,
                         mu_c=mu_c,
                         dim=dim,
                         c_stab=c_stab,
                         force_stab=force_stab,
                         reinitialize=reinitialize,
                         array_convention=array_convention,
                         solver=solver,
                         solve_tol=solve_tol,
                         apply_positions=apply_positions,
                         apply_cell=apply_cell,
                         logfile=logfile)

        self.calculator = calculator
        self.terms = terms
        self.nbr_of_evals = nbr_of_evals
        self.ctr_of_evals = 0
        self.exp_precon = Exp()
        self.fmax = float('inf')
Example #2
0
def test_precon_amin():
    cu0 = bulk("Cu") * (2, 2, 2)
    sigma = cu0.get_distance(0, 1) * (2.**(-1. / 6))
    lj = LennardJones(sigma=sigma)

    # perturb the cell
    cell = cu0.get_cell()
    cell *= 0.95
    cell[1, 0] += 0.2
    cell[2, 1] += 0.5
    cu0.set_cell(cell, scale_atoms=True)

    energies = []
    for use_armijo in [True, False]:
        for a_min in [None, 1e-3]:
            atoms = cu0.copy()
            atoms.calc = lj
            opt = PreconLBFGS(atoms,
                              precon=Exp(A=3),
                              use_armijo=use_armijo,
                              a_min=a_min,
                              variable_cell=True)
            opt.run(fmax=1e-3, smax=1e-4)
            energies.append(atoms.get_potential_energy())

    # check we get the expected energy for all methods
    assert np.abs(np.array(energies) - -63.5032311942).max() < 1e-4
Example #3
0
def main(argv):
    fname = argv[0]
    if (int(argv[1]) == 1):
        variable_cell = True
    else:
        variable_cell = False
    atoms = read(fname)
    #atoms = bulk("Al")

    calc = gp.GPAW(mode=gp.PW(500), kpts=(12, 12, 12), xc="PBE", nbands=-20)
    atoms.set_calculator(calc)
    prc = Exp(mu=1.0, mu_c=1.0)
    outfname = fname.split("/")[-1]
    outfname = outfname.split(".")[0]
    logfile = outfname + ".log"
    traj_file = outfname + ".traj"
    relaxer = PreconLBFGS(atoms,
                          logfile=logfile,
                          precon=prc,
                          use_armijo=True,
                          variable_cell=variable_cell)
    trajObj = Trajectory(traj_file, 'w', atoms)
    relaxer.attach(trajObj)
    if (variable_cell):
        relaxer.run(fmax=0.025, smax=0.003)
    else:
        relaxer.run(fmax=0.025)
    outfname = fname.split("/")[-1]
    outfname = outfname.split(".")[0]
    outfname += "_relaxed.xyz"
    print("Energy: {}".format(atoms.get_potential_energy()))
    write(outfname, atoms)
Example #4
0
def test_single_precon_initialisation(setup_images):
    images, _, _ = setup_images
    precon = Exp()
    mep = NEB(images, method='spline', precon=precon)
    mep.get_forces()
    assert len(mep.precon) == len(mep.images)
    assert mep.precon[0].mu == mep.precon[1].mu
Example #5
0
def relax(runID):
    db = connect(db_name)
    atoms = db.get_atoms(id=runID)

    con = sq.connect(db_name)
    cur = con.cursor()
    cur.execute("SELECT value FROM text_key_values WHERE id=? AND key='name'",
                (runID, ))
    name = cur.fetchone()[0]
    con.close()

    calc = gp.GPAW(mode=gp.PW(600),
                   xc="PBE",
                   kpts=(4, 4, 4),
                   nbands="120%",
                   symmetry="off")
    atoms.set_calculator(calc)
    precon = Exp(mu=1.0, mu_c=1.0)
    save_to_db = SaveToDB(db_name, runID, name)
    logfile = "logfile%d.log" % (runID)
    traj = "trajectory%d.traj" % (runID)
    uf = UnitCellFilter(atoms, hydrostatic_strain=True)
    relaxer = PreconLBFGS(uf, logfile=logfile, use_armijo=True, precon=precon)
    relaxer.attach(save_to_db, interval=1, atoms=atoms)
    trajObj = Trajectory(traj, "w", atoms)
    relaxer.attach(trajObj)
    relaxer.run(fmax=0.05, smax=0.003)
Example #6
0
def test_precon():
    cu0 = bulk("Cu") * (2, 2, 2)
    lj = LennardJones(sigma=cu0.get_distance(0,1))

    cu = cu0.copy()
    cu.set_cell(1.2*cu.get_cell())
    cu.calc = lj
    ucf = UnitCellFilter(cu, constant_volume=True)
    opt = PreconLBFGS(ucf, precon=Exp(mu=1.0, mu_c=1.0))
    opt.run(fmax=1e-3)
    assert abs(np.linalg.det(cu.cell)/np.linalg.det(cu0.cell) - 1.2**3) < 1e-3

    # EcpCellFilter allows relaxing to lower tolerance
    cu = cu0.copy()
    cu.set_cell(1.2*cu.get_cell())
    cu.calc = lj
    ecf = ExpCellFilter(cu, constant_volume=True)
    opt = PreconLBFGS(ecf, precon=Exp(mu=1.0, mu_c=1.0))
    opt.run(fmax=1e-3)
    assert abs(np.linalg.det(cu.cell)/np.linalg.det(cu0.cell) - 1.2**3) < 1e-7
def test_linesearch_maxstep():
    import numpy as np
    from ase import Atoms
    from ase.calculators.emt import EMT
    from ase.optimize import BFGS, BFGSLineSearch
    from ase.optimize.precon import Exp, PreconLBFGS

    positions = [
        [5.8324672234339969, 8.5510800490537271, 5.686535793302002],
        [7.3587688835494625, 5.646353802990923, 6.8378173997818958],
        [7.9908510609316235, 5.4456005797117335, 4.5249260246251213],
        [9.7103024117445145, 6.4768915365291466, 4.6502022197421278],
        [9.5232482249292509, 8.7417754382952051, 4.6747936030744448],
        [8.2738330473112036, 7.640248516254645, 6.1624124370797215],
        [7.4198265919217921, 9.2882534361810016, 4.3654132356242874],
        [6.8506783463494623, 9.2004422130272605, 8.611538688631887],
        [5.9081131977596133, 5.6951755645279949, 5.4134092632199602],
        [9.356736354387575, 9.2718534012646359, 8.491942486888524],
        [9.0390271264592403, 9.5752757925665453, 6.4771649275571779],
        [7.0554382804264533, 7.0016335250680779, 8.418151938177477],
        [9.4855926945401272, 5.5650406772147694, 6.8445655410690591],
    ]
    atoms = Atoms('Pt13', positions=positions, cell=[15] * 3)

    maxstep = 0.2
    longest_steps = []

    labels = [
        'BFGS', 'BFGSLineSearch', 'PreconLBFGS_Armijo', 'PreconLBFGS_Wolff'
    ]
    optimizers = [BFGS, BFGSLineSearch, PreconLBFGS, PreconLBFGS]

    for i, Optimizer in enumerate(optimizers):
        a = atoms.copy()
        a.set_calculator(EMT())

        kwargs = {'maxstep': maxstep, 'logfile': None}
        if 'Precon' in labels[i]:
            kwargs['precon'] = Exp(A=3)
            kwargs['use_armijo'] = 'Armijo' in labels[i]

        opt = Optimizer(a, **kwargs)
        opt.run(steps=1)

        dr = a.get_positions() - positions
        steplengths = (dr**2).sum(1)**0.5
        longest_step = np.max(steplengths)

        print('%s: longest step = %.4f' % (labels[i], longest_step))
        longest_steps.append(longest_step)

    longest_steps = np.array(longest_steps)
    assert (longest_steps < maxstep + 1e-8).all()
Example #8
0
def test_preconlbfgs():
    N = 1
    a0 = bulk('Cu', cubic=True)
    a0 *= (N, N, N)

    # perturb the atoms
    s = a0.get_scaled_positions()
    s[:, 0] *= 0.995
    a0.set_scaled_positions(s)

    nsteps = []
    energies = []
    for OPT in [PreconLBFGS, PreconFIRE]:
        for precon in [None, Exp(A=3, mu=1.0)]:
            atoms = a0.copy()
            atoms.calc = EMT()
            opt = OPT(atoms, precon=precon, use_armijo=True)
            opt.run(1e-4)
            energies += [atoms.get_potential_energy()]
            nsteps += [opt.get_number_of_steps()]

    # check we get the expected energy for all methods
    assert np.abs(np.array(energies) - -0.022726045433998365).max() < 1e-4

    # test with fixed bondlength and fixed atom constraints
    cu0 = bulk("Cu") * (2, 2, 2)
    cu0.rattle(0.01)
    a0 = cu0.get_distance(0, 1)
    cons = [FixBondLength(0, 1), FixAtoms([2, 3])]
    for precon in [None, Exp(mu=1.0)]:
        cu = cu0.copy()
        cu.calc = EMT()
        cu.set_distance(0, 1, a0 * 1.2)
        cu.set_constraint(cons)
        opt = PreconLBFGS(cu, precon=precon, use_armijo=True)
        opt.run(fmax=1e-3)

        assert abs(cu.get_distance(0, 1) / a0 - 1.2) < 1e-3
        assert np.all(abs(cu.positions[2] - cu0.positions[2]) < 1e-3)
        assert np.all(abs(cu.positions[3] - cu0.positions[3]) < 1e-3)
def test_linesearch(optcls, name, atoms, positions):
    maxstep = 0.2
    kwargs = {'maxstep': maxstep, 'logfile': None}
    if 'Precon' in name:
        kwargs['precon'] = Exp(A=3)
        kwargs['use_armijo'] = 'Armijo' in name

    opt = optcls(atoms, **kwargs)
    opt.run(steps=1)

    dr = atoms.get_positions() - positions
    steplengths = (dr**2).sum(1)**0.5
    longest_step = np.max(steplengths)
    assert longest_step < maxstep + 1e-8
Example #10
0
    def optimise_positions(self, fmax=0.01, use_precon=None, use_armijo=None):
        """ Relax atoms positions with the fixed cell to a given force
        threshold """
        if use_precon is None:
            use_precon = self.parameters.use_precon
        if use_armijo is None:
            use_armijo = self.parameters.use_armijo

        if use_precon:
            precon = Exp(A=3, use_pyamg=False)
        else:
            precon = None
        if self.parameters.optimizer == 'BFGS':
            relax = BFGS(self.atoms)
        elif self.parameters.optimizer == 'FIRE':
            relax = PreconFIRE(self.atoms, precon=precon)
        elif self.parameters.optimizer == 'ase-FIRE':
            relax = ASEFIRE(self.atoms)
        elif self.parameters.optimizer == 'LBFGS':
            relax = PreconLBFGS(self.atoms,
                                precon=precon,
                                use_armijo=use_armijo)
        elif self.parameters.optimizer == 'ase-LBFGS':
            relax = ASELBFGS(self.atoms)
        else:
            parprint("ERROR: unknown optimizer {}. "
                     "Reverting to BFGS".format(self.parameters.optimizer))
            relax = BFGS(self.atoms)
        name = self.atoms.get_chemical_formula()
        relax.attach(
            lambda: self.atoms.calc.write(name + '_relax.gpw', mode='all'))
        relax.attach(lambda: write_atat_input(self.atoms, 'str_last.out'))
        relax.run(fmax=fmax, steps=100)
        if not relax.converged():
            relax = ASELBFGS(self.atoms)
            relax.run(fmax=fmax, steps=100)
            if not relax.converged():
                max_force = self.atoms.get_forces()
                max_force = np.sqrt((max_force**2).sum(axis=1).max())
                print('WARNING: optimisation not converged.' +
                      ' Maximum force: %.4f' % max_force)
Example #11
0
def run_dftb_precon(folder, param_file):
    """ Run DFTB+ using a preconditioned optimizer

    Unlike a regular DFTB+ run a result.tag file is not produced because ASE
    deletes the file. Instead we dump the energy and forces to a json file so
    we cna load it back in later.

    Args:
        folder (str): directory containing a structure to optimize
        param_file (str): path to the parameter file for this structure.
    """

    if param_file is None:
        raise RuntimeError("A parameter file must be supplied to run \
                           batch_dftb+ in precon mode")

    hsd_file = os.path.join(folder, 'dftb_pin.hsd')
    file_name = 'dftb_pin.hsd' if os.path.isfile(hsd_file) else 'geo_end.gen'
    atoms = io.read(os.path.join(folder, file_name))
    params = load_input_file(param_file)
    calc = make_dftb_calc(atoms, folder, params)
    atoms.set_calculator(calc)

    try:
        opt = PreconLBFGS(atoms,
                          precon=Exp(A=3),
                          use_armijo=True,
                          logfile=None)
        opt.run(steps=int(params['geom_steps']))
    except:
        return

    results = {
        'energy': calc.get_potential_energy(),
        'forces': calc.get_forces().tolist()
    }

    json.dump(results, open(os.path.join(folder, "results.json"), 'w'))
Example #12
0
def do_short_relax(atoms, index=None, vc_relax=False, precon=True,
                   maxsteps=20):
    ''' Performs a (usually short) local optimization.

    atoms: an Atoms object 
    index: index to be used as suffix for the output files
    vc_relax: whether to also optimize the cell vectors
              (after having run several steps with fixed cell)
    precon: whether to use the preconditioned optimizers
    maxsteps: maximum number of ionic steps
    '''
    if vc_relax:
        assert precon

    t = time()
    label = 'opt' if index is None else 'opt_' + str(index)
    logfile = '%s.log' % label
    trajfile = '%s.traj' % label

    traj = Trajectory(trajfile, 'a', atoms)
    nsteps = 0
    maxsteps_no_vc = maxsteps / 2 if vc_relax else maxsteps
    fmax = 2. if vc_relax else 0.1

    try:
        if precon:
            dyn = PreconLBFGS_My(atoms, precon=Exp(A=3), variable_cell=False,
                                 use_armijo=True, a_min=1e-2, logfile=logfile)
        else:
            dyn = BFGS(atoms, maxstep=0.4, logfile=logfile)
        dyn.attach(traj)
        dyn.run(fmax=fmax, steps=maxsteps_no_vc)
    except RuntimeError:
        nsteps += dyn.get_number_of_steps()
        if precon:
            dyn = PreconFIRE_My(atoms, precon=Exp(A=3), variable_cell=False,
                                use_armijo=False, logfile=logfile,
                                dt=0.1, maxmove=0.5, dtmax=1.0, finc=1.1)
        else:
            dyn = FIRE(atoms, logfile=logfile, dt=0.1, maxmove=0.5, dtmax=1.0,
                       finc=1.1)
        dyn.attach(traj)
        steps = maxsteps_no_vc - nsteps
        dyn.run(fmax=fmax, steps=steps)

    nsteps += dyn.get_number_of_steps()

    if vc_relax:
        L = atoms.get_volume() / 4.  # largest cell vector length allowed
        cellbounds = CellBounds(bounds={'phi':[20., 160.], 'a':[1.5, L],
                                        'chi':[20., 160.], 'b':[1.5, L],
                                        'psi':[20., 160.], 'c':[1.5, L]})
        try:
            dyn = PreconLBFGS_My(atoms, precon=Exp(A=3), variable_cell=True,
                                 use_armijo=True, logfile=logfile,
                                 cellbounds=cellbounds, a_min=1e-2)
            dyn.e1 = None
            try:
                dyn._just_reset_hessian
            except AttributeError:
                dyn._just_reset_hessian = True
            dyn.attach(traj)
            steps = maxsteps - nsteps
            dyn.run(fmax=0., smax=0., steps=steps)
        except RuntimeError:
            nsteps += dyn.get_number_of_steps()
            dyn = PreconFIRE_My(atoms, precon=Exp(A=3), variable_cell=True,
                                use_armijo=False, logfile=logfile,
                                cellbounds=cellbounds,
                                dt=0.1, maxmove=0.5, dtmax=1.0, finc=1.1)
            dyn.attach(traj)
            steps = maxsteps - nsteps
            dyn.run(fmax=0., steps=steps)

    name = atoms.calc.name
    print('%s relaxation took %.3f seconds' % (name, time()-t))
    return atoms
Example #13
0
import numpy as np

from ase.build import bulk
from ase.calculators.lj import LennardJones
from ase.optimize.precon import PreconLBFGS, Exp
from ase.constraints import UnitCellFilter

cu0 = bulk("Cu") * (2, 2, 2)
lj = LennardJones(sigma=cu0.get_distance(0, 1))

cu = cu0.copy()
cu.set_cell(1.2 * cu.get_cell())
cu.set_calculator(lj)

ucf = UnitCellFilter(cu, constant_volume=True)
opt = PreconLBFGS(ucf, precon=Exp(mu=1.0, mu_c=1.0))
opt.run(fmax=1e-3)

assert abs(np.linalg.det(cu.cell) / np.linalg.det(cu0.cell) - 1.2**3) < 1e-3
Example #14
0
        calc = GPAW(mode=PW(e_cut),
                    nbands=nbands,
                    xc='PBE',
                    spinpol=True,
                    kpts=(k_pts, k_pts, k_pts),
                    occupations=FermiDirac(smear),
                    txt=system_name + '.out')

        bulk_mat.set_calculator(calc)
        #Save the initial state of the calculations
        #calc.write('Fe_relaxer_initial.gpw')
        save_atoms(bulk_mat, e_cut, nbands, k_pts, smear, a, initial_magmom, 1,
                   str(sys.argv[6]))

    precon = Exp()
    saver = Gpw_save(calc, system_name + '_relaxed.gpw')
    traj = Trajectory(system_name + '_relaxed.traj', 'w', bulk_mat)
    relaxer = PreconFire(bulk_mat,
                         precon=precon,
                         variable_cell=True,
                         logfile=system_name + '.txt')
    relaxer.attach(traj)
    relaxer.attach(saver)
    relaxer.run(fmax=0.025, smax=0.003)
    bulk_mat.get_potential_energy()

    #Save the final state of the calculations
    calc.write(system_name + '_relaxer_final.gpw')
    save_atoms(bulk_mat, e_cut, nbands, k_pts, smear, a, initial_magmom, 0,
               str(sys.argv[6]))
Example #15
0
    def __init__(self,
                 atoms,
                 restart=None,
                 logfile='-',
                 trajectory=None,
                 maxstep=None,
                 memory=100,
                 damping=1.0,
                 alpha=70.0,
                 master=None,
                 precon='auto',
                 variable_cell=False,
                 use_armijo=True,
                 c1=0.23,
                 c2=0.46,
                 a_min=None,
                 rigid_units=None,
                 rotation_factors=None,
                 Hinv=None):
        """Parameters:

        atoms: Atoms object
            The Atoms object to relax.

        restart: string
            Pickle file used to store vectors for updating the inverse of
            Hessian matrix. If set, file with such a name will be searched
            and information stored will be used, if the file exists.

        logfile: file object or str
            If *logfile* is a string, a file with that name will be opened.
            Use '-' for stdout.

        trajectory: string
            Pickle file used to store trajectory of atomic movement.

        maxstep: float
            How far is a single atom allowed to move. This is useful for DFT
            calculations where wavefunctions can be reused if steps are small.
            Default is 0.04 Angstrom.

        memory: int
            Number of steps to be stored. Default value is 100. Three numpy
            arrays of this length containing floats are stored.

        damping: float
            The calculated step is multiplied with this number before added to
            the positions.

        alpha: float
            Initial guess for the Hessian (curvature of energy surface). A
            conservative value of 70.0 is the default, but number of needed
            steps to converge might be less if a lower value is used. However,
            a lower value also means risk of instability.

        master: boolean
            Defaults to None, which causes only rank 0 to save files.  If
            set to true,  this rank will save files.

        precon: ase.optimize.precon.Precon instance or compatible.
            Apply the given preconditioner during optimization. Defaults to
            'auto', which will choose the `Exp` preconditioner unless the system
            is too small (< 100 atoms) in which case a standard LBFGS fallback
            is used. To enforce use of the `Exp` preconditioner, use `precon =
            'Exp'`. Other options include 'C1', 'Pfrommer' and 'FF' - see the
            corresponding classes in the `ase.optimize.precon` module for more
            details. Pass precon=None or precon='ID' to disable preconditioning.

        use_armijo: boolean
            Enforce only the Armijo condition of sufficient decrease of
            of the energy, and not the second Wolff condition for the forces.
            Often significantly faster than full Wolff linesearch.
            Defaults to True.

        c1: float
            c1 parameter for the line search. Default is c1=0.23.

        c2: float
            c2 parameter for the line search. Default is c2=0.46.

        a_min: float
            minimal value for the line search step parameter. Default is
            a_min=1e-8 (use_armijo=False) or 1e-10 (use_armijo=True).
            Higher values can be useful to avoid performing many
            line searches for comparatively small changes in geometry.

        variable_cell: bool
            If True, wrap atoms an ase.constraints.UnitCellFilter to
            relax both postions and cell. Default is False.

        rigid_units: each I = rigid_units[i] is a list of indices, which
            describes a subsystem of atoms that forms a (near-)rigid unit
            If rigid_units is not None, then special search-paths are
            are created to take the rigidness into account

        rotation_factors: list of scalars; acceleration factors deteriming
           the rate of rotation as opposed to the rate of stretch in the
           rigid units
        """
        if variable_cell:
            atoms = UnitCellFilter(atoms)
        Optimizer.__init__(self, atoms, restart, logfile, trajectory, master)

        # default preconditioner
        #   TODO: introduce a heuristic for different choices of preconditioners
        if precon == 'auto':
            if len(atoms) < 100:
                precon = None
                warnings.warn(
                    'The system is likely too small to benefit from ' +
                    'the standard preconditioner, hence it is ' +
                    'disabled. To re-enable preconditioning, call' +
                    '`PreconLBFGS` by explicitly providing the ' +
                    'kwarg `precon`')
            else:
                precon = 'Exp'

        if maxstep is not None:
            if maxstep > 1.0:
                raise ValueError('You are using a much too large value for ' +
                                 'the maximum step size: %.1f Angstrom' %
                                 maxstep)
            self.maxstep = maxstep
        else:
            self.maxstep = 0.04

        self.memory = memory
        self.H0 = 1. / alpha  # Initial approximation of inverse Hessian
        # 1./70. is to emulate the behaviour of BFGS
        # Note that this is never changed!
        self.Hinv = Hinv
        self.damping = damping
        self.p = None

        # construct preconditioner if passed as a string
        if isinstance(precon, basestring):
            if precon == 'C1':
                precon = C1()
            if precon == 'Exp':
                precon = Exp()
            elif precon == 'Pfrommer':
                precon = Pfrommer()
            elif precon == 'ID':
                precon = None
            else:
                raise ValueError('Unknown preconditioner "{0}"'.format(precon))
        self.precon = precon
        self.use_armijo = use_armijo
        self.c1 = c1
        self.c2 = c2
        self.a_min = a_min
        if self.a_min is None:
            self.a_min = 1e-10 if use_armijo else 1e-8

        # CO
        self.rigid_units = rigid_units
        self.rotation_factors = rotation_factors
Example #16
0
def relax_precon(atoms,
                 calc,
                 fmax=5e-2,
                 smax=1e-4,
                 variable_cell=False,
                 trajfile=None,
                 logfile=None,
                 maxsteps=10000,
                 verbose=True,
                 dEmin=1e-3,
                 a_min=1e-8,
                 optimizer='LBFGS',
                 cellbounds=None,
                 fix_bond_lengths_pairs=None,
                 debug=False):
    """ Locally optimizes the geometry using the preconditioned optimizers.
    
    atoms: the Atoms object
    calc: a calculator instance to attach to the Atoms object
    fmax: the force convergence criterion (in eV/Angstrom)
    smax: the stress convergence criterion (in eV/Angstrom^3)
    variable_cell: whether to also optimize the cell vectors
    trajfile: filename of the trajectory to attach to the optimizers
    logfile: filename of the logfile for the optimizers
    maxsteps: maximum allowed total number of ionic steps
    verbose: whether to print output or remain silent
    dEmin: minimum (absolute) energy difference for the main loop
           iterations; if the energy difference is smaller, the
           minimization is aborted
    a_min: minimal value for the linesearch parameter in the
           PreconLBFGS optimizer
    optimizer: 'LBFGS' or 'FIRE', the preferred type of
               (preconditioned) optimizer
    cellbounds: ase.ga.bulk_utilities.CellBounds instance for
                setting bounds on the cell shape and size
                during variable-cell relaxation
    fix_bond_lengths_pairs: list of (i,j) tuples of atom pairs
                for which the (minimum image) bond distance
                will be kept fixed
    debug: if True, extra output may be printed to stdout
    """
    assert optimizer in ['LBFGS', 'FIRE']

    class UnitCellFilterFixBondLengthsWrapper(UnitCellFilterFixBondLengths):
        """ Wrapper to nitCellFilterFixBondLengths,
        required because of ase.optimize.precon.estimate_mu.
        """
        def __init__(self, atoms):
            UnitCellFilterFixBondLengths.__init__(self,
                                                  atoms,
                                                  maxiter=1000,
                                                  pairs=fix_bond_lengths_pairs)

    if variable_cell:
        atoms.set_pbc(True)
    else:
        smax = None
        cellbounds = None

    atoms.wrap()
    atoms.set_calculator(calc)
    nsteps = 0

    if trajfile is not None:
        if os.path.exists(trajfile):
            os.remove(trajfile)
        traj = Trajectory(trajfile, 'a', atoms)
    else:
        traj = None

    if fix_bond_lengths_pairs is not None:
        original_constraints = [c for c in atoms.constraints]
        bond_lengths = np.zeros(len(fix_bond_lengths_pairs))
        for i, ab in enumerate(fix_bond_lengths_pairs):
            bond_lengths[i] = atoms.get_distance(ab[0], ab[1], mic=True)
        fbl_constraint = FixBondLengths(fix_bond_lengths_pairs)
        atoms.set_constraint(original_constraints + [fbl_constraint])

    if variable_cell:
        # Start with partial relaxation using fixed cell vectors
        if fix_bond_lengths_pairs or optimizer == 'FIRE':
            # PreconFIRE is more suitable in this case
            dyn = PreconFIRE_My(atoms,
                                precon=Exp(A=3),
                                use_armijo=True,
                                variable_cell=False,
                                logfile=logfile)
        else:
            dyn = PreconLBFGS_My(atoms,
                                 precon=Exp(A=3),
                                 use_armijo=True,
                                 variable_cell=False,
                                 logfile=logfile,
                                 maxstep=0.2,
                                 a_min=a_min)
        attach1 = VarianceError(atoms, dyn, N=5)
        dyn.attach(attach1)
        attach2 = DivergenceError(atoms, dyn, N=2, max_slope=0.05)
        dyn.attach(attach2)
        if traj is not None:
            dyn.attach(traj)
        try:
            dyn.run(fmax=1., steps=25)
        except RuntimeError as err:
            if debug:
                print(err.message, flush=True)
        nsteps += dyn.get_number_of_steps()

    if variable_cell and fix_bond_lengths_pairs is not None:
        # Fixing bond lengths will be taken care of
        # by the modified UnitCellFilter
        atoms.set_constraint(original_constraints)
        atoms = UnitCellFilterFixBondLengthsWrapper(atoms)
    elif variable_cell:
        atoms = UnitCellFilter(atoms)

    steps = 30
    niter = 0
    maxiter = 50
    nerr = 0
    maxerr = 10
    e_prev = 1e64

    while nsteps < maxsteps and niter < maxiter and nerr < maxerr:
        e = atoms.get_potential_energy()
        if abs(e - e_prev) < dEmin:
            break
        e_prev = e

        try:
            if optimizer == 'LBFGS':
                dyn = PreconLBFGS_My(atoms,
                                     precon=Exp(A=3),
                                     use_armijo=True,
                                     logfile=logfile,
                                     a_min=a_min,
                                     variable_cell=False,
                                     maxstep=0.2,
                                     cellbounds=cellbounds)
            elif optimizer == 'FIRE':
                dyn = PreconFIRE_My(atoms,
                                    precon=Exp(A=3),
                                    use_armijo=True,
                                    variable_cell=False,
                                    logfile=logfile,
                                    maxmove=0.5,
                                    cellbounds=cellbounds)
            dyn.e1 = None
            try:
                dyn._just_reset_hessian
            except AttributeError:
                dyn._just_reset_hessian = True
            if traj is not None:
                dyn.attach(traj)
            attach1 = VarianceError(atoms, dyn, N=5)
            dyn.attach(attach1)
            attach2 = DivergenceError(atoms, dyn, N=2, max_slope=0.05)
            dyn.attach(attach2)
            dyn.run(fmax=fmax, smax=smax, steps=steps)
            nsteps += dyn.get_number_of_steps()
        except RuntimeError as err:
            if debug:
                print(err.message, flush=True)
            nerr += 1
            nsteps += dyn.get_number_of_steps()
            dyn = PreconFIRE_My(atoms,
                                precon=Exp(A=3),
                                use_armijo=False,
                                variable_cell=False,
                                logfile=logfile,
                                maxmove=0.5,
                                cellbounds=cellbounds)
            if traj is not None:
                dyn.attach(traj)
            attach1 = VarianceError(atoms, dyn, N=5)
            dyn.attach(attach1)
            attach2 = DivergenceError(atoms, dyn, N=2, max_slope=0.05)
            dyn.attach(attach2)
            try:
                dyn.run(fmax=fmax, smax=smax, steps=steps)
            except RuntimeError as err:
                if debug:
                    print(err.message, flush=True)
                nerr += 1
            nsteps += dyn.get_number_of_steps()

        niter += 1

        try:
            if dyn.converged():
                break
        except RuntimeError:
            break

        if isinstance(atoms, UnitCellFilterFixBondLengthsWrapper):
            atoms = atoms.atoms
            atoms.wrap()
            pos = atoms.get_positions()
            cell = atoms.get_cell()
            pbc = atoms.get_pbc()
            for (i1, i2), bl in zip(fix_bond_lengths_pairs, bond_lengths):
                vec = pos[i2] - pos[i1]
                vec = find_mic([vec], cell, pbc)[0][0]
                pos[i2] = pos[i1] + vec * bl / np.linalg.norm(vec)
            atoms.set_positions(pos)
            atoms = UnitCellFilterFixBondLengthsWrapper(atoms)

    calculate_regular = False

    if fix_bond_lengths_pairs is not None:
        # Guarantee that the fixed bonds have the specified length
        pos = atoms.get_positions()

        if isinstance(atoms, Filter):
            cell = atoms.atoms.get_cell()
            pbc = atoms.atoms.get_pbc()
        else:
            cell = atoms.get_cell()
            pbc = atoms.get_pbc()

        for (i1, i2), bl in zip(fix_bond_lengths_pairs, bond_lengths):
            vec = pos[i2] - pos[i1]
            vec = find_mic([vec], cell, pbc)[0][0]
            pos[i2] = pos[i1] + vec * bl / np.linalg.norm(vec)

        if isinstance(atoms, Filter):
            atoms = atoms.atoms
            atoms.set_positions(pos[:len(atoms)])
            atoms = UnitCellFilterFixBondLengthsWrapper(atoms)
            try:
                E = atoms.get_potential_energy()
                F = atoms.get_forces()[:len(atoms.atoms)]
                S = atoms.stress
            except RuntimeError:
                calculate_regular = True
        else:
            atoms.set_constraint(original_constraints)
            atoms.set_positions(pos)
            atoms.set_constraint(original_constraints + [fbl_constraint])
            calculate_regular = True

    if isinstance(atoms, Filter):
        atoms = atoms.atoms

    if fix_bond_lengths_pairs is None or calculate_regular:
        E = atoms.get_potential_energy()
        F = atoms.get_forces()
        S = atoms.get_stress() if variable_cell else None

    atoms.wrap()

    if verbose:
        print('Done E=%8.3f, maxF=%5.3f, maxS=%5.3e, nsteps=%d' % \
              (E, (F**2).sum(axis=1).max()**0.5,
               0. if S is None else np.max(np.abs(S)), nsteps), flush=True)

    finalize(atoms, energy=E, forces=F, stress=S)
    return atoms
Example #17
0
atoms.set_cell(atoms.cell * np.diag([1.0 + s, 1.0 - s * nu, 1.0 - s * nu]),
               scale_atoms=True)

# simple header for the output
print("        Iteration       Time     Energy     max Force")

# loop to perform the successive small strain increments
for i in range(50):

    # record and print the current strain
    atoms.info["strain"] = (atoms.cell[0, 0] -
                            origLx) / origLx  # Engineering strain
    print "strain: ", atoms.info["strain"]

    # set up an optimizer, this is a particularly efficient one
    opt = PreconLBFGS(atoms, precon=Exp(3.0))

    # attach the optimiser to the atoms object, asking it to call our helper function
    # that writes out the atomic positions, after every 2nd iterations of the optimisation
    opt.attach(write_frame, 2, atoms)

    # run the optimizer, until the maximum force on any atom is less than a tolerance in eV/Ang
    opt.run(fmax=0.02)

    # update the "grip" by applying the small strain increment.
    s = 0.01  # small strain increment
    atoms.set_cell(atoms.cell * np.diag([1.0 + s, 1.0 - s * nu, 1.0 - s * nu]),
                   scale_atoms=True)

#######################################################################
#
Example #18
0
]
atoms = Atoms('Pt13', positions=positions, cell=[15] * 3)

maxstep = 0.2
longest_steps = []

labels = ['BFGS', 'BFGSLineSearch', 'PreconLBFGS_Armijo', 'PreconLBFGS_Wolff']
optimizers = [BFGS, BFGSLineSearch, PreconLBFGS, PreconLBFGS]

for i, Optimizer in enumerate(optimizers):
    a = atoms.copy()
    a.set_calculator(EMT())

    kwargs = {'maxstep': maxstep, 'logfile': None}
    if 'Precon' in labels[i]:
        kwargs['precon'] = Exp(A=3)
        kwargs['use_armijo'] = 'Armijo' in labels[i]

    opt = Optimizer(a, **kwargs)
    opt.run(steps=1)

    dr = a.get_positions() - positions
    steplengths = (dr**2).sum(1)**0.5
    longest_step = np.max(steplengths)

    print('%s: longest step = %.4f' % (labels[i], longest_step))
    longest_steps.append(longest_step)

longest_steps = np.array(longest_steps)
assert (longest_steps < maxstep + 1e-8).all()
Example #19
0
atoms.set_cell(atoms.cell * np.diag([1.0 + s, 1.0 - s * nu, 1.0 - s * nu]),
               scale_atoms=True)

# simple header for the output
print("        Iteration       Time     Energy     max Force")

# loop to perform the successive small strain increments
for i in range(10):

    # record and print the current strain
    atoms.info["strain"] = (atoms.cell[0, 0] -
                            origLx) / origLx  # Engineering strain
    print("strain: ", atoms.info["strain"])

    # set up an optimizer, this is a particularly efficient one
    opt = PreconLBFGS(atoms, precon=Exp(3.0, use_pyamg=False))

    # attach the optimiser to the atoms object, asking it to call our helper function
    # that writes out the atomic positions, after every iteration of the optimisation
    opt.attach(write_frame, 1, atoms)

    # run the optimizer, until the maximum force on any atom is less than a tolerance in eV/Ang
    opt.run(fmax=0.02)

    # update the "grip" by applying the small strain increment.
    s = 0.01  # small strain increment
    atoms.set_cell(atoms.cell * np.diag([1.0 + s, 1.0 - s * nu, 1.0 - s * nu]),
                   scale_atoms=True)

#######################################################################
#
Example #20
0
class Hessian_EAM_EXP(SparsePrecon):
    """ Creates matrix with the hessian matrix of the matscipy EAM calculator.
    """
    def __init__(self,
                 calculator=None,
                 nbr_of_evals=5,
                 terms=["pair", "T1", "T2", "T3", "T4", "T5", "T6", "T7"],
                 r_cut=None,
                 mu=None,
                 mu_c=None,
                 dim=3,
                 c_stab=0.1,
                 force_stab=False,
                 reinitialize=False,
                 array_convention='C',
                 solver="auto",
                 solve_tol=1e-9,
                 apply_positions=True,
                 apply_cell=True,
                 logfile=None):
        """Initialise an Hessian_EAM preconditioner with given parameters.

        Args:
            calculator: matscipy eam calculator
                The calculator that calculates the hessian of the system.
            nbr_of_evals: int
                The number of evaluation after the matrix is calculated.
            terms: list of strings
                The list which terms of the analytic eam hessian should be
                calculated.
            r_cut, mu, c_stab, dim, recalc_mu, array_convention: see
                precon.__init__()
        """
        super().__init__(r_cut=r_cut,
                         mu=mu,
                         mu_c=mu_c,
                         dim=dim,
                         c_stab=c_stab,
                         force_stab=force_stab,
                         reinitialize=reinitialize,
                         array_convention=array_convention,
                         solver=solver,
                         solve_tol=solve_tol,
                         apply_positions=apply_positions,
                         apply_cell=apply_cell,
                         logfile=logfile)

        self.calculator = calculator
        self.terms = terms
        self.nbr_of_evals = nbr_of_evals
        self.ctr_of_evals = 0
        self.exp_precon = Exp()
        self.fmax = float('inf')

    def make_precon(self, atoms, reinitialize=None):
        start_time = time.time()
        forces = atoms.get_forces()
        last_fmax = self.fmax
        self.fmax = sqrt((forces**2).sum(axis=1).max())
        # Create the preconditioner:
        if (self.P is None or self.ctr_of_evals >= self.nbr_of_evals
                or (self.fmax <= 0.1 and last_fmax > 0.1)):
            self._make_sparse_precon(atoms, force_stab=self.force_stab)
            self.ctr_of_evals = 0
        if self.fmax <= 0.1:
            self.ctr_of_evals += 1
        self.logfile.write('--- Precon created in %s seconds ---\n' %
                           (time.time() - start_time))

    def _make_sparse_precon(self,
                            atoms,
                            initial_assembly=False,
                            force_stab=False):
        """Create a sparse preconditioner matrix based on the passed atoms.

        Args:
            atoms: the Atoms object used to create the preconditioner.

        Returns:
            A scipy.sparse.csr_matrix object, representing a d*N by d*N matrix
            (where N is the number of atoms, and d is the value of self.dim).
            BE AWARE that using numpy.dot() with this object will result in
            errors/incorrect results - use the .dot method directly on the
            sparse matrix instead.

        """
        self.logfile.write('creating eam hessian: terms={}'.format(self.terms))
        start_time = time.time()
        if self.fmax > 0.1:
            self.exp_precon.make_precon(atoms)
            self.P = self.exp_precon.P
            print("Fmax > 1")
        else:
            self.P = self.calculator.calculate_hessian_matrix(atoms,
                                                              terms=self.terms)
            val, vec = eigsh(self.P, k=1, which="SA")
            a_s = 0.00001
            a = a_s
            for k in range(100):
                self.P += a * sparse.eye(self.P.shape[0])
                val, vec = eigsh(self.P, k=1, which="SA")
                if (val > 0):
                    break
                a = 1.5 * a
        self.logfile.write('--- Hessian created in %s seconds ---\n' %
                           (time.time() - start_time))
        self.create_solver()
Example #21
0
from ase.optimize.precon import Exp, PreconLBFGS, PreconFIRE
from ase.constraints import FixBondLength, FixAtoms

N = 1
a0 = bulk('Cu', cubic=True)
a0 *= (N, N, N)

# perturb the atoms
s = a0.get_scaled_positions()
s[:, 0] *= 0.995
a0.set_scaled_positions(s)

nsteps = []
energies = []
for OPT in [PreconLBFGS, PreconFIRE]:
    for precon in [None, Exp(A=3, mu=1.0)]:
        atoms = a0.copy()
        atoms.set_calculator(EMT())
        opt = OPT(atoms, precon=precon, use_armijo=True)
        opt.run(1e-4)
        energies += [atoms.get_potential_energy()]
        nsteps += [opt.get_number_of_steps()]

# check we get the expected energy for all methods
assert np.abs(np.array(energies) - -0.022726045433998365).max() < 1e-4

# test with fixed bondlength and fixed atom constraints
cu0 = bulk("Cu") * (2, 2, 2)
cu0.rattle(0.01)
a0 = cu0.get_distance(0, 1)
cons = [FixBondLength(0,1), FixAtoms([2,3])]
Example #22
0
            calc = GFN2(charge=args.charge,
                        accuracy=args.acc,
                        temperature=args.etemp,
                        max_iterations=args.maxiter,
                        print_level=2,
                        solvent=args.solvent)
    else:
        raise Exception("Method not implemented.")
    mol.set_calculator(calc)

    e = mol.get_potential_energy()

    print('Initial energy: eV, Eh', e, e / Hartree)

    if args.precon:
        precon = Exp(A=3, r_NN=args.rnn + 0.1, r_cut=args.rnn + 0.6)
    else:
        precon = None

    if args.optcell:
        sf = ExpCellFilter(mol)
        relax = PatchedOptimizer(sf,
                                 precon=precon,
                                 trajectory=args.trajectory,
                                 logfile=args.logfile,
                                 use_armijo=args.armijo)
    else:
        relax = PatchedOptimizer(mol,
                                 precon=precon,
                                 trajectory=args.trajectory,
                                 logfile=args.logfile,
Example #23
0
    dimer.positions[3:, 0] += 2.8
    dimer.constraints = FixBondLengths([
        ((selection[i] + 3) % 6, (selection[i - 1] + 3) % 6) for i in range(3)
    ])

    dimer.calc = EIQMMM(selection,
                        GPAW(txt=name + '.txt', h=0.16),
                        TIP4P(),
                        interaction,
                        vacuum=4,
                        embedding=Embedding(rc=0.2, rc2=20, width=1),
                        output=name + '.out')
    opt = LBFGS(dimer, trajectory=name + '.traj')
    opt.run(0.02)

    monomer = dimer[selection]
    monomer.center(vacuum=4)
    monomer.calc = GPAW(txt=name + 'M.txt', h=0.16)
    opt = PreconLBFGS(monomer, precon=Exp(A=3), trajectory=name + 'M.traj')
    opt.run(0.02)
    e0 = monomer.get_potential_energy()
    be = dimer.get_potential_energy() - e0
    d = dimer.get_distance(0, 3)
    print(name, be, d)
    if name == '012':
        assert abs(be - -0.288) < 0.002
        assert abs(d - 2.76) < 0.02
    else:
        assert abs(be - -0.316) < 0.002
        assert abs(d - 2.67) < 0.02
Example #24
0
# creates: precon.png

from ase.build import bulk
from ase.calculators.emt import EMT
from ase.optimize.precon import Exp, PreconLBFGS

from ase.calculators.loggingcalc import LoggingCalculator
import matplotlib.pyplot as plt

a0 = bulk('Cu', cubic=True)
a0 *= [3, 3, 3]
del a0[0]
a0.rattle(0.1)

nsteps = []
energies = []
log_calc = LoggingCalculator(EMT())

for precon, label in [(None, 'None'), (Exp(A=3, mu=1.0), 'Exp(A=3)')]:
    log_calc.label = label
    atoms = a0.copy()
    atoms.set_calculator(log_calc)
    opt = PreconLBFGS(atoms, precon=precon, use_armijo=True)
    opt.run(fmax=1e-3)

log_calc.plot(markers=['r-', 'b-'], energy=False, lw=2)
plt.savefig('precon.png')
Example #25
0
    def estimate_mu(self, structure, fixed_frame, parameters):
        """Estimate scaling parameter mu for
        Expoential preconditioner scheme.
        For more implementation detail see Packwood et. al:
        A universal preconditioner for simulating condensed phase materials,
        J. Chem. Phys. 144, 164109 (2016).
        https://aip.scitation.org/doi/full/10.1063/1.4947024

        and

        https://wiki.fysik.dtu.dk/ase/ase/optimize.html

        First reads the parameters file and checks if the
        estimation of mu is necessary. Then Estimates mu
        with default parameters of r_cut=2*r_NN, where r_NN
        is estimated nearest neighbour distance. Parameter
        A=3.0 set to default value as was mentioned in the
        paper.

        Args:
            structure {GenSec structure}: structure object
            fixed_frame {GenSec fixed frame}: fixed frame object
            parameters {JSON} : Parameters from file

        Returns:
            float: Scaling parameter mu
        """
        # Figure out for which atoms Exp is applicapble
        precons_parameters = {
            "mol":
            parameters["calculator"]["preconditioner"]["mol"]["precon"],
            "fixed_frame":
            parameters["calculator"]["preconditioner"]["fixed_frame"]
            ["precon"],
            "mol-mol":
            parameters["calculator"]["preconditioner"]["mol-mol"]["precon"],
            "mol-fixed_frame":
            parameters["calculator"]["preconditioner"]["mol-fixed_frame"]
            ["precon"],
        }
        precons_parameters_init = {
            "mol":
            parameters["calculator"]["preconditioner"]["mol"]["initial"],
            "fixed_frame":
            parameters["calculator"]["preconditioner"]["fixed_frame"]
            ["initial"],
            "mol-mol":
            parameters["calculator"]["preconditioner"]["mol-mol"]["initial"],
            "mol-fixed_frame":
            parameters["calculator"]["preconditioner"]["mol-fixed_frame"]
            ["initial"],
        }
        precons_parameters_update = {
            "mol":
            parameters["calculator"]["preconditioner"]["mol"]["update"],
            "fixed_frame":
            parameters["calculator"]["preconditioner"]["fixed_frame"]
            ["update"],
            "mol-mol":
            parameters["calculator"]["preconditioner"]["mol-mol"]["update"],
            "mol-fixed_frame":
            parameters["calculator"]["preconditioner"]["mol-fixed_frame"]
            ["update"],
        }
        need_for_exp = False
        for i in range(len(list(precons_parameters.values()))):
            if list(precons_parameters.values())[i] == "Exp":
                if (list(precons_parameters_init.values())[i]
                        or list(precons_parameters_update.values())[i]):
                    need_for_exp = True
        mu = 1.0
        if need_for_exp:
            if len(structure.molecules) > 1:
                a0 = structure.molecules[0].copy()
                for i in range(1, len(structure.molecules)):
                    a0 += structure.molecules[i]
            else:
                a0 = structure.molecules[0]
            if hasattr(fixed_frame, "fixed_frame"):
                all_atoms = a0 + fixed_frame.fixed_frame
            else:
                all_atoms = a0
            atoms = all_atoms.copy()
            atoms.set_calculator(self.calculator)
            self.set_constrains(atoms, parameters)
            r_NN = estimate_nearest_neighbour_distance(atoms)
            try:
                mu = Exp(r_cut=2.0 * r_NN, A=3.0).estimate_mu(atoms)[0]
            except:
                print("Something is wrong!")
        return mu
Example #26
0
# creates: precon.png

from ase.build import bulk
from ase.calculators.emt import EMT
from ase.optimize.precon import Exp, PreconLBFGS

from ase.calculators.loggingcalc import LoggingCalculator
import matplotlib.pyplot as plt

a0 = bulk('Cu', cubic=True)
a0 *= [3, 3, 3]
del a0[0]
a0.rattle(0.1)

nsteps = []
energies = []
log_calc = LoggingCalculator(EMT())

for precon, label in [(None, 'None'), (Exp(A=3, use_pyamg=False), 'Exp(A=3)')]:
    log_calc.label = label
    atoms = a0.copy()
    atoms.set_calculator(log_calc)
    opt = PreconLBFGS(atoms, precon=precon, use_armijo=True)
    opt.run(fmax=1e-3)

log_calc.plot(markers=['r-', 'b-'], energy=False, lw=2)
plt.savefig('precon.png')
Example #27
0
import numpy as np

from ase.build import bulk
from ase.calculators.emt import EMT
from ase.optimize.precon import Exp, PreconLBFGS, PreconFIRE

N = 1
a0 = bulk('Cu', cubic=True)
a0 *= (N, N, N)

# perturb the atoms
s = a0.get_scaled_positions()
s[:, 0] *= 0.995
a0.set_scaled_positions(s)

nsteps = []
energies = []
for OPT in [PreconLBFGS, PreconFIRE]:
    for precon in [None, Exp(A=3, use_pyamg=False)]:
        atoms = a0.copy()
        atoms.set_calculator(EMT())
        opt = OPT(atoms, precon=precon, use_armijo=True)
        opt.run(1e-4)
        energies += [atoms.get_potential_energy()]
        nsteps += [opt.get_number_of_steps()]

# check we get the expected energy for all methods
assert np.abs(np.array(energies) - -0.022726045433998365).max() < 1e-4
Example #28
0
def calc(primitive,
         cachedir=None,
         supercell=(1, 1, 1),
         delta=0.01,
         quick=True):
    """Calculates the Hessian for a given atoms object (which *must* have an
    attached calculator).

    .. note:: We choose to use the Hessian as the fundamental quantity in
      vibrational analysis in `matdb`.

    .. note:: `atoms` will be relaxed before calculating the Hessian.

    Args:
        primitive (matdb.atoms.Atoms): atomic structure of the *primitive*.
        cachedir (str): path to the directory where phonon calculations are
          cached. If not specified, a temporary directory will be used.
        supercell (tuple): number of times to duplicate the cell when
          forming the supercell.
        delta (float): displacement in Angstroms of each atom when computing the
          phonons. 
        quick (bool): when True, use symmetry to speed up the Hessian
          calculation. See :func:`_calc_quick`.

    Returns:
        numpy.ndarray: Hessian matrix that has dimension `(natoms*3, natoms*3)`,
        where `natoms` is the number of atoms in the *supercell*.
    """
    if quick:
        return _calc_quick(primitive, supercell, delta)
    else:
        atoms = primitive.make_supercell(supercell)
        atoms.set_calculator(primitive.get_calculator())

    from ase.vibrations import Vibrations

    #The phonon calculator caches the displacements and force sets for each
    #atomic displacement using pickle. This generates three files for each
    #atomic degree of freedom (one for each cartesian direction). We want to
    #save these in a special directory.
    tempcache = False
    if cachedir is None:
        cachedir = mkdtemp()
        tempcache = True
    else:
        cachedir = path.abspath(path.expanduser(cachedir))
    if not path.isdir(cachedir):
        mkdir(cachedir)

    result = None
    precon = Exp(A=3)
    aphash = None

    #Calculate a hash of the calculator and atoms object that we are calculating
    #for. If the potential doesn't have a `to_dict` method, then we ignore the
    #hashing.
    if not tempcache and hasattr(atoms, "to_dict") and hasattr(
            atoms._calc, "to_dict"):
        atoms_pot = {"atoms": atoms.to_dict(), "pot": atoms._calc.to_dict()}
        #This UUID will probably be different, even if the positions and species
        #are identical.
        del atoms_pot["atoms"]["uuid"]
        hash_str = convert_dict_to_str(atoms_pot)
        aphash = str(sha1(hash_str).hexdigest())

    if not tempcache:
        #Check whether we should clobber the cache or not.
        extras = ["vibsummary.log", "vib.log", "phonons.log"]

        with chdir(cachedir):
            hash_match = False
            if path.isfile("atomspot.hash"):
                with open("atomspot.hash") as f:
                    xhash = f.read()
                hash_match = xhash == aphash

            hascache = False
            if not hash_match:
                for vibfile in glob("vib.*.pckl"):
                    remove(vibfile)
                    hascache = True

                for xfile in extras:
                    if path.isfile(xfile):
                        remove(xfile)
                        hascache = True

            if hascache:
                msg.warn(
                    "Using hard-coded cache directory. We were unable to "
                    "verify that the atoms-potential combination matches "
                    "the one for which existing cache files exist. So, we "
                    "clobbered the existing files to get the science "
                    "right. You can fix this by using `matdb.atoms.Atoms` "
                    "and `matdb.calculators.*Calculator` objects.")

    with chdir(cachedir):
        #Relax the cell before we calculate the Hessian; this gets the forces
        #close to zero before we make harmonic approximation.
        try:
            with open("phonons.log") as f:
                with redirect_stdout(f):
                    minim = PreconLBFGS(atoms, precon=precon, use_armijo=True)
                    minim.run(fmax=1e-5)
        except:
            #The potential is unstable probably. Issue a warning.
            msg.warn(
                "Couldn't optimize the atoms object. Potential may be unstable."
            )

        vib = Vibrations(atoms, delta=delta)
        with open("vib.log", 'a') as f:
            with redirect_stdout(f):
                vib.run()

        vib.summary(log="vibsummary.log")
        result = vib.H

        #Cache the hash of the atoms object and potential that we were using so
        #that we can check next time whether we should clobber the cache or not.
        if aphash is not None and not tempcache:
            with open(path.join(cachedir, "atomspot.hash"), 'w') as f:
                f.write(aphash)

    return result
Example #29
0
import numpy as np

from ase.build import bulk
from ase.calculators.lj import LennardJones
from ase.optimize.precon import Exp, PreconLBFGS

cu0 = bulk("Cu") * (2, 2, 2)
sigma = cu0.get_distance(0,1)*(2.**(-1./6))
lj = LennardJones(sigma=sigma)

# perturb the cell
cell = cu0.get_cell()
cell *= 0.95
cell[1,0] += 0.2
cell[2,1] += 0.5
cu0.set_cell(cell, scale_atoms=True)

energies = []
for use_armijo in [True, False]:
    for a_min in [None, 1e-3]:
        atoms = cu0.copy()
        atoms.set_calculator(lj)
        opt = PreconLBFGS(atoms, precon=Exp(A=3), use_armijo=use_armijo,
                          a_min=a_min, variable_cell=True)
        opt.run(fmax=1e-3, smax=1e-4)
        energies.append(atoms.get_potential_energy())

# check we get the expected energy for all methods
assert np.abs(np.array(energies) - -63.5032311942).max() < 1e-4
def main():
    atoms = build.bulk("Al")
    atoms = atoms * (2, 2, 2)
    print(len(atoms))

    nRuns = 10
    optimizerFname = "optimizer.pck"
    for i in range(nRuns):
        nMgAtoms = np.random.randint(0, len(atoms) / 2)

        # Insert Mg atoms
        system = cp.copy(atoms)
        if (parallel.rank == 0):
            for j in range(nMgAtoms):
                system[i].symbol = "Mg"

            # Shuffle the list
            for j in range(10 * len(system)):
                first = np.random.randint(0, len(system))
                second = np.random.randint(0, len(system))
                symb1 = system[first].symbol
                system[first].symbol = system[second].symbol
                system[second].symbol = symb1
        system = parallel.broadcast(system)

        # Initialize the calculator
        calc = gp.GPAW(mode=gp.PW(400), xc="PBE", kpts=(4, 4, 4), nbands=-10)
        system.set_calculator(calc)

        traj = Trajectory("trajectoryResuse.traj", 'w', atoms)

        if (i == 0):
            relaxer = PreconLBFGS(UnitCellFilter(system), logfile="resuse.log")
        else:
            relaxer = None
            relaxParams = None
            if (parallel.rank == 0):
                with open(optimizerFname, 'rb') as infile:
                    relaxParams = pck.load(infile)
            relaxParams = parallel.broadcast(relaxParams)
            precon = Exp(r_cut=relaxParams["r_cut"],
                         r_NN=relaxParams["r_NN"],
                         mu=relaxParams["mu"],
                         mu_c=relaxParams["mu_c"])
            relaxer = PreconLBFGS(UnitCellFilter(system),
                                  logfile="resuse.log",
                                  precon=precon)

        relaxer.attach(traj)

        relaxer.run(fmax=0.05)
        print(relaxer.iteration)
        if (parallel.rank == 0):
            with open(optimizerFname, 'wb') as outfile:
                relaxParams = {
                    "r_cut": relaxer.precon.r_cut,
                    "r_NN": relaxer.precon.r_NN,
                    "mu": relaxer.precon.mu,
                    "mu_c": relaxer.precon.mu_c
                }
                pck.dump(relaxParams, outfile, pck.HIGHEST_PROTOCOL)
        parallel.barrier()