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 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
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)
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
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)
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()
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
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)
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'))
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
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
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]))
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
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
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) ####################################################################### #
] 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()
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) ####################################################################### #
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()
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])]
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,
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
# 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')
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
# 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')
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
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
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()