def read(self): # # The interisting part to read in are the grads and energy, # rest will be ignored afterwards # # FIXME: Somehow we need (sometimes) to pass some time here, # before we can find the gxfile as output. This is especially # valid when running several calculations in parallel. It's # done this way and not with sleep as the problems seems to be # related to a file that should but isn't there. So it makes # sense to read all the files that are there, even if we don't # need the output. # os.system("ls > /dev/null") if isfile('o.' + basename(self.input) + '/trace_output'): # If there are some trace files keep content of them, as # if several calcualtions have been performed after # another, only for the last iteration the trace would be # available f = open('o.' + basename(self.input) + '/trace_output', "r") keep = f.read() f.close() f = open("keep_traces", "a") f.write(keep) f.close() if os.path.exists('gxfile'): __, __, __, __, __, __,__, self.__grads, self.__energy, loopi_d = gxread('gxfile') if self.__energy is not None: return else: print_error ("ParaGauss ERROR: Found no gxfile to read energy or forces from") print_error ("There should be at least the one I created") print_error ("Therefore something very strange happened") print_error ("ERROR: I quit!!") sys.exit(1) self.__energy = self.parse_output(self.output)
def step(self, forces): """Called in a loop by .run() method of Optimizer class with forces computed for current geometry. """ # read the metadata from gxfile in columns: atnums, positions, isyms, inums, iconns, ivars, adds, grads, energy, loop_d = gxread('gxfile') # use current positions as returned by the framework, # in case the on-disk version is outdated (units?): positions = self.atoms.get_positions() # the energy corresponding to the current geometry (units?): energy = self.atoms.get_potential_energy() if (loop_d != self._loop+1): print "WARNING: loop number of gxfile", loop_d, "and intern loop number", self._loop, "differ" # for use in gxfile: self._loop += 1 print "GxOptimizer: loop=", self._loop print "GxOptimizer: energy=", energy print "GxOptimizer: positions=\n", positions print "GxOptimizer: forces=\n", forces # write gxfile to disk, note that energy gradients == - forces (units?): gxwrite(atnums, positions / Bohr, isyms, inums, iconns, ivars, adds, -forces / Hartree * Bohr, energy/Hartree, file='gxfile', loop=self._loop) # run external executable to update geometry: # exitcode = os.system('optimizer.exe') tty = os.popen(self.cmdline,"r") for line in tty: print "PGOptimizer: ", line.rstrip("\n") # the last line of the output should tell if optimizer thinks it is converged: line = line.rstrip("\n") if line.startswith(" optimizer_main: converged="): # in regular runs the last line should tell if convergence was reached: self._converged = line.endswith("T") else: # on errors the last line could be anything, abort by faking convergence: print "GxOptimizer: ERROR! UNEXPECTED OUTPUT FROM EXTRENAL OPTIMIZER!" self._converged = True print "GxOptimizer: converged=", self._converged # read the updated geometry from the gxfile: atnums, positions1, isyms, inums, iconns, ivars, adds, grads, energy, loop_d = gxread('gxfile') positions1 = positions1 * Bohr print "GxOptimizer: new positions=\n", positions1 print "GxOptimizer: step=\n", positions1 - positions self.atoms.set_positions(positions1)
def calculate (self, atoms): """ Calculates the energy and forces with ParaGauss. Uses gxfile to comunicate with the rest of the system. """ # read in actual positions and atomic numbers self.positions = atoms.get_positions().copy() atnums = atoms.get_atomic_numbers().copy() if (self.atnums == None): self.atnums = atnums if len (atnums) != len (self.atnums) or any (array (atnums) != array (self.atnums)): print_error (""" ERROR: (ParaGauss) gxfile does not fit! Gxfile contains wrong atoms! Please delete or change it before restart. """) raise Exception ("gxfile does not fit, delete or adjust!") n = len(self.atnums) loop = 1 # There may be a gxfile from another source make sure it # contains the same meta data than our source: t_gx = {} if os.path.exists('gxfile'): atnums, __, t_gx["isyms"], t_gx["inums"], t_gx["iconns"], t_gx["ivars"], t_gx["additional"], __, __, loop = gxread('gxfile') for dat in self.data.keys(): if (np2.asarray(self.data[dat]) != np2.array(t_gx[dat])).any(): print_error ("ERROR: (ParaGauss) gxfile does not fit!") print_error ("ERROR: (ParaGauss) gxfile contains wrong " + dat +" !") print_error ("Please delete or change it before restart") raise Exception("gxfile does not fit, delete or adjust!") if (np2.array(atnums) != self.atnums).any(): print_error ("ERROR: (ParaGauss) gxfile does not fit!") print_error ("ERROR: (ParaGauss) gxfile contains wrong atoms!") print_error ("Please delete or change it before restart") raise Exception("gxfile does not fit, delete or adjust!") # Needs not to know size of system at init, but soon they will # be needed if "isyms" not in self.data: if "isyms" in t_gx: self.data.update(t_gx) else: def dummy_or_not(at): if at == 0: return 0 else: return 1 self.data["isyms"] = np2.array([dummy_or_not(at) for at in atnums]) self.data["inums"] = np2.array(range(1,n+1)) self.data["iconns"] = np2.zeros((n,3)) self.data["ivars"] = np2.zeros((n,3)) self.data["additional"] = None # Create gxfile with actual geometry for calculation units of # positions should be Bohrs in here, so they are changed gxwrite(self.atnums, self.positions/Bohr, self.data["isyms"], self.data["inums"], self.data["iconns"],\ self.data["ivars"], self.data["additional"], None, None, loop, file='gxfile' ) input = basename(self.input) # FIXME: when copy_inp is True, we will occasionally overwrite # the user supplied input with the version we saved at # construction time over and over again. The danger is the # user may assume he/she can edit the input while the job is # running: copy_inp = (self.copy_input == "always") \ or ((self.copy_input == "inexistent") and not isfile (input)) if copy_inp: # This logic is to warn the user if he/she edits the file # we are supposed to overwrite. FIXME: race condition: if isfile (input): with open (input, "r") as inputfile: if inputfile.read() != self.inputstring: print_error ("WARNING: Changes in", input, "will be overwritten!") print_error (" Consider copy_input=\"inexistent\" or \"never\".") # (Over)writing input here. FIXME: should we skip that if # the content is already the same? with open (input, "w") as inputfile: inputfile.write (self.inputstring) if self.optimizer is not None: with open ("optimizer.input", "w") as optifile: optifile.write (self.optimizer) # The geometry file appears to be used for monitoring the # progress by the user. Write it before starting potentially # long-runnuing process. FIXME: we are supposed to "report" # also computed properties! Therefore we call this once again # after PG finishes: self.report (atoms, "ParaGauss.xyz") # The actual calcualtion starts about here. FIXME: at least # once I did a mistake of specifying the input in the command # line thus letting PG process the same input twice because it # is already appended here: cmd = self.cmdline + [input] if self.silence: stdout = open ("./ParaGauss.out", "w") else: stdout = sys.stdout subprocess.call (cmd, stdout=stdout) if self.silence: stdout.close() # Reads in new energy and forces self.read() # Do it once again, this time also with the valid energy: self.report (atoms, "ParaGauss.xyz") self.converged = True
def __init__(self, input = "input", cmdline = "runpg /users/alexei/exe/openmpi/mainscf_V3.1.4b7-64", silence = True, optimizer = None, copy_input = "always" ): """ Parameters ========== |input| name of the input file wich contains all the informations ParaGauss needs |cmdline| Shell command to start ParaGauss, it will be executed in working directory. A typical command line reads: runpg /users/alexei/exe/openmpi/mainscf_V3.1.4 |silence| if True (is as default) ParaGauss stdout will go to a separate file if False it would go to the normal stdout |optimizer| If optimizer input is needed for a ParaGauss single point calculation the programm takes the content from optimizer and provides it as optimizer.input in the directory the calculation runs |copy_input| Allows three different modes: always (is the default) will create new input file from storage each time a quantum chemistry calculation starts never will never create an input file inexistent will create a new input file for a quantum chemistry calculation if it finds that the file does not exist Both always and inexistent will fetch the input file they will create lateron in the current working directory during initalization """ self.input = input # Command line is stored internally as list of arguments: if type (cmdline) == type (""): cmdline = shlex.split (cmdline) self.cmdline = cmdline self.silence = silence assert (copy_input in ["always", "never", "inexistent"]) self.copy_input = copy_input self.converged = False # I am getting tired of this voodoo, FIXME: factor this out # into a function: if input.startswith("i."): # e.g. i.h2o: input_base_name = input[2:] elif input.endswith(".scm") or input.endswith(".nml"): input_base_name = input[:-4] else: # e.g. input, or anything else: input_base_name = input if input_base_name == "input": self.output = "output" else: self.output = "o." + input_base_name + "/output" # store metadata here, it might be needed (even in another # directory) self.data = {} if not self.copy_input == "never": with open (self.input, "r") as file: self.inputstring = file.read() if optimizer is None: self.optimizer = None else: with open (optimizer, "r") as file: self.optimizer = file.read() # print self.inputstring self.atnums = None # there may be a gxfile from gxoptimizer we must not disturb # its internal coordinates if os.path.exists('gxfile'): self.atnums, __, self.data["isyms"], self.data["inums"], self.data["iconns"], self.data["ivars"], \ self.data["additional"], __, __, loop = gxread('gxfile') # We compare against None in self.report() and elsewhere: self.__energy = None
def read_zmt_from_gx(gx_file): """ Read zmat out from a string, convert to easier to interprete results give back more results than really needed, to have it easier to use them later on OUTPUT: [<Name of Atoms>], [Connectivity matrix, format see ZMat input from zmat.py], [<variable numbers, with possible repitions>], how often a variable was used more than once, [<dihedral angles variable numbers>], (number of Cartesian coordinates covered with zmt, number of interal coordinates of zmt), [<mask for fixed Atoms>] """ from ase.gxfile import gxread from ase.data import chemical_symbols atnums, __, __, inums, iconns, ivars, __, __, __, __ = gxread(gx_file) # For consistency with the rest, transform the numbers to symbols symbols = [chemical_symbols[a] for a in atnums] assert(list(inums) == range(1, len(atnums) + 1)) iconns2 = [] var_names = [] var_names_gx = {} mult = 0 dih_names = [] fixed = [] j = 1 #Iconns should contain connectivities, ivars tell # what to do with them for ic, iv in zip(iconns, ivars): a, b, c = ic new_vars = [] # Three atoms are special, because they do not # contain 3 variables if a == 0: t = () elif b == 0: t = (a-1,) new_vars = [iv[0]] elif c == 0: t = (a-1, b-1,) new_vars = iv[:-1] else: t = (a-1, b-1, c-1) new_vars = iv # connectivities from gx also have the wrong basis # change them and give back as our connectivity matrix iconns2.append(t) # Now find out what is to be done to the corresponding # variables for i, nv in enumerate(new_vars): if nv in var_names_gx.keys(): # They have appeared already once, thus # should go to With_equals var_names.append(var_names_gx[nv]) mult = mult + 1 else: # New variable var_names.append(j) if i == 2: # For finding the shortest path # Attention here numbering starts with 0 # not with 1 as for the var_names dih_names.append(j - 1) if nv == 0: # Normally masked ones are only # considered lateron, gx already # addresses them by giving a 0 to # as their variable number fixed.append(j) else: var_names_gx[nv] = j j = j + 1 iconns = iconns2 # create also a mask if there is something about it if fixed == []: mask = None else: mask = [ i not in fixed for i in var_names] return symbols, iconns, var_names, mult, dih_names, (len(symbols) * 3, j), mask