def loadcon(filein, reset=True): ''' Load a con file filein: may be either a filename or a file-like object ''' if hasattr(filein, 'readline'): con = filein else: con = open(filein, 'r') con.readline() # line 1: comment con.readline() # line 2: comment # determine how many dimensions tmp = numpy.array(con.readline().split()) # line 3: Box lengths for i in range(len(tmp)): dim = i + 1 try: float(tmp[i]) except: dim = i break # handle the box boxlengths = numpy.zeros(dim) for i in range(dim): boxlengths[i] = float(tmp[i]) boxangles = numpy.array([float(f) for f in con.readline().split()[0:dim] ]) # line 4: Box angles boxtemp = numpy.zeros((dim, dim), 'd') boxtemp = length_angle_to_box(boxlengths, boxangles) con.readline() # line 5: comment con.readline() # line 6: comment num_types = int(con.readline().split()[0]) # line 7: number of atom types num_each_type = con.readline().split( ) # line 8: number of each type of atom mass_of_type = con.readline().split() # line 9: mass of each type of atom num_atoms = 0 for i in range(num_types): num_each_type[i] = int(num_each_type[i]) mass_of_type[i] = float(mass_of_type[i]) num_atoms += num_each_type[i] a = atoms.Atoms(num_atoms) a.box = boxtemp index = 0 for i in range(num_types): name = con.readline().strip() if abs(1.0 - mass_of_type[i]) < 1e-6 and name != "H": logger.warning("WARNING: Mass of %s set to 1.0", name) con.readline() # skip meaningless line for j in range(num_each_type[i]): vals = con.readline().split() for k in range(dim): a.r[index][k] = float(vals[k]) a.mass[index] = mass_of_type[i] a.names[index] = name if not int(vals[dim]) == 0: a.free[index] = 0 index += 1 if reset: con.seek(0) return a
def loadposcar(filein): ''' Load the POSCAR file named filename and returns an atoms object ''' if hasattr(filein, 'readline'): f = filein else: f = open(filein, 'r') # Line 1: Atom types AtomTypes = f.readline().split() # Line 2: scaling of coordinates scale = float(f.readline()) # Lines 3-5: the box box = numpy.zeros((3, 3)) for i in range(3): line = f.readline().split() box[i] = numpy.array([float(line[0]), float(line[1]), float(line[2])]) * scale # Line 6: number of atoms of each type. line = f.readline().split() NumAtomsPerType = [] for l in line: NumAtomsPerType.append(int(l)) # Now have enough info to make the atoms object. num_atoms = sum(NumAtomsPerType) p = atoms.Atoms(num_atoms) # Fill in the box. p.box = box # Line 7: selective or cartesian sel = f.readline()[0] selective_flag = (sel == 's' or sel == 'S') if not selective_flag: car = sel else: car = f.readline()[0] direct_flag = not (car == 'c' or car == 'C' or car == 'k' or car == 'K') atom_index = 0 for i in range(len(NumAtomsPerType)): for j in range(NumAtomsPerType[i]): p.names[atom_index] = AtomTypes[i] line = f.readline().split() if (selective_flag): assert len(line) >= 6 else: assert len(line) >= 3 pos = line[0:3] if selective_flag: sel = line[3:7] if sel[0] == 'T' or sel[0] == 't': p.free[atom_index] = 1 elif sel[0] == 'F' or sel[0] == 'f': p.free[atom_index] = 0 p.r[atom_index] = numpy.array([float(q) for q in pos]) if direct_flag: p.r[atom_index] = numpy.dot(p.r[atom_index], p.box) else: p.r[atom_index] *= scale atom_index += 1 return p
import pathfix import io, atoms confile = sys.argv[1] xscale = int(sys.argv[2]) yscale = int(sys.argv[3]) zscale = int(sys.argv[4]) try: outcon = sys.argv[5] except: outcon = "repeat-" + confile p0 = io.loadcon(sys.argv[1]) t0 = atoms.Atoms(0) for x in range(xscale): for a in range(len(p0)): t0.r = numpy.append(t0.r, [p0.r[a] + p0.box[0] * x], 0) t0.free = numpy.append(t0.free, p0.free[a]) t0.names.append(p0.names[a]) t0.mass = numpy.append(t0.mass, p0.mass[a]) t1 = atoms.Atoms(0) for y in range(yscale): for a in range(len(t0)): t1.r = numpy.append(t1.r, [t0.r[a] + p0.box[1] * y], 0) t1.free = numpy.append(t1.free, t0.free[a]) t1.names.append(t0.names[a]) t1.mass = numpy.append(t1.mass, t0.mass[a]) t2 = atoms.Atoms(0) for z in range(zscale):
def read_castep_output(castep_file, cluster=None, abort=True): """Parse .castep file, and return Atoms object with positions, energy, forces, and possibly stress and atomic populations as well""" opened = False if type(castep_file) == type(''): opened = True castep_file = open(castep_file, 'r') castep_output = [] got_header = False while True: line = castep_file.readline() if line == '': break castep_output.append(line) if line == ' | CCC AA SSS TTTTT EEEEE PPPP |\n': if got_header: break else: got_header = True if opened: castep_file.close() # NB: CASTEP doesn't always print 'Total time' run_time = -1.0 if abort: total_time = filter(lambda s: s.startswith('Total time'), castep_output) if total_time == []: has_converged = filter( lambda s: s.startswith('Total energy has converged'), castep_output) if has_converged == []: raise ValueError("castep didn't complete") else: run_time = float(total_time[0].split()[3]) # Now we should have contents of a valid .castep file in castep_output # First let's read the user parameters for this run from top of file param = CastepParam() param.read_from_castep_output(castep_output) # Next let's extract the lattice and atomic positions try: lattice_line = castep_output.index( ' Unit Cell\n') except: raise ValueError('No unit cell found in castep file') lattice_lines = castep_output[lattice_line + 3:lattice_line + 6] R1 = map(float, lattice_lines[0].split()[0:3]) R2 = map(float, lattice_lines[1].split()[0:3]) R3 = map(float, lattice_lines[2].split()[0:3]) lattice = numpy.array([R1, R2, R3]) try: cell_first_line = castep_output.index( ' Cell Contents\n') except ValueError: raise ValueError('No cell contents found in castep file') n_atoms = int(castep_output[cell_first_line + 2].split()[-1]) if cluster is not None: # If we were passed in an Atoms object, construct mapping from # CASTEP (element, number) to original atom index cluster = cluster.copy() species_count = {} lookup = {} for i in range(cluster.n): el = cluster.species[i] if species_count.has_key(el): species_count[el] += 1 else: species_count[el] = 1 lookup[(el, species_count[el])] = i else: # Otherwise we make a new, empty Atoms object. Atoms will # be ordered as they are in .castep file. lookup = {} cluster = atoms.Atoms(n=n_atoms, lattice=lattice) cluster.params['castep_run_time'] = run_time cell_lines = castep_output[cell_first_line + 10:cell_first_line + 10 + n_atoms] # Fill in species and fractional positions cluster.add_property('frac_pos', 0.0, ncols=3) for i, line in enumerate(cell_lines): x1, el, num, u, v, w, x2 = line.split() num = int(num) if not (el, num) in lookup: lookup[(el, num)] = i cluster.species[lookup[(el, num)]] = el cluster.frac_pos[lookup[(el, num)]] = numpy.array(map(float, (u, v, w))) # Calculate cartesian postions from fractional positions cluster.pos[:] = numpy.array([ numpy.dot(cluster.frac_pos[i, :], cluster.lattice) for i in range(cluster.n) ]) if ((param.has_key('calculate_stress') and param['calculate_stress'].lower() == 'true') or (param.has_key('finite_basis_corr') and param['finite_basis_corr'].lower() == 'manual')): energy_lines = filter(lambda s: s.startswith(' Total energy corrected for finite basis set'), \ castep_output) elif param.has_key( 'task') and param['task'].lower() == 'geometryoptimization': energy_lines = filter(lambda s: s.startswith(' BFGS: Final Enthalpy'), castep_output) else: energy_lines = filter( lambda s: s.startswith('Final energy') and not s.endswith( '<- EDFT\n'), castep_output) if (len(energy_lines) == 0): if abort: raise ValueError('No total energy found in castep file') else: # Energy is second to last field on line (last is "eV") # Use last matching energy line in file cluster.params['energy'] = float(energy_lines[-1].split()[-2]) try: for fn in ('Forces', 'Symmetrised Forces'): force_start_lines = [ i for i, s in enumerate(castep_output) if s.find('****** %s ******' % fn) != -1 ] if force_start_lines != []: break if force_start_lines == []: raise ValueError # Use last set of forces in file force_start = force_start_lines[-1] # Extract force lines from .castep file force_lines = castep_output[force_start + 6:force_start + 6 + cluster.n] cluster.add_property('force', 0.0, ncols=3) # Fill in the forces for i, line in enumerate(force_lines): line = line.replace('*', '') # Remove the *s el, num_str, fx, fy, fz = line.split() num = int(num_str) cluster.force[lookup[(el, num)], :] = numpy.array((fx, fy, fz)) except ValueError, m: if abort: raise ValueError('No forces found in castep file %s: ' % m)
def read_castep_md(md_file, cluster=None, abort=True, first=True): """Parse .md file, and return Atoms object with positions, energy, forces, and possibly stress and atomic populations as well""" opened = False if type(md_file) == type(''): opened = True md_file = open(md_file, 'r') E_re = re.compile(' E[ ]*$') R_re = re.compile(' R[ ]*$') V_re = re.compile(' V[ ]*$') F_re = re.compile(' F[ ]*$') h_re = re.compile(' h[ ]*$') in_config = False while True: line = md_file.readline() fields = line.split() if (E_re.search(line)): if (in_config and first): break Energy = float(fields[0]) in_config = True R = [] species = [] at_num = [] pos = [] velo = [] force = [] cur_h_line = 0 cur_R_line = 0 cur_V_line = 0 cur_F_line = 0 if (in_config): if (h_re.search(line)): cur_h_line += 1 R.append(map(float, fields[0:3])) if (cur_h_line == 3): lattice = numpy.array([R[0], R[1], R[2]]) if (R_re.search(line)): cur_R_line += 1 pos.append([fields[2], fields[3], fields[3]]) species.append(fields[0]) at_num.append(int(fields[1])) if (V_re.search(line)): cur_V_line += 1 velo.append([fields[2], fields[3], fields[3]]) if (F_re.search(line)): cur_F_line += 1 force.append([fields[2], fields[3], fields[3]]) if cluster is not None: # If we were passed in an Atoms object, construct mapping from # CASTEP (element, number) to original atom index n_atoms = cluster.n cluster = cluster.copy() species_count = {} lookup = {} for i in range(cluster.n): el = cluster.species[i] if species_count.has_key(el): species_count[el] += 1 else: species_count[el] = 1 lookup[(el, species_count[el])] = i else: # Otherwise we make a new, empty Atoms object. Atoms will # be ordered as they are in .castep file. lookup = {} n_atoms = len(pos) cluster = atoms.Atoms(n=n_atoms, lattice=lattice) cluster.params['energy'] = Energy if (not first): cluster.add_property('new_pos', 0.0, ncols=3) cluster.add_property('new_velo', 0.0, ncols=3) cluster.add_property('force', 0.0, ncols=3) for i in range(n_atoms): el = species[i] num = at_num[i] cluster.species[lookup[(el, num)]] = el cluster.force[lookup[(el, num)]] = numpy.array(map(float, force[i][:])) if (not first): cluster.new_pos[lookup[(el, num)]] = numpy.array(map(float, pos[i][:])) cluster.new_velo[lookup[(el, num)]] = numpy.array( map(float, velo[i][:])) return cluster
def read_geom(): opened = False if type(geom) == type(''): geom = open(geom, 'r') opened = True lines = [] line = geom.readline().strip() # Skip header if present if line.startswith('BEGIN header'): while not line.startswith('END header'): line = geom.readline().strip() geom.readline() # skip blank line else: lines.append(line) # Read until next blank line while line != '': line = geom.readline().strip() if line != '': lines.append(line) if opened: geom.close() # Let go of the file if len(lines) <= 1: # Check for EOF raise IOError params = ParamReader() # First line is the time/step params['time'] = float(lines[0]) # Then the energy, in Hartree energy_lines = filter(lambda s: s.endswith('<-- E'), lines) if len(energy_lines) != 1: raise ValueError('Number of energy lines should be exactly one.') params['energy'], params['hamiltonian'] = \ [float(x)*HARTREE_TO_EV for x in energy_lines[0].split()[0:2]] # Lattice is next, in units of Bohr lattice_lines = filter(lambda s: s.endswith('<-- h'), lines) lattice = numpy.array([[float(x) * BOHR_TO_ANG for x in row[0:3]] for row in map(string.split, lattice_lines)]) # Then optionally stress tensor (FIXME: units) stress_lines = filter(lambda s: s.endswith('<-- S'), lines) params['stress'] = numpy.array([[float(x) for x in row[0:3]] for row in map(string.split, stress_lines) ]) # Find positions and forces poslines = filter(lambda s: s.endswith('<-- R'), lines) forcelines = filter(lambda s: s.endswith('<-- F'), lines) if len(poslines) != len(forcelines): raise ValueError('Number of pos lines (%d) != force lines (%d)'\ % (len(poslines), len(forcelines))) result = atoms.Atoms(n=len(poslines), lattice=lattice, params=params) # Now parse the positions, converting from units of Bohr field_list = [line.split() for line in poslines] result.species[:] = numpy.array(map(operator.itemgetter(0), field_list)) result.pos[:,:] = numpy.array([ [float(x)* BOHR_TO_ANG for x in row] \ for row in [field[2:5] for field in field_list]]) # And finally the forces, which are in units of hartree/bohr field_list = [line.split() for line in forcelines] force = numpy.array([ [float(x)*HARTREE_TO_EV/BOHR_TO_ANG for x in row] \ for row in [field[2:5] for field in field_list]]) result.add_property('force', force) result.force[:] = force result.add_property('norm_f', norm(force)) return result
if 'bcc' in sys.argv: ids = atoms.cnat(p, 3.5) for i in range(len(ids)): if ids[i] == 5: rejects.append(i) if 'a15' in sys.argv: ids = atoms.cnat(p, 3.5) for i in range(len(ids)): if ids[i] in [1, 2]: rejects.append(i) rejects = list(set(rejects)) newp = atoms.Atoms(len(p) - len(rejects)) newp.box = p.box index = 0 for i in range(len(p)): if i not in rejects: newp.r[index] = p.r[i] newp.free[index] = p.free[i] newp.names[index] = p.names[i] newp.mass[index] = p.mass[i] index += 1 filtered.append(newp) io.savecon(sys.argv[2], filtered[0], 'w') for p in filtered[1:]: io.savecon(sys.argv[2], p, 'a')