def test_zopen(self): with zopen(os.path.join(test_dir, "myfile_gz.gz"), mode="rt") as f: self.assertEqual(f.read(), "HelloWorld.\n\n") with zopen(os.path.join(test_dir, "myfile_bz2.bz2"), mode="rt") as f: self.assertEqual(f.read(), "HelloWorld.\n\n") with zopen(os.path.join(test_dir, "myfile_bz2.bz2"), "rt") as f: self.assertEqual(f.read(), "HelloWorld.\n\n") with zopen(os.path.join(test_dir, "myfile"), mode="rt") as f: self.assertEqual(f.read(), "HelloWorld.\n\n")
def read_mol(filename): """ Reads a molecule based on file extension. For example, anything ending in a "xyz" is assumed to be a XYZ file. Supported formats include xyz, gaussian input (gjf|g03|g09|com|inp), Gaussian output (.out|and pymatgen's JSON serialized molecules. Using openbabel, many more extensions are supported but requires openbabel to be installed. Args: filename (str): A filename to read from. Returns: A Molecule object. """ fname = os.path.basename(filename) if fnmatch(fname.lower(), "*.xyz*"): return XYZ.from_file(filename).molecule elif any([fnmatch(fname.lower(), "*.{}*".format(r)) for r in ["gjf", "g03", "g09", "com", "inp"]]): return GaussianInput.from_file(filename).molecule elif any([fnmatch(fname.lower(), "*.{}*".format(r)) for r in ["out", "lis", "log"]]): return GaussianOutput(filename).final_structure elif fnmatch(fname, "*.json*") or fnmatch(fname, "*.mson*"): with zopen(filename) as f: s = json.load(f, cls=MontyDecoder) if type(s) != Molecule: raise IOError("File does not contain a valid serialized " "molecule") return s else: m = re.search("\.(pdb|mol|mdl|sdf|sd|ml2|sy2|mol2|cml|mrv)", filename.lower()) if m: return BabelMolAdaptor.from_file(filename, m.group(1)).pymatgen_mol raise ValueError("Unrecognized file extension!")
def atoms_string_from_file(filename): """ Reads atomic shells from file such as feff.inp or ATOMS file The lines are arranged as follows: x y z ipot Atom Symbol Distance Number with distance being the shell radius and ipot an integer identifying the potential used. Args: filename: File name containing atomic coord data. Returns: Atoms string. """ with zopen(filename, "r") as fobject: f = fobject.readlines() coords = 0 atoms_str = [] for line in f: if coords == 0: find_atoms = line.find("ATOMS") if find_atoms >= 0: coords = 1 if coords == 1: atoms_str.append(line.replace("\r", "")) return ''.join(atoms_str)
def run(self): """ Performs actual nwchem run. """ with zopen(self.output_file, 'w') as fout: return subprocess.Popen(self.nwchem_cmd + [self.input_file], stdout=fout)
def test_potcar_map(self): fe_potcar = zopen(os.path.join(test_dir, "POT_GGA_PAW_PBE", "POTCAR.Fe_pv.gz")).read().decode("utf-8") # specify V instead of Fe - this makes sure the test won't pass if the # code just grabs the POTCAR from the config file (the config file would # grab the V POTCAR) potcar = Potcar(["V"], sym_potcar_map={"V": fe_potcar}) self.assertEqual(potcar.symbols, ["Fe_pv"], "Wrong symbols read in " "for POTCAR")
def read(self): """Read all snapshots from each dump file.""" for dumpfile in self.dumpfiles: with zopen(dumpfile) as f: snapshot = self.read_snapshot(f) while snapshot is not None: self.trajectory.append(snapshot) print(snapshot.timestep, end=' ') sys.stdout.flush() snapshot = self.read_snapshot(f) print() self.trajectory.sort(key=attrgetter('timestep')) self.trajectory.cull() print("read {:d} snapshots".format(self.Nsnaps)) self.trajectory.time_selection.all() self.trajectory.t0_snapshot = self.trajectory[0] if self.dumpattrs: print('Dumped Atom attributes: {}'.format(self.dumpattrs2str())) else: print('No dump column assignments') if 'x' not in self.dumpattrs or 'y' not in self.dumpattrs or \ 'z' not in self.dumpattrs: print('dump scaling status unknown') elif self.Nsnaps > 0: if self.scale_original: self.unscale() elif self.scale_original is None: print('dump scaling status unknown') else: print('dump is already unscaled')
def __init__(self, filename, parse_box=True, dtype=float): """ Args: filename (str): Filename to parse. The timestep wildcard ('*') is supported and the files are parsed in the sequence of timestep. parse_box (bool): Whether parse box info for each step. Default to True. dtype: np.dtype for atoms data array. """ self.filename = filename self.parse_box = parse_box self.dtype = dtype fnames = glob.glob(self.filename) if len(fnames) > 1: pattern = r"%s" % filename.replace("*", "([0-9]+)") pattern = pattern.replace("\\", "\\\\") fnames = sorted(fnames, key=lambda f: int(re.match(pattern, f).group(1))) steps = [] for fname in fnames: with zopen(fname, "rt") as f: run = f.read() dumps = run.split("ITEM: TIMESTEP")[1:] steps.extend([self._parse_timestep(d) for d in dumps]) self.steps = steps self.timesteps = [s["timestep"] for s in self.steps]
def gw_run(self): """ Performs FIESTA (gw) run """ if self.folder != os.getcwd(): init_folder = os.getcwd() os.chdir(self.folder) with zopen(self.log_file, "w") as fout: subprocess.call( [ "mpirun", "-n", str(self.mpi_procs), "fiesta", str(self.grid[0]), str(self.grid[1]), str(self.grid[2]), ], stdout=fout, ) if self.folder != os.getcwd(): os.chdir(init_folder)
def parse_lammps_dumps(file_pattern): """ Generator that parses dump file(s). Args: file_pattern (str): Filename to parse. The timestep wildcard (e.g., dump.atom.'*') is supported and the files are parsed in the sequence of timestep. Yields: LammpsDump for each available snapshot. """ files = glob.glob(file_pattern) if len(files) > 1: pattern = r"%s" % file_pattern.replace("*", "([0-9]+)") pattern = pattern.replace("\\", "\\\\") files = sorted(files, key=lambda f: int(re.match(pattern, f).group(1))) for fname in files: with zopen(fname, "rt") as f: dump_cache = [] for line in f: if line.startswith("ITEM: TIMESTEP"): if len(dump_cache) > 0: yield LammpsDump.from_string("".join(dump_cache)) dump_cache = [line] else: dump_cache.append(line) yield LammpsDump.from_string("".join(dump_cache))
def dumpfn(obj, fn, *args, **kwargs): """ Dump to a json/yaml directly by filename instead of a File-like object. For YAML, PyYAML must be installed. The file type is automatically detected. YAML is assumed if the filename contains "yaml" (lower or upper case). Otherwise, json is always assumed. Furthermore, if pyyaml is compiled with the LibYAML library, the CDumper is automatically chosen for ~10x faster parsing. Args: obj (object): Object to dump. fn (str): filename. \*args: Any of the args supported by json/yaml.dump. \*\*kwargs: Any of the kwargs supported by json/yaml.dump. Returns: (object) Result of json.load. """ with zopen(fn, "wt") as fp: if "yaml" in fn.lower(): if yaml is None: raise RuntimeError("Loading of YAML files is not " "possible as PyYAML is not installed.") if "Dumper" not in kwargs: kwargs["Dumper"] = Dumper yaml.dump(obj, fp, *args, **kwargs) else: fp.write("%s" % json.dumps(obj, *args, **kwargs))
def loadfn(fn, *args, **kwargs): """ Loads json/yaml directly from a filename instead of a File-like object. For YAML, PyYAML must be installed. The file type is automatically detected. YAML is assumed if the filename contains "yaml" (lower or upper case). Otherwise, json is always assumed. Furthermore, if pyyaml is compiled with the LibYAML library, the CLoader is automatically chosen for ~10x faster parsing. Args: fn (str): filename \*args: Any of the args supported by json/yaml.load. \*\*kwargs: Any of the kwargs supported by json/yaml.load. Returns: (object) Result of json/yaml.load. """ with zopen(fn) as fp: if "yaml" in fn.lower(): if yaml is None: raise RuntimeError("Loading of YAML files is not " "possible as PyYAML is not installed.") if "Loader" not in kwargs: kwargs["Loader"] = Loader return yaml.load(fp, *args, **kwargs) else: return json.load(fp, *args, **kwargs)
def read_structure(filename): """ Reads a structure based on file extension. For example, anything ending in a "cif" is assumed to be a Crystallographic Information Format file. Supported formats include CIF, POSCAR/CONTCAR, CHGCAR, LOCPOT, vasprun.xml, CSSR and pymatgen's JSON serialized structures. Args: filename (str): A filename to read from. Returns: A Structure object. """ fname = os.path.basename(filename) if fnmatch(fname.lower(), "*.cif*"): parser = CifParser(filename) return parser.get_structures(True)[0] elif fnmatch(fname, "POSCAR*") or fnmatch(fname, "CONTCAR*"): return Poscar.from_file(filename, False).structure elif fnmatch(fname, "CHGCAR*") or fnmatch(fname, "LOCPOT*"): return Chgcar.from_file(filename).structure elif fnmatch(fname, "vasprun*.xml*"): return Vasprun(filename).final_structure elif fnmatch(fname.lower(), "*.cssr*"): cssr = Cssr.from_file(filename) return cssr.structure elif fnmatch(fname, "*.json*") or fnmatch(fname, "*.mson*"): with zopen(filename) as f: s = json.load(f, cls=PMGJSONDecoder) if type(s) != Structure: raise IOError("File does not contain a valid serialized " "structure") return s raise ValueError("Unrecognized file extension!")
def write_mol(mol, filename): """ Write a molecule to a file based on file extension. For example, anything ending in a "xyz" is assumed to be a XYZ file. Supported formats include xyz, Gaussian input (gjf|g03|g09|com|inp), and pymatgen's JSON serialized molecules. Args: mol (Molecule/IMolecule): Molecule to write filename (str): A filename to write to. """ fname = os.path.basename(filename) if fnmatch(fname.lower(), "*.xyz*"): return XYZ(mol).write_file(filename) elif any([fnmatch(fname.lower(), "*.{}*".format(r)) for r in ["gjf", "g03", "g09", "com", "inp"]]): return GaussianInput(mol).write_file(filename) elif fnmatch(fname, "*.json*") or fnmatch(fname, "*.mson*"): with zopen(filename, "wt") as f: return f.write(str2unicode(json.dumps(mol, cls=MontyEncoder))) else: m = re.search("\.(pdb|mol|mdl|sdf|sd|ml2|sy2|mol2|cml|mrv)", filename.lower()) if m: return BabelMolAdaptor(mol).write_file(filename, m.group(1)) raise ValueError("Unrecognized file extension!")
def write_structure(structure, filename): """ Write a structure to a file based on file extension. For example, anything ending in a "cif" is assumed to be a Crystallographic Information Format file. Supported formats include CIF, POSCAR, CSSR and pymatgen's JSON serialized structures. Args: structure (Structure/IStructure): Structure to write filename (str): A filename to write to. """ fname = os.path.basename(filename) if fnmatch(fname, "*.cif*"): writer = CifWriter(structure) elif fnmatch(fname, "POSCAR*") or fnmatch(fname, "CONTCAR*"): writer = Poscar(structure) elif fnmatch(fname.lower(), "*.cssr*"): writer = Cssr(structure) elif fnmatch(fname, "*.json*") or fnmatch(fname, "*.mson*"): with zopen(filename, "wt") as f: f.write(str2unicode(json.dumps(structure, cls=MontyEncoder))) return else: raise ValueError("Unrecognized file extension!") writer.write_file(filename)
def read_excitation_energies(self): """ Read a excitation energies after a TD-DFT calculation. Returns: A list: A list of tuple for each transition such as [(energie (eV), lambda (nm), oscillatory strength), ... ] """ float_patt = re.compile("\s*([+-]?\d+\.\d+)") transitions = list() # read in file with zopen(self.filename, "r") as f: line = f.readline() td = False while line != "": if re.search("^\sExcitation energies and oscillator strengths:", line): td = True if td: if re.search("^\sExcited State\s*\d", line): val = [float(v) for v in float_patt.findall(line)] transitions.append(tuple(val[0:3])) line = f.readline() return transitions
def store_dataframe_as_json(dataframe, filename, compression=None, orient='split'): """Store pandas dataframe as a json file. Automatically encodes pymatgen objects as dictionaries. Args: dataframe (Pandas.Dataframe): A pandas dataframe. filename (str): Path to json file. compression (str or None): A compression mode. Valid options are "gz", "bz2", and None. Defaults to None. If the filename does not end in with the correct suffix it will be added automatically. orient (str): Determines the format in which the dictionary data is stored. This takes the same set of arguments as the `orient` option in `pandas.DataFrame.to_dict()` function. 'split' is recommended as it is relatively space efficient and preserves the dtype of the index. """ if compression not in ["gz", "bz2", None]: raise ValueError("Supported compression formats are 'gz' and 'bz2'.") if compression and not filename.lower().endswith(".{}".format(compression)): filename = "{}.{}".format(filename, compression) write_type = "wb" if compression else "w" with zopen(filename, write_type) as f: data = json.dumps(dataframe.to_dict(orient=orient), cls=MontyEncoder) if compression: data = data.encode() f.write(data)
def __init__(self, are_coops=False, filename=None): self.are_coops = are_coops if filename is None: filename = "COOPCAR.lobster" if are_coops \ else "COHPCAR.lobster" with zopen(filename, "rt") as f: contents = f.read().split("\n") # The parameters line is the second line in a COHPCAR file. It # contains all parameters that are needed to map the file. parameters = contents[1].split() # Subtract 1 to skip the average num_bonds = int(parameters[0]) - 1 self.efermi = float(parameters[-1]) if int(parameters[1]) == 2: spins = [Spin.up, Spin.down] self.is_spin_polarized = True else: spins = [Spin.up] self.is_spin_polarized = False # The COHP data start in row num_bonds + 3 data = np.array([np.array(row.split(), dtype=float) for row in contents[num_bonds+3:]]).transpose() self.energies = data[0] cohp_data = {"average": {"COHP": {spin: data[1+2*s*(num_bonds+1)] for s, spin in enumerate(spins)}, "ICOHP": {spin: data[2+2*s*(num_bonds+1)] for s, spin in enumerate(spins)}}} orb_cohp = {} for bond in range(num_bonds): bond_data = self._get_bond_data(contents[3+bond]) label = bond_data["label"] orbs = bond_data["orbitals"] cohp = {spin: data[2*(bond+s*(num_bonds+1))+3] for s, spin in enumerate(spins)} icohp = {spin: data[2*(bond+s*(num_bonds+1))+4] for s, spin in enumerate(spins)} if orbs is None: cohp_data[label] = {"COHP": cohp, "ICOHP": icohp, "length": bond_data["length"], "sites": bond_data["sites"]} elif label in orb_cohp: orb_cohp[label].update({bond_data["orb_label"]: {"COHP": cohp, "ICOHP": icohp, "orbitals": orbs}}) else: if label not in cohp_data: cohp_data[label] = {"COHP": None, "ICOHP": None, "length": bond_data["length"], "sites": bond_data["sites"]} orb_cohp[label] = {bond_data["orb_label"]: {"COHP": cohp, "ICOHP": icohp, "orbitals": orbs}} self.orb_res_cohp = orb_cohp if orb_cohp else None self.cohp_data = cohp_data
def write_file(self, filename,cart_coords=False): """ Write the input string into a file Option: see __str__ method """ with zopen(filename, "w") as f: f.write(self.to_string(cart_coords))
def __init__(self, filename): self.filename = filename with zopen(filename) as f: log_bse = f.read() # self.job_info = self._parse_preamble(preamble) self.exiton = self._parse_job(log_bse)
def from_multi_jobs_file(cls, filename): # returns a list of QCInput objects with zopen(filename, 'rt') as f: # the delimiter between QChem jobs is @@@ multi_job_strings = f.read().split("@@@") # list of individual QChem jobs input_list = [cls.from_string(i) for i in multi_job_strings] return input_list
def write_file(self, filename): """ Write out an xr file. Args: filename (str): name of the file to write to. """ with zopen(filename, 'wt') as f: f.write(str(self) + "\n")
def write_file(self, filename='POTENTIALS'): """ Write to file. Args: filename: filename and path to write potential file to. """ with zopen(filename, "wt") as f: f.write(str(self) + "\n")
def __init__(self, filename, occupancy_tolerance=1.): self._occupancy_tolerance = occupancy_tolerance if isinstance(filename, basestring): with zopen(filename, "r") as f: # We use this round-about way to clean up the CIF first. stream = cStringIO.StringIO(_clean_cif(f.read())) self._cif = CifFile.ReadCif(stream) else: self._cif = CifFile.ReadCif(filename)
def write_file(self, filename): """ Writes XYZ to file. Args: filename: File name of output file. """ with zopen(filename, "wt") as f: f.write(self.__str__())
def write_file(self, filename='ATOMS'): """ Write Atoms list to file. Args: filename: path for file to be written """ with zopen(filename, "wt") as f: f.write(str(self) + "\n")
def __init__(self, filename): self.filename = filename with zopen(filename) as f: basis_set = f.read() self.data = self._parse_file(basis_set) # compute the number of nlm orbitals per atom self.data.update(n_nlmo=self.set_n_nlmo())
def write_file(self, filename): """ Write out a CSSR file. Args: filename (str): Filename to write to. """ with zopen(filename, 'wt') as f: f.write(str(self) + "\n")
def write_file(self, filename='PARAMETERS'): """ Write Tags to a Feff parameter tag file. Args: filename: filename and path to write to. """ with zopen(filename, "wt") as f: f.write(self.__str__() + "\n")
def charge_transfer_from_file(filename1, filename2): """ Get charge transfer from file. Args: filename1: name of feff.inp file for run filename2: ldos filename for run, assume consequetive order, .i.e., ldos01.dat, ldos02.dat.... Returns: dictionary of dictionaries in order of potential sites ({"p": 0.154, "s": 0.078, "d": 0.0, "tot": 0.232}, ...) """ cht = OrderedDict() pot_string = FeffPot.pot_string_from_file(filename1) dicts = FeffPot.pot_dict_from_string(pot_string) pot_dict = dicts[1] for i in range(0, len(dicts[0]) + 1): if len(str(i)) == 1: with zopen("{}0{}.dat".format(filename2, i), "r") \ as fobject: f = fobject.readlines() s = float(f[3].split()[2]) p = float(f[4].split()[2]) d = float(f[5].split()[2]) f1 = float(f[6].split()[2]) tot = float(f[1].split()[4]) cht[str(i)] = {pot_dict[i]: {'s': s, 'p': p, 'd': d, 'f': f1, 'tot': tot}} else: with zopen(filename2 + str(i) + ".dat", "r") as fid: f = fid.readlines() s = float(f[3].split()[2]) p = float(f[4].split()[2]) d = float(f[5].split()[2]) f1 = float(f[6].split()[2]) tot = float(f[1].split()[4]) cht[str(i)] = {pot_dict[i]: {'s': s, 'p': p, 'd': d, 'f': f1, 'tot': tot}} return cht
def update_checkpoint(launchpad, launch_dir, launch_id, checkpoint): """ Helper function to update checkpoint Args: launchpad (LaunchPad): LaunchPad to ping with checkpoint data launch_dir (str): directory in which FW_offline.json was created launch_id (int): launch id to update checkpoint (dict): checkpoint data """ if launchpad: launchpad.ping_launch(launch_id, checkpoint=checkpoint) else: fpath = zpath("FW_offline.json") with zopen(fpath) as f_in: d = json.loads(f_in.read()) d['checkpoint'] = checkpoint with zopen(fpath, "wt") as f_out: f_out.write(json.dumps(d, ensure_ascii=False))
def from_file(cls, filename): with zopen(filename, "rt") as f: return cls.from_string(f.read())
def write_file(self, filename): """ Write the cif file. """ with zopen(filename, "wt") as f: f.write(self.__str__())
def load_data(self, filename): """ Load assimilated data from a file """ with zopen(filename, "rt") as f: self._data = json.load(f, cls=MontyDecoder)
def __init__(self, are_coops=False, filename=None): self.are_coops = are_coops if filename is None: filename = "COOPCAR.lobster" if are_coops \ else "COHPCAR.lobster" with zopen(filename, "rt") as f: contents = f.read().split("\n") # The parameters line is the second line in a COHPCAR file. It # contains all parameters that are needed to map the file. parameters = contents[1].split() # Subtract 1 to skip the average num_bonds = int(parameters[0]) - 1 self.efermi = float(parameters[-1]) if int(parameters[1]) == 2: spins = [Spin.up, Spin.down] self.is_spin_polarized = True else: spins = [Spin.up] self.is_spin_polarized = False # The COHP data start in row num_bonds + 3 data = np.array([ np.array(row.split(), dtype=float) for row in contents[num_bonds + 3:] ]).transpose() self.energies = data[0] cohp_data = { "average": { "COHP": { spin: data[1 + 2 * s * (num_bonds + 1)] for s, spin in enumerate(spins) }, "ICOHP": { spin: data[2 + 2 * s * (num_bonds + 1)] for s, spin in enumerate(spins) } } } orb_cohp = {} for bond in range(num_bonds): bond_data = self._get_bond_data(contents[3 + bond]) label = bond_data["label"] orbs = bond_data["orbitals"] cohp = { spin: data[2 * (bond + s * (num_bonds + 1)) + 3] for s, spin in enumerate(spins) } icohp = { spin: data[2 * (bond + s * (num_bonds + 1)) + 4] for s, spin in enumerate(spins) } if orbs is None: cohp_data[label] = { "COHP": cohp, "ICOHP": icohp, "length": bond_data["length"], "sites": bond_data["sites"] } elif label in orb_cohp: orb_cohp[label].update({ bond_data["orb_label"]: { "COHP": cohp, "ICOHP": icohp, "orbitals": orbs } }) else: if label not in cohp_data: cohp_data[label] = { "COHP": None, "ICOHP": None, "length": bond_data["length"], "sites": bond_data["sites"] } orb_cohp[label] = { bond_data["orb_label"]: { "COHP": cohp, "ICOHP": icohp, "orbitals": orbs } } self.orb_res_cohp = orb_cohp if orb_cohp else None self.cohp_data = cohp_data
def __init__( self, chgcar_filename=None, potcar_filename=None, chgref_filename=None, parse_atomic_densities=False, cube_filename=None, ): """ Initializes the Bader caller. Args: chgcar_filename (str): The filename of the CHGCAR. parse_atomic_densities (bool): Optional. turns on atomic partition of the charge density charge densities are atom centered """ if not BADEREXE: raise RuntimeError( "BaderAnalysis requires the executable bader to be in the path." " Please download the library at http://theory.cm.utexas" ".edu/vasp/bader/ and compile the executable.") if not (cube_filename or chgcar_filename): raise ValueError( "You must provide a file! Either a cube file or a CHGCAR") if cube_filename and chgcar_filename: raise ValueError( "You cannot parse a cube and a CHGCAR at the same time!") self.parse_atomic_densities = parse_atomic_densities if chgcar_filename: fpath = os.path.abspath(chgcar_filename) self.is_vasp = True self.chgcar = Chgcar.from_file(chgcar_filename) self.structure = self.chgcar.structure self.potcar = Potcar.from_file( potcar_filename) if potcar_filename is not None else None self.natoms = self.chgcar.poscar.natoms chgrefpath = os.path.abspath( chgref_filename) if chgref_filename else None self.reference_used = bool(chgref_filename) # List of nelects for each atom from potcar potcar_indices = [] for i, v in enumerate(self.natoms): potcar_indices += [i] * v self.nelects = ([ self.potcar[potcar_indices[i]].nelectrons for i in range(len(self.structure)) ] if self.potcar else []) else: fpath = os.path.abspath(cube_filename) self.is_vasp = False self.cube = Cube(fpath) self.structure = self.cube.structure self.nelects = None chgrefpath = os.path.abspath( chgref_filename) if chgref_filename else None self.reference_used = bool(chgref_filename) tmpfile = "CHGCAR" if chgcar_filename else "CUBE" with ScratchDir("."): with zopen(fpath, "rt") as f_in: with open(tmpfile, "wt") as f_out: shutil.copyfileobj(f_in, f_out) args = [BADEREXE, tmpfile] if chgref_filename: with zopen(chgrefpath, "rt") as f_in: with open("CHGCAR_ref", "wt") as f_out: shutil.copyfileobj(f_in, f_out) args += ["-ref", "CHGCAR_ref"] if parse_atomic_densities: args += ["-p", "all_atom"] with subprocess.Popen(args, stdout=subprocess.PIPE, stdin=subprocess.PIPE, close_fds=True) as rs: stdout, stderr = rs.communicate() if rs.returncode != 0: raise RuntimeError( "bader exited with return code %d. Please check your bader installation." % rs.returncode) try: self.version = float(stdout.split()[5]) except ValueError: self.version = -1 # Unknown if self.version < 1.0: warnings.warn( "Your installed version of Bader is outdated, calculation of vacuum charge may be incorrect." ) data = [] with open("ACF.dat") as f: raw = f.readlines() headers = ("x", "y", "z", "charge", "min_dist", "atomic_vol") raw.pop(0) raw.pop(0) while True: l = raw.pop(0).strip() if l.startswith("-"): break vals = map(float, l.split()[1:]) data.append(dict(zip(headers, vals))) for l in raw: toks = l.strip().split(":") if toks[0] == "VACUUM CHARGE": self.vacuum_charge = float(toks[1]) elif toks[0] == "VACUUM VOLUME": self.vacuum_volume = float(toks[1]) elif toks[0] == "NUMBER OF ELECTRONS": self.nelectrons = float(toks[1]) self.data = data if self.parse_atomic_densities: # convert the charge denisty for each atom spit out by Bader into Chgcar objects for easy parsing atom_chgcars = [ Chgcar.from_file(f"BvAt{str(i).zfill(4)}.dat") for i in range(1, len(self.chgcar.structure) + 1) ] atomic_densities = [] # For each atom in the structure for atom, loc, chg in zip( self.chgcar.structure, self.chgcar.structure.frac_coords, atom_chgcars, ): # Find the index of the atom in the charge density atom index = np.round(np.multiply(loc, chg.dim)) data = chg.data["total"] # Find the shift vector in the array shift = (np.divide(chg.dim, 2) - index).astype(int) # Shift the data so that the atomic charge density to the center for easier manipulation shifted_data = np.roll(data, shift, axis=(0, 1, 2)) # Slices a central window from the data array def slice_from_center(data, xwidth, ywidth, zwidth): x, y, z = data.shape startx = x // 2 - (xwidth // 2) starty = y // 2 - (ywidth // 2) startz = z // 2 - (zwidth // 2) return data[startx:startx + xwidth, starty:starty + ywidth, startz:startz + zwidth, ] # Finds the central encompassing volume which holds all the data within a precision def find_encompassing_vol(data, prec=1e-3): total = np.sum(data) for i in range(np.max(data.shape)): sliced_data = slice_from_center(data, i, i, i) if total - np.sum(sliced_data) < 0.1: return sliced_data return None d = { "data": find_encompassing_vol(shifted_data), "shift": shift, "dim": self.chgcar.dim, } atomic_densities.append(d) self.atomic_densities = atomic_densities
def write_file(self, filename): with zopen(filename, "w") as f: f.write(self.__str__())
def from_file(filename): with zopen(filename, 'rt') as f: data=f.read().replace('\n','') return ExcitingInput.from_string(data)
def read_scan(self): """ Read a potential energy surface from a gaussian scan calculation. Returns: A dict: {"energies": [ values ], "coords": {"d1": [ values ], "A2", [ values ], ... }} "energies" are the energies of all points of the potential energy surface. "coords" are the internal coordinates used to compute the potential energy surface and the internal coordinates optimized, labelled by their name as defined in the calculation. """ def floatList(l): """ return a list of float from a list of string """ return [float(v) for v in l] scan_patt = re.compile("^\sSummary of the potential surface scan:") optscan_patt = re.compile( "^\sSummary of Optimized Potential Surface Scan") # data dict return data = {"energies": list(), "coords": dict()} # read in file with zopen(self.filename, "r") as f: line = f.readline() while line != "": if optscan_patt.match(line): f.readline() line = f.readline() endScan = False while not endScan: data["energies"] += floatList(float_patt.findall(line)) line = f.readline() while not re.search("(^\s+(\d+)|^\s-+)", line): icname = line.split()[0].strip() if icname in data["coords"]: data["coords"][icname] += floatList( float_patt.findall(line)) else: data["coords"][icname] = floatList( float_patt.findall(line)) line = f.readline() if re.search("^\s-+", line): endScan = True else: line = f.readline() elif scan_patt.match(line): line = f.readline() data["coords"] = { icname: list() for icname in line.split()[1:-1] } f.readline() line = f.readline() while not re.search("^\s-+", line): values = floatList(line.split()) data["energies"].append(values[-1]) for i, icname in enumerate(data["coords"]): data["coords"][icname].append(values[i + 1]) line = f.readline() else: line = f.readline() return data
def tasks(test_dir): with zopen(test_dir / "test_si_tasks.json.gz") as f: data = json.load(f) return [TaskDocument(**d) for d in data]
def post_process(self, dir_name, d): """ Post-processing for various files other than the vasprun.xml and OUTCAR. Looks for files: transformations.json and custodian.json. Modify this if other output files need to be processed. Args: dir_name: The dir_name. d: Current doc generated. """ logger.info("Post-processing dir:{}".format(dir_name)) fullpath = os.path.abspath(dir_name) # VASP input generated by pymatgen's alchemy has a transformations.json file that tracks # the origin of a particular structure. If such a file is found, it is inserted into the # task doc as d["transformations"] transformations = {} filenames = glob.glob(os.path.join(fullpath, "transformations.json*")) if len(filenames) >= 1: with zopen(filenames[0], "rt") as f: transformations = json.load(f) try: m = re.match("(\d+)-ICSD", transformations["history"][0]["source"]) if m: d["icsd_id"] = int(m.group(1)) except Exception as ex: logger.warning( "Cannot parse ICSD from transformations file.") pass else: logger.warning("Transformations file does not exist.") other_parameters = transformations.get("other_parameters") new_tags = None if other_parameters: # We don't want to leave tags or authors in the # transformations file because they'd be copied into # every structure generated after this one. new_tags = other_parameters.pop("tags", None) new_author = other_parameters.pop("author", None) if new_author: d["author"] = new_author if not other_parameters: # if dict is now empty remove it transformations.pop("other_parameters") d["transformations"] = transformations # Calculations done using custodian has a custodian.json, # which tracks the jobs performed and any errors detected and fixed. # This is useful for tracking what has actually be done to get a # result. If such a file is found, it is inserted into the task doc # as d["custodian"] filenames = glob.glob(os.path.join(fullpath, "custodian.json*")) if len(filenames) >= 1: custodian = [] for fname in filenames: with zopen(fname, "rt") as f: custodian.append(json.load(f)[0]) d["custodian"] = custodian # Convert to full uri path. if self.use_full_uri: d["dir_name"] = get_uri(dir_name) if new_tags: d["tags"] = new_tags # Calculations using custodian generate a *.orig file for the inputs # This is useful to know how the calculation originally started # if such files are found they are inserted into orig_inputs filenames = glob.glob(os.path.join(fullpath, "*.orig*")) if len(filenames) >= 1: d["orig_inputs"] = {} for f in filenames: if "INCAR.orig" in f: d["orig_inputs"]["incar"] = Incar.from_file(f).as_dict() if "POTCAR.orig" in f: d["orig_inputs"]["potcar"] = Potcar.from_file(f).as_dict() if "KPOINTS.orig" in f: d["orig_inputs"]["kpoints"] = Kpoints.from_file( f).as_dict() if "POSCAR.orig" in f: d["orig_inputs"]["poscar"] = Poscar.from_file(f).as_dict() filenames = glob.glob(os.path.join(fullpath, "*.json*")) if self.store_additional_json and filenames: for filename in filenames: key = os.path.basename(filename).split('.')[0] if key != "custodian" and key != "transformations": with zopen(filename, "rt") as f: d[key] = json.load(f) logger.info("Post-processed " + fullpath)
def __init__(self, chgcar_filename, potcar_filename=None, chgref_filename=None, parse_atomic_densities=False): """ Initializes the Bader caller. Args: chgcar_filename (str): The filename of the CHGCAR. potcar_filename (str): Optional: the filename of the corresponding POTCAR file. Used for calculating the charge transfer. If None, the get_charge_transfer method will raise a ValueError. chgref_filename (str): Optional. The filename of the reference CHGCAR, which calculated by AECCAR0 + AECCAR2. (See http://theory.cm.utexas.edu/henkelman/code/bader/ for details.) parse_atomic_densities (bool): Optional. turns on atomic partition of the charge density charge densities are atom centered """ if not BADEREXE: raise RuntimeError( "BaderAnalysis requires the executable bader to be in the path." " Please download the library at http://theory.cm.utexas" ".edu/vasp/bader/ and compile the executable.") self.chgcar = Chgcar.from_file(chgcar_filename) self.potcar = Potcar.from_file(potcar_filename) \ if potcar_filename is not None else None self.natoms = self.chgcar.poscar.natoms chgcarpath = os.path.abspath(chgcar_filename) chgrefpath = os.path.abspath( chgref_filename) if chgref_filename else None self.reference_used = True if chgref_filename else False self.parse_atomic_densities = parse_atomic_densities with ScratchDir(".") as temp_dir: with zopen(chgcarpath, 'rt') as f_in: with open("CHGCAR", "wt") as f_out: shutil.copyfileobj(f_in, f_out) args = [BADEREXE, "CHGCAR"] if chgref_filename: with zopen(chgrefpath, 'rt') as f_in: with open("CHGCAR_ref", "wt") as f_out: shutil.copyfileobj(f_in, f_out) args += ['-ref', 'CHGCAR_ref'] if parse_atomic_densities: args += ['-p', 'all_atom'] rs = subprocess.Popen(args, stdout=subprocess.PIPE, stdin=subprocess.PIPE, close_fds=True) stdout, stderr = rs.communicate() if rs.returncode != 0: raise RuntimeError("bader exited with return code %d. " "Please check your bader installation." % rs.returncode) try: self.version = float(stdout.split()[5]) except: self.version = -1 # Unknown if self.version < 1.0: warnings.warn('Your installed version of Bader is outdated, ' 'calculation of vacuum charge may be incorrect.') data = [] with open("ACF.dat") as f: raw = f.readlines() headers = ('x', 'y', 'z', 'charge', 'min_dist', 'atomic_vol') raw.pop(0) raw.pop(0) while True: l = raw.pop(0).strip() if l.startswith("-"): break vals = map(float, l.split()[1:]) data.append(dict(zip(headers, vals))) for l in raw: toks = l.strip().split(":") if toks[0] == "VACUUM CHARGE": self.vacuum_charge = float(toks[1]) elif toks[0] == "VACUUM VOLUME": self.vacuum_volume = float(toks[1]) elif toks[0] == "NUMBER OF ELECTRONS": self.nelectrons = float(toks[1]) self.data = data if self.parse_atomic_densities: # convert the charge denisty for each atom spit out by Bader into Chgcar objects for easy parsing atom_chgcars = [ Chgcar.from_file("BvAt{}.dat".format(str(i).zfill(4))) for i in range(1, len(self.chgcar.structure) + 1) ] atomic_densities = [] # For each atom in the structure for atom, loc, chg in zip(self.chgcar.structure, self.chgcar.structure.frac_coords, atom_chgcars): # Find the index of the atom in the charge density atom index = np.round(np.multiply(loc, chg.dim)) data = chg.data['total'] # Find the shift vector in the array shift = (np.divide(chg.dim, 2) - index).astype(int) # Shift the data so that the atomic charge density to the center for easier manipulation shifted_data = np.roll(data, shift, axis=(0, 1, 2)) # Slices a central window from the data array def slice_from_center(data, xwidth, ywidth, zwidth): x, y, z = data.shape startx = x // 2 - (xwidth // 2) starty = y // 2 - (ywidth // 2) startz = z // 2 - (zwidth // 2) return data[startx:startx + xwidth, starty:starty + ywidth, startz:startz + zwidth] # Finds the central encompassing volume which holds all the data within a precision def find_encompassing_vol(data, prec=1e-3): total = np.sum(data) for i in range(np.max(data.shape)): sliced_data = slice_from_center(data, i, i, i) if total - np.sum(sliced_data) < 0.1: return sliced_data return None d = { "data": find_encompassing_vol(shifted_data), "shift": shift, "dim": self.chgcar.dim } atomic_densities.append(d) self.atomic_densities = atomic_densities
def run(self, pdb_on_exception=False): """ Run the rocket (check out a job from the database and execute it) Args: pdb_on_exception (bool): whether to invoke the debugger on a caught exception. Default False. """ all_stored_data = {} # combined stored data for *all* the Tasks all_update_spec = {} # combined update_spec for *all* the Tasks all_mod_spec = [] # combined mod_spec for *all* the Tasks lp = self.launchpad launch_dir = os.path.abspath(os.getcwd()) logdir = lp.get_logdir() if lp else None l_logger = get_fw_logger('rocket.launcher', l_dir=logdir, stream_level='INFO') # check a FW job out of the launchpad if lp: m_fw, launch_id = lp.checkout_fw(self.fworker, launch_dir, self.fw_id) else: # offline mode m_fw = Firework.from_file(os.path.join(os.getcwd(), "FW.json")) # set the run start time with open('FW_offline.json', 'r+') as f: d = json.loads(f.read()) d['started_on'] = datetime.utcnow().isoformat() f.seek(0) f.write(json.dumps(d)) f.truncate() launch_id = None # we don't need this in offline mode... if not m_fw: print("No FireWorks are ready to run and match query! {}".format( self.fworker.query)) return False final_state = None ping_stop = None btask_stops = [] try: if '_launch_dir' in m_fw.spec and lp: prev_dir = launch_dir launch_dir = os.path.expandvars(m_fw.spec['_launch_dir']) if not os.path.abspath(launch_dir): launch_dir = os.path.normpath( os.path.join(os.getcwd(), launch_dir)) # thread-safe "mkdir -p" try: os.makedirs(launch_dir) except OSError as exception: if exception.errno != errno.EEXIST: raise os.chdir(launch_dir) if not os.path.samefile(launch_dir, prev_dir): lp.change_launch_dir(launch_id, launch_dir) if not os.listdir(prev_dir) and REMOVE_USELESS_DIRS: try: os.rmdir(prev_dir) except: pass recovery = m_fw.spec.get('_recovery', None) if recovery: recovery_dir = recovery.get('_prev_dir') recovery_mode = recovery.get('_mode') starting_task = recovery.get('_task_n') all_stored_data.update(recovery.get('_all_stored_data')) all_update_spec.update(recovery.get('_all_update_spec')) all_mod_spec.extend(recovery.get('_all_mod_spec')) if lp: l_logger.log( logging.INFO, 'Recovering from task number {} in folder {}.'.format( starting_task, recovery_dir)) if recovery_mode == 'cp' and launch_dir != recovery_dir: if lp: l_logger.log( logging.INFO, 'Copying data from recovery folder {} to folder {}.' .format(recovery_dir, launch_dir)) distutils.dir_util.copy_tree(recovery_dir, launch_dir, update=1) else: starting_task = 0 files_in = m_fw.spec.get("_files_in", {}) prev_files = m_fw.spec.get("_files_prev", {}) for f in set(files_in.keys()).intersection(prev_files.keys()): # We use zopen for the file objects for transparent handling # of zipped files. shutil.copyfileobj does the actual copy # in chunks that avoid memory issues. with zopen(prev_files[f], "rb") as fin, zopen(files_in[f], "wb") as fout: shutil.copyfileobj(fin, fout) if lp: message = 'RUNNING fw_id: {} in directory: {}'.\ format(m_fw.fw_id, os.getcwd()) l_logger.log(logging.INFO, message) # write FW.json and/or FW.yaml to the directory if PRINT_FW_JSON: m_fw.to_file('FW.json', indent=4) if PRINT_FW_YAML: m_fw.to_file('FW.yaml') my_spec = dict( m_fw.spec) # make a copy of spec, don't override original my_spec["_fw_env"] = self.fworker.env # set up heartbeat (pinging the server that we're still alive) ping_stop = start_ping_launch(lp, launch_id) # start background tasks if '_background_tasks' in my_spec: for bt in my_spec['_background_tasks']: btask_stops.append(start_background_task(bt, m_fw.spec)) # execute the Firetasks! for t_counter, t in enumerate(m_fw.tasks[starting_task:], start=starting_task): checkpoint = { '_task_n': t_counter, '_all_stored_data': all_stored_data, '_all_update_spec': all_update_spec, '_all_mod_spec': all_mod_spec } self.update_checkpoint(lp, launch_id, checkpoint) if lp: l_logger.log(logging.INFO, "Task started: %s." % t.fw_name) if my_spec.get("_add_launchpad_and_fw_id"): t.fw_id = m_fw.fw_id if FWData().MULTIPROCESSING: # hack because AutoProxy manager can't access attributes t.launchpad = LaunchPad.from_dict( self.launchpad.to_dict()) else: t.launchpad = self.launchpad if my_spec.get("_add_fworker"): t.fworker = self.fworker try: m_action = t.run_task(my_spec) except BaseException as e: traceback.print_exc() tb = traceback.format_exc() stop_backgrounds(ping_stop, btask_stops) do_ping( lp, launch_id) # one last ping, esp if there is a monitor # If the exception is serializable, save its details if pdb_on_exception: pdb.post_mortem() try: exception_details = e.to_dict() except AttributeError: exception_details = None except BaseException as e: if lp: l_logger.log( logging.WARNING, "Exception couldn't be serialized: %s " % e) exception_details = None try: m_task = t.to_dict() except: m_task = None m_action = FWAction(stored_data={ '_message': 'runtime error during task', '_task': m_task, '_exception': { '_stacktrace': tb, '_details': exception_details } }, exit=True) m_action = self.decorate_fwaction(m_action, my_spec, m_fw, launch_dir) if lp: final_state = 'FIZZLED' lp.complete_launch(launch_id, m_action, final_state) else: with open('FW_offline.json', 'r+') as f: d = json.loads(f.read()) d['fwaction'] = m_action.to_dict() d['state'] = 'FIZZLED' d['completed_on'] = datetime.utcnow().isoformat() f.seek(0) f.write(json.dumps(d)) f.truncate() return True # read in a FWAction from a file, in case the task is not Python and cannot return # it explicitly if os.path.exists('FWAction.json'): m_action = FWAction.from_file('FWAction.json') elif os.path.exists('FWAction.yaml'): m_action = FWAction.from_file('FWAction.yaml') if not m_action: m_action = FWAction() # update the global stored data with the data to store and update from this # particular Task all_stored_data.update(m_action.stored_data) all_update_spec.update(m_action.update_spec) all_mod_spec.extend(m_action.mod_spec) # update spec for next task as well my_spec.update(m_action.update_spec) for mod in m_action.mod_spec: apply_mod(mod, my_spec) if lp: l_logger.log(logging.INFO, "Task completed: %s " % t.fw_name) if m_action.skip_remaining_tasks: break # add job packing info if this is needed if FWData().MULTIPROCESSING and STORE_PACKING_INFO: all_stored_data[ 'multiprocess_name'] = multiprocessing.current_process( ).name # perform finishing operation stop_backgrounds(ping_stop, btask_stops) for b in btask_stops: b.set() do_ping(lp, launch_id) # one last ping, esp if there is a monitor # last background monitors if '_background_tasks' in my_spec: for bt in my_spec['_background_tasks']: if bt.run_on_finish: for task in bt.tasks: task.run_task(m_fw.spec) m_action.stored_data = all_stored_data m_action.mod_spec = all_mod_spec m_action.update_spec = all_update_spec m_action = self.decorate_fwaction(m_action, my_spec, m_fw, launch_dir) if lp: final_state = 'COMPLETED' lp.complete_launch(launch_id, m_action, final_state) else: with open('FW_offline.json', 'r+') as f: d = json.loads(f.read()) d['fwaction'] = m_action.to_dict() d['state'] = 'COMPLETED' d['completed_on'] = datetime.utcnow().isoformat() f.seek(0) f.write(json.dumps(d)) f.truncate() return True except LockedWorkflowError as e: l_logger.log(logging.DEBUG, traceback.format_exc()) l_logger.log( logging.WARNING, "Firework {} reached final state {} but couldn't complete the update of " "the database. Reason: {}\nRefresh the WF to recover the result " "(lpad admin refresh -i {}).".format(self.fw_id, final_state, e, self.fw_id)) return True except: # problems while processing the results. high probability of malformed data. traceback.print_exc() stop_backgrounds(ping_stop, btask_stops) # restore initial state to prevent the raise of further exceptions if lp: lp.restore_backup_data(launch_id, m_fw.fw_id) do_ping(lp, launch_id) # one last ping, esp if there is a monitor # the action produced by the task is discarded m_action = FWAction(stored_data={ '_message': 'runtime error during task', '_task': None, '_exception': { '_stacktrace': traceback.format_exc(), '_details': None } }, exit=True) try: m_action = self.decorate_fwaction(m_action, my_spec, m_fw, launch_dir) except: traceback.print_exc() if lp: try: lp.complete_launch(launch_id, m_action, 'FIZZLED') except LockedWorkflowError as e: l_logger.log(logging.DEBUG, traceback.format_exc()) l_logger.log( logging.WARNING, "Firework {} fizzled but couldn't complete the update of the database." " Reason: {}\nRefresh the WF to recover the result " "(lpad admin refresh -i {}).".format( self.fw_id, final_state, e, self.fw_id)) return True else: with open('FW_offline.json', 'r+') as f: d = json.loads(f.read()) d['fwaction'] = m_action.to_dict() d['state'] = 'FIZZLED' d['completed_on'] = datetime.utcnow().isoformat() f.seek(0) f.write(json.dumps(d)) f.truncate() return True
def write_cif(self): """ Generates and writes a structure file (.cif) from SPuDS output.txt """ def parse_spuds_out(self): """ Read SPuDS output.txt and get predicted structure info Returns: site_list: list of lists of site info list([element, multiplicity, Wycoff label, x-coord, y-coord, z-coord, site occupancy]) a_len: SPuDS a lattice length (Ang) b_len: SPuDS b lattice length (Ang) c_len: SPuDS c lattice length (Ang) alp: SPuDS alpha lattice angle (deg) bet: SPuDS beta lattice angle (deg) gam: SPuDS gamma lattice angle (deg) """ a_lat,b_lat,c_lat,alp,bet,gam = None,None,None,None,None,None dvdr = ['***********************************'+ '***********************************'] site_list = [] with open(os.path.join(self.SPuDS_dir,'output.txt'),'r') as f: linecount = 0 count = 0 for line in f: linelist = line.split() if linelist == dvdr: count += 1 if count == 2: if len(linelist) == 4: if linelist[0] == 'Space' and linelist[1] == 'group': # Get dict key for retrieving symmetry operations self.symops_key = " ".join([linelist[2],linelist[3]]) if len(linelist) == 3: # Get lattice lengths (Ang) if linelist[0] == 'a': a_lat = linelist[2] if linelist[0] == 'b': b_lat = linelist[2] if linelist[0] == 'c': c_lat = linelist[2] # Get lattice angles (deg) if linelist[0] == 'alpha': alp = linelist[2] if linelist[0] == 'beta': bet = linelist[2] if linelist[0] == 'gamma': gam = linelist[2] if len(linelist) > 3: # Get site_list for el in self.ellist: if linelist[0] == el: if el == self.Xel: # Anion should be fully occupied occup = 1.00 else: # Cations can be partially occupied occup = linelist[5] multsite = re.split('(\d+)',linelist[1]) mult = multsite[1] wycoff = multsite[2] x = linelist[2] y = linelist[3] z = linelist[4] # Append site info to site_list site_list.append([el,mult,wycoff, x,y,z,occup]) linecount += 1 f.close() # Add count labels to site_list elcounts = {} for d in site_list: for el in self.ellist: if d[0] == el: if el not in elcounts.keys(): elcounts[el] = 0 elcounts[el] += 1 for k,v in elcounts.items(): count = 1 ii = 0 for d in site_list: if d[0] == k: if v > 1: label = k+str(count) site_list[ii].insert(0,label) count += 1 else: label = k site_list[ii].insert(0,label) ii += 1 return site_list, a_lat, b_lat, c_lat, alp, bet, gam def map_lattice_menu_1(self,a_len,b_len,c_len,alp,bet,gam): """ Map to .cif compatible lattice parameters from SPuDS output.txt Args: a_len: SPuDS a lattice length (Ang) b_len: SPuDS b lattice length (Ang) c_len: SPuDS c lattice length (Ang) alp: SPuDS alpha lattice angle (deg) bet: SPuDS beta lattice angle (deg) gam: SPuDS gamma lattice angle (deg) Returns: .cif compatible lattice parameters: a: a lattice length (Ang) b: b lattice length (Ang) c: c lattice length (Ang) alpha: alpha lattice angle (deg) beta: beta lattice angle (deg) gamma: gamma lattice angle (deg) """ # Lattice angles (deg) if self.tilt in [3,5,10,16,17,20,21,22,23]: alpha = 90 beta = 90 gamma = 90 elif self.tilt == 13: alpha = 90 beta = bet gamma = 90 elif self.tilt == 14: alpha = 90 beta = 90 gamma = 120 # Lattice lengths (Ang) if self.tilt in [3,23]: a = a_len b = a_len c = a_len elif self.tilt in [5,14,16,21,22]: a = a_len b = a_len c = c_len elif self.tilt in [10,13,17,20]: a = a_len b = b_len c = c_len return a,b,c,alpha,beta,gamma def make_cif(self): """ Generates a pymatgen CifFile object using structure info parsed from SPuDS output.txt. Returns: cf: pymatgen CifFile object """ # SPuDS ouput structure info site_list,a_lat,b_lat,c_lat,alp,bet,gam = parse_spuds_out(self) # Mapped lattice parameters to .cif compatibility a,b,c,alpha,beta,gamma = map_lattice_menu_1(self, a_lat,b_lat,c_lat, alp,bet,gam) symd = self.symops_dict[self.symops_key] # symops dict data # Create dict of .cif parameters data = {} data['_cell_length_a'] = a data['_cell_length_b'] = b data['_cell_length_c'] = c data['_cell_angle_alpha'] = alpha data['_cell_angle_beta'] = beta data['_cell_angle_gamma'] = gamma data['_space_group_name_H-M_alt'] = symd['name'] data['_symmetry_Int_tables_number'] = symd['number'] data['_symmetry_cell_setting'] = symd['latsym'] data['_space_group_symop_operation_xyz'] = symd['symops'] data['_atom_type_symbol'] = self.ellist data['_atom_type_oxidation_number'] = self.oxilist data['_atom_site_label'] = [d[0] for d in site_list] data['_atom_site_type_symbol'] = [d[1] for d in site_list] data['_atom_site_symmetry_multiplicity'] = [d[2] for d in site_list] data['_atom_site_Wycoff_symbol'] = [d[3] for d in site_list] data['_atom_site_fract_x'] = [d[4] for d in site_list] data['_atom_site_fract_y'] = [d[5] for d in site_list] data['_atom_site_fract_z'] = [d[6] for d in site_list] data['_atom_site_occupancy'] = [d[7] for d in site_list] # .cif file header cif_header = 'SPuDS' # .cif file loops cif_loops = [['_space_group_symop_operation_xyz'], ['_atom_type_symbol','_atom_type_oxidation_number'], ['_atom_site_label','_atom_site_type_symbol', '_atom_site_symmetry_multiplicity', '_atom_site_Wycoff_symbol','_atom_site_fract_x', '_atom_site_fract_y','_atom_site_fract_z', '_atom_site_occupancy']] # Create CifFile object d = OrderedDict() d[self.formula] = CifBlock(data,cif_loops,cif_header) cf = CifFile(d) return cf # .cif file nameing scheme self.cif_file = str(self.tilt)+'_'+self.formula+'.cif' # Generate pymatgen CifFile object cf = make_cif(self) # Write created .cif file to SPuDS_dir with zopen(os.path.join(self.SPuDS_dir,self.cif_file), "wt") as f: f.write(cf.__str__()) f.close()
def write_file(self, filename="paths.dat"): """ Write paths.dat. """ with zopen(filename, "wt") as f: f.write(str(self) + "\n")
def _parse(self, filename): start_patt = re.compile(" \(Enter \S+l101\.exe\)") route_patt = re.compile(" #[pPnNtT]*.*") charge_mul_patt = re.compile("Charge\s+=\s*([-\\d]+)\s+" "Multiplicity\s+=\s*(\d+)") num_basis_func_patt = re.compile("([0-9]+)\s+basis functions") pcm_patt = re.compile("Polarizable Continuum Model") stat_type_patt = re.compile("imaginary frequencies") scf_patt = re.compile("E\(.*\)\s*=\s*([-\.\d]+)\s+") mp2_patt = re.compile("EUMP2\s*=\s*(.*)") oniom_patt = re.compile("ONIOM:\s+extrapolated energy\s*=\s*(.*)") termination_patt = re.compile("(Normal|Error) termination of Gaussian") std_orientation_patt = re.compile("Standard orientation") end_patt = re.compile("--+") orbital_patt = re.compile("Alpha\s*\S+\s*eigenvalues --(.*)") thermo_patt = re.compile("(Zero-point|Thermal) correction(.*)=" "\s+([\d\.-]+)") self.properly_terminated = False self.is_pcm = False self.stationary_type = "Minimum" self.structures = [] self.corrections = {} self.energies = [] self.pcm = None coord_txt = [] read_coord = 0 orbitals_txt = [] parse_stage = 0 num_basis_found = False terminated = False with zopen(filename) as f: for line in f: if parse_stage == 0: if start_patt.search(line): parse_stage = 1 elif route_patt.search(line): self.route = {} for tok in line.split(): sub_tok = tok.strip().split("=") key = sub_tok[0].upper() self.route[key] = sub_tok[1].upper() \ if len(sub_tok) > 1 else "" m = re.match("(\w+)/([^/]+)", key) if m: self.functional = m.group(1) self.basis_set = m.group(2) elif parse_stage == 1: if charge_mul_patt.search(line): m = charge_mul_patt.search(line) self.charge = int(m.group(1)) self.spin_mult = int(m.group(2)) parse_stage = 2 elif parse_stage == 2: if self.is_pcm: self._check_pcm(line) if "FREQ" in self.route and thermo_patt.search(line): m = thermo_patt.search(line) if m.group(1) == "Zero-point": self.corrections["Zero-point"] = float(m.group(3)) else: key = m.group(2).strip(" to ") self.corrections[key] = float(m.group(3)) if read_coord: if not end_patt.search(line): coord_txt.append(line) else: read_coord = (read_coord + 1) % 4 if not read_coord: sp = [] coords = [] for l in coord_txt[2:]: toks = l.split() sp.append(Element.from_Z(int(toks[1]))) coords.append(map(float, toks[3:6])) self.structures.append(Molecule(sp, coords)) elif termination_patt.search(line): m = termination_patt.search(line) if m.group(1) == "Normal": self.properly_terminated = True terminated = True elif (not num_basis_found) and \ num_basis_func_patt.search(line): m = num_basis_func_patt.search(line) self.num_basis_func = int(m.group(1)) num_basis_found = True elif (not self.is_pcm) and pcm_patt.search(line): self.is_pcm = True self.pcm = {} elif "FREQ" in self.route and "OPT" in self.route and \ stat_type_patt.search(line): self.stationary_type = "Saddle" elif mp2_patt.search(line): m = mp2_patt.search(line) self.energies.append(float(m.group(1).replace("D", "E"))) elif oniom_patt.search(line): m = oniom_patt.matcher(line) self.energies.append(float(m.group(1))) elif scf_patt.search(line): m = scf_patt.search(line) self.energies.append(float(m.group(1))) elif std_orientation_patt.search(line): coord_txt = [] read_coord = 1 elif orbital_patt.search(line): orbitals_txt.append(line) if not terminated: raise IOError("Bad Gaussian output file.")
def write(self, file_name): with zopen(file_name, "wt") as f: f.write(self.get_str())