def vibmodes(atoms, startdir=None, mask=None, workhere=False, save=None, give_output=0, pmap=pmap3, **kwargs): """ Wrapper around vibmode, which used the atoms objects The hessian is calculated by derivatef qfunc.fwrapper is used as a wrapper to calulate the gradients """ from pts.memoize import Memoize, DirStore coord = atoms.get_positions() if mask == None: mask = constraints2mask(atoms) if mask == None: # (if still None) fun = Cartesian() else: fun = Masked(Cartesian(), mask, coord.flatten()) xcenter = fun.pinv(coord) myfunc = QFunc(atoms, atoms.get_calculator()) myfunc = Memoize(myfunc, DirStore("cache.d")) myfunc = compose(myfunc, fun) pmapc = pwrapper(pmap) func = fwrapper(myfunc, startdir=startdir, mask=mask, workhere=workhere) # the derivatives are needed hessian = derivatef(func, xcenter, pmap=pmapc, **kwargs) # save is assumend to be a filename, so far only the hessian is saved: if save is not None: savetxt(save, hessian) mass = mass_matrix(atoms.get_masses(), mask) freqs, modes = vibmod(mass, hessian) # the output will be printed on demand, with modes if wanted # the output for the freqs can easily be recreated later on, but # for the mode vectors the mass (not included in the direct output_ # is needed if VERBOSE or give_output == 2: output(freqs, modes, mass, mask) elif give_output == 1: output(freqs) return freqs, modes
def test_uranyl(): e = compose(uo2, uranyl) e = Memoize(e, DirStore(salt="XXX YYY")) # s = array ([1.79, 1.79, pi]) s = zeros(6) # + 0.1 print(uranyl(s)) # print (e(s)) # exit (0) sm, info = minimize(e, s, ftol=1.0e-2, xtol=1.0e-2, algo=1) print(uranyl(sm)) print("e(0)=", e(s), "e(1)=", e(sm), "iterations=", info["iterations"])
def exchange(s): # Functions of cartesian coordinates: ra = Distance([0, 3]) rb = Distance([0, 18]) # rc = Difference (ra, rb) rc = ra f0 = Server(cmd) h0 = Server(alt) F0 = QFunc(atoms, ParaGauss(cmdline=qmd, input="6w,c1.scm")) with f0 as f0, h0 as h0, F0 as F0: f1 = Memoize(f0, DirStore(salt=cmd + "TESTING")) h1 = Memoize(h0, DirStore(salt=alt + "TESTING")) F1 = Memoize(F0, DirStore(salt=qmd + "XXX4 50/291")) # A Func of cartesian coordinates, stretching and bending # parameters as tuples. Parameters not hashed, beware of # memoization of this term!: if uo2 is not None: f1 = f1 + uo2 f = compose(f1, trafo) h = compose(h1, trafo) c = compose(rc, trafo) F = compose(F1, trafo) B = compose(bias, trafo) refine = True if False: ss = loadtxt("ss,initial.txt") qs = array(map(c, ss)) print("qs=", qs) print("dq=", qs[1:] - qs[:-1]) if False: P = Path(ss, qs) C = compose(c, P) print("q=", qs) def solve(q): x = q while abs(C(x) - q) > 1.0e-10: f, fprime = C.taylor(x) df = f - q x = x - df / fprime return x xs = [] for q in linspace(qs[0], qs[-1], 21): x = solve(q) print("x=", x, "C(x)=", C(x), "q=", q) xs.append(x) ss = array(map(P, xs)) qs = array(map(c, ss)) print("qs=", qs) print("dq=", qs[1:] - qs[:-1]) p = Path(ss, qs) if False: qs, ss = initial_path(f + B, s, c) savetxt("ss-initial.txt", ss) p = Path(ss, qs) if False: for i, q in enumerate(qs): write_xyz("in-%03d.xyz" % i, trafo(p(q))) if False: # Energy profile, smooth: with open("profile-initial.txt", "w") as file: print("# q, ra(q), rb(q), E(q), E_qm(q), B(q)", file=file) for q in linspace(qs[0], qs[-1], 100): print(q, ra(trafo(p(q))), rb(trafo(p(q))), f(p(q)), B(p(q)), file=file) if False: # Energy profile, coarse, with dG(q): with open("./profile,intial.txt", "w") as file: print("# q, ra(q), rb(q), F(q)", file=file) # print ("# q, ra(q), rb(q), E(q), dG(q), F(q)", file=file) for q in qs: print(q, ra(trafo(p(q))), rb(trafo(p(q))), F(p(q)), file=file) # print (q, ra (trafo (p(q))), rb (trafo (p (q))), f(p(q)), h(p(q)), F(p(q)), file=file) # with f + h as e: with F + h + B as e: print("XXX") if False: p1 = Path(loadtxt("sxy.txt", ndmin=2)) c1 = compose(c, p1) q1 = linspace(0., 1., 21) for i, q in enumerate(q1): write_xyz("interp-%03d.xyz" % i, trafo(p1(q))) print("c=", [c1(q) for q in q1]) Q1 = Inverse(c1) q2 = [Q1(c_) for c_ in [3.05]] print("q=", q2, "c=", [c1(q) for q in q2]) s2 = array([p1(q) for q in q2]) savetxt("sxy1.txt", s2) exit(0) def opt(s): sm, info = minimize(e, s, maxit=50, ftol=1.0e-2, xtol=1.0e-2, algo=1) print("converged=", info["converged"], "in", info["iterations"]) return sm if True: # Unconstrained termnals: sab = loadtxt("sab,initial.txt", ndmin=2) # sab = (ss[0], ss[-1]) print("before: q=", map(c, sab)) sab = map(opt, sab) print("after: q=", map(c, sab)) savetxt("sab.txt", sab) for i, s in enumerate(sab): write_xyz("min-%03d.xyz" % i, trafo(s)) exit(0) def copt(s, maxit): sm, info = cminimize(e, s, Array(c), maxit=maxit, ftol=1.0e-3, xtol=1.0e-3, algo=0) print("converged=", info["converged"], "in", info["iterations"]) return sm maxit = [30] * len(ss) ss = array(map(copt, ss, maxit)) savetxt("ss.txt", ss) # Energy profile: with open("./profile,rs.txt", "w") as file: print("# Optimized profile:", file=file) # print (qa, f(sa), h(sa), e(sa), file=file) for q, s in zip(qs, ss): print(q, f(s), h(s), e(s), file=file) # print (qb, f(sb), h(sb), e(sb), file=file) for i, s in enumerate(ss): write_xyz("KH-%03d.xyz" % i, trafo(s)) if False: p = Path(ss, qs) with open("./profile,rs,interp.txt", "w") as file: print("# Interpolated profile:", file=file) for q in linspace(qs[0], qs[-1], 3 * len(qs)): print(q, f(p(q)), h(p(q)), e(p(q)), file=file)
def optimization(s): with Server(cmd) as g, Server(alt) as h: # Change the salts to discard memoized results (or delete # ./cache.d): g = Memoize(g, DirStore(salt=cmd + "TESTING1 Sep14")) h = Memoize(h, DirStore(salt=alt + "TESTING1 Sep14")) # A Func of cartesian coordinates, stretching and bending # parameters as tuples. Parameters not hashed, beware of # memoization of this term!: if uo2 is not None: g = g + uo2 g = compose(g, trafo) # MM self-energy h = compose(h, trafo) # RISM solvation energy def opt(e, s, name, **kwargs): print("XXX: " + name + "...") print(name + ": e(0)=", e(s) / kcal, "kcal, |g|=", max(abs(e.fprime(s))) / kcal, "kcal/Unit") s, info = minimize(e, s, **kwargs) print(name + ": converged =", info["converged"], "in", info["iterations"], "iterations") write_xyz(name + ".xyz", trafo(s)) # print info if "trajectory" in info: traj = info["trajectory"] print(map(e, traj)) for i, si in enumerate(traj): # write_xyz ("traj-%s-%03d.xyz" % (name, i), trafo (si)) pass return s, info # MM self-energy: with g as e: s0, info = opt(e, s, "MM", algo=1, maxstep=0.1, maxit=200, ftol=5.0e-3, xtol=5.0e-3) print("XXX: MM", e(s), "eV", e(s) / kcal, "kcal", info["converged"]) exit(0) # MM self-energy with RISM solvation: with g + h as e: s1, info = opt(e, (s if NW == 4 else s0), "MM+RISM", algo=1, maxstep=0.1, maxit=200, ftol=5.0e-3, xtol=5.0e-3) print("XXX: MM+RISM", e(s), "eV", e(s) / kcal, "kcal", info["converged"]) # This prints a table of various functionals applied to several # geometries: ss = [s0, s1] with g + h as e: for s in ss: print("MM=", g(s) / kcal, "RISM=", h(s) / kcal, "MM+RISM=", e(s) / kcal, "(kcal)")
def pathsearcher(atoms, init_path, trafo, **kwargs): """ MUSTDIE: from python code use a PES method find_path(pes, path, ...), for command line there is call_with_pes(find_path, sys.argv[1:]) See call_with_pes() and find_path() instead. Only used in ./tests. It is possible to use the pathsearcher() function in a python script. It looks like: from pts.inputs import pathsearcher pathsearcher(atoms, init_path, trafo, **kwargs) * atoms is an ASE atoms object used to calculate the forces and energies of a given (Cartesian) geometry. Be aware that it needs to have an calculator attached to it, which will do the actual transformation. Another possibility is to give the calculator separately as an option. * init_path is an array containting for each bead of the starting path the internal coordinates. * trafo is a Func to transform internal to Cartesian coordinates. * the other parameters give the possibility to overwrite some of the default behaviour of the module, They are provided as kwargs in here. For a list of them see defaults.py They can be also specified in an input file given as paramfile. """ # most parameters are stored in a dictionary, default parameters are stored in # defaults.py para_dict = create_params_dict(kwargs) # calculator from kwargs, if valid, has precedence over if "calculator" in para_dict: # the associated (or not) with the atoms: if para_dict["calculator"] is not None: atoms.set_calculator(para_dict["calculator"]) # calculator is not used below: del para_dict["calculator"] # print parameters to STDOUT: tell_params(para_dict) # # PES to be used for energy, forces. FIXME: maybe adapt QFunc not # to default to LJ, but rather keep atoms as is? # pes = QFunc(atoms, atoms.get_calculator()) # # Memoize early, PES as a function of cartesian coordiantes is the # lowest denominator. The cache store used here should allow # concurrent writes and reads from multiple processes eventually # running on different nodes --- DirStore() keeps everything in a # dedicated directory on disk or on an NFS share: # pes = Memoize(pes, DirStore("cache.d")) # # PES as a funciton of optimization variables, such as internal # coordinates: # pes = compose(pes, trafo) # This parallel mapping function puts every single point calculation in # its own subfolder strat = Strategy(para_dict["cpu_architecture"], para_dict["pmin"], para_dict["pmax"]) del para_dict["cpu_architecture"] del para_dict["pmin"] del para_dict["pmax"] if "pmap" not in para_dict: para_dict["pmap"] = PMap3(strat=strat) para_dict["trafo"] = trafo para_dict["symbols"] = atoms.get_chemical_symbols() # this operates with PES in internals: convergence, optimized_path = find_path(pes, init_path, **para_dict) # print user-friendly output, including cartesian geometries: output(optimized_path, trafo, para_dict["output_level"], para_dict["output_geo_format"], atoms) return convergence, optimized_path
def call_with_pes(method, args): """ Interprete command line args and call a method() with PES and other input constructed according to the command line. A method should have an interface as method(pes, geometries, **kw) Return value(s) of the method are passed up the caller chain. Most path searcher algorithms can be made to fit this interface. For use in scripts. Note that args is a list of strings from sys.args. To invoke a method(pes, path, **rest) do this: from pts.path_searcher import call_with_pes results = call_with_pes(method, sys.argv[1:]) The rest is a slightly misplaced doc copied from a more specialized code: * atoms is an ASE atoms object used to calculate the forces and energies of a given (Cartesian) geometry. Be aware that it needs to have an calculator attached to it, which will do the actual transformation. Another possibility is to give a file in which calculator is specified separately as parameter. (FIXME: this another possibility is vaguely specified) * geometries is an array containting for each bead of the starting path the internal coordinates. * trafo is a Func to transform internal to Cartesian coordinates. * the other parameters give the possibility to overwrite some of the default behaviour of the module, They are provided as kwargs in here. For a list of them see defaults.py They can be also specified in an input file given as paramfile. """ # this interpretes the command line: atoms, geometries, trafo, kwargs = interprete_input(args) # most parameters are stored in a dictionary, default parameters # are stored in defaults.py kw = create_params_dict(kwargs) # calculator from kwargs, if valid, has precedence over if "calculator" in kw: # the associated (or not) with the atoms: if kw["calculator"] is not None: atoms.set_calculator(kw["calculator"]) # calculator is not used below: del kw["calculator"] # # Print parameters to STDOUT. This was a habit when doing path # searcher calculations. So for backwards compatibility do it in # this case, but only in this case (unless a yet non-existent # command line option requires that explicitly): # if method is pes_method: tell_params(kw) # # PES to be used for energy, forces. FIXME: maybe adapt QFunc not # to default to LJ, but rather keep atoms as is? # pes = QFunc(atoms, atoms.get_calculator()) # # Memoize early, PES as a function of cartesian coordiantes is the # lowest denominator. The cache store used here should allow # concurrent writes and reads from multiple processes eventually # running on different nodes --- DirStore() keeps everything in a # dedicated directory on disk or on an NFS share: # pes = Memoize(pes, DirStore("cache.d")) # # PES as a funciton of optimization variables, such as internal # coordinates: # pes = compose(pes, trafo) # This parallel mapping function puts every single point calculation in # its own subfolder if "pmap" not in kw: strat = Strategy(kw["cpu_architecture"], kw["pmin"], kw["pmax"]) kw["pmap"] = PMap3(strat=strat) del kw["cpu_architecture"] del kw["pmin"] del kw["pmax"] # some methds think they need to know atomic details of the PES: kw["atoms"] = atoms kw["trafo"] = trafo kw["symbols"] = atoms.get_chemical_symbols() # this operates with PES in internal variables: return method(pes, geometries, **kw)
def read_dimer_input(rest, name): """ This function is similar to the one for pathsearcher """ from pts.ui.read_inputs import from_params_file from pts.ui.read_COS import geo_params from pts.defaults import are_strings from pts.common import file2str from pts.func import compose from pts.qfunc import QFunc from pts.memoize import Memoize, FileStore from pts.defaults import di_default_params, qn_default_params, ln_default_params from pts.trajectories import dimer_log from pts.defaults import di_default_params, qn_default_params, di_default_params_rot #This variables will be needed afterwards anyway # independent of beeing given by user geo_dict = {"format": None, "zmt_format": "direct"} add_param = {} paramfile = None zmatrix = [] geo = None mode = None as_pickle = False ts_estim = None accept_all = False give_help_if_needed(rest, name) if rest[0] == "--accept_all": rest = rest[1:] accept_all = True defaults_available = True if name in ["dimer"]: default_params = di_default_params elif name in ["lanczos"]: default_params = ln_default_params elif name in ["lanczos-rotate", "dimer-rotate"]: default_params = di_default_params_rot elif name in ["qn", "simple_qn", "quasi-newton"]: default_params = qn_default_params else: print >> stderr, "WARNING: No default parameter for specific method found" print >> stderr, " There will be no test if the given parameter make sense" default_params = {} accept_all = True defaults_available = False if "--defaults" in rest: if defaults_available: print "The default parameters for the algorithm ", name, " are:" for param, value in default_params.iteritems(): print " %s = %s" % (str(param), str(value)) else: print "No default values available for the specific method", name exit() for i in range(len(rest)): if rest == []: break # filter out all the options if rest[0].startswith("--"): o = rest[0][2:] a = rest[1] # filter out the special ones if o == "paramfile": # file containing parameters paramfile = file2str(a) elif o in ("zmatrix"): # zmatrix if given separate to the geometries zmatrix.append(a) elif o in ("pickle"): as_pickle = True ts_estim = a elif o in geo_params: # only needed to build up the geometry if o in ("mask"): # needed to build up the geometry and wanted for params output geo_dict[o] = get_mask(a) elif o in ("cell", "pbc"): geo_dict[o] = eval(a) else: geo_dict[o] = a else: # suppose that the rest are setting parameters # we do not have a complete list of them if not accept_all: assert o in default_params or o == "rot_method", "Parameter %s" % ( o) if o in are_strings: add_param[o] = a else: add_param[o] = eval(a) rest = rest[2:] else: # This two files are needed any way: one geometry file and one # for the modevector, expect the geoemtry file to be given first if geo == None: # For reusing pathsearcher routines with several geoemtries for input geo = [rest[0]] else: mode = rest[0] rest = rest[1:] if paramfile == None: params_dict = add_param geo_dict_dim = geo_dict else: if accept_all: params_dict, geo_dict_dim = from_params_file_dimer(paramfile) else: params_dict, geo_dict_dim = from_params_file( paramfile, default_params) params_dict.update(add_param) geo_dict_dim.update(geo_dict) if as_pickle: start_geo, init_mode, funcart, atoms = read_from_pickle( geo[0], ts_estim, geo_dict_dim) else: start_geo, funcart, atoms = build_new(geo, geo_dict_dim, zmatrix) if name in ["lanczos", "dimer", "lanczos-rotate", "dimer-rotate"]: init_mode = build_mode(mode, start_geo, funcart) else: assert mode == None init_mode = None # Build up the qfunc, calculator is included in atoms already pes = compose(QFunc(atoms, calc=atoms.get_calculator()), funcart) if "cache" in params_dict: if params_dict["cache"] == None: pes = Memoize(pes, FileStore("%s.ResultDict.pickle" % (name))) else: pes = Memoize(pes, FileStore(params_dict["cache"])) else: pes = Memoize(pes, FileStore("%s.ResultDict.pickle" % (name))) #Attention inital mode need not be normed (and cannot as metric is not yet known) return pes, start_geo, init_mode, params_dict, atoms, funcart
# # Z-matrix for water: # zmt = ZMat([(None, None, None), (1, None, None), (1, 2, None)], base=1) # # Initial values of internal coordinates: # s = zmt.pinv(x) assert max(abs(s - zmt.pinv(zmt(s)))) < 1.0e-10 clean() with QFunc(atoms, calc) as f: f = Memoize(f, DirStore(salt="h2o, qm")) e = compose(f, zmt) print s, e(s) s, info = minimize(e, s, algo=1, ftol=1.0e-2, xtol=1.0e-2) print s, e(s), info["converged"] # # Internal coordinates: # # In: 0.97843364 0.97826543 1.87152024 # Out: 0.98477091 0.98477695 1.88111918 # # Rigid water with optimized internal coordinates: # X = Rigid(zmt(s))