def read_cell(filename, _=None): """Read a .cell file and return an atoms object. Any value found that does not fit the atoms API will be stored in the atoms.calc attribute. """ from ase.calculators.castep import Castep calc = Castep() fileobj = open(filename) lines = fileobj.readlines() fileobj.close() def get_tokens(lines, l): """Tokenizes one line of a *cell file.""" comment_chars = "#!" while l < len(lines): line = lines[l].strip() if len(line) == 0: l += 1 continue elif any([ line.startswith(comment_char) for comment_char in comment_chars ]): l += 1 continue else: for c in comment_chars: if c in line: icomment = min(line.index(c)) else: icomment = len(line) tokens = line[:icomment].split() return tokens, l + 1 tokens = "" print("read_cell: Warning - get_tokens has not found any more tokens") return tokens, l lat = [] have_lat = False pos = [] spec = [] constraints = [] raw_constraints = {} have_pos = False pos_frac = False l = 0 while l < len(lines): tokens, l = get_tokens(lines, l) if not tokens: continue elif tokens[0].upper() == "%BLOCK": if tokens[1].upper() == "LATTICE_CART" and not have_lat: tokens, l = get_tokens(lines, l) if len(tokens) == 1: print('read_cell: Warning - ignoring unit specifier in') print('%BLOCK LATTICE_CART (assuming Angstrom instead)') tokens, l = get_tokens(lines, l) for _ in range(3): lat_vec = map(float, tokens[0:3]) lat.append(lat_vec) tokens, l = get_tokens(lines, l) if tokens[0].upper() != "%ENDBLOCK": print('read_cell: Warning - ignoring more than three') print('lattice vectors in invalid %BLOCK LATTICE_CART') print('%s ...' % tokens[0].upper()) have_lat = True elif tokens[1].upper() == "LATTICE_ABC" and not have_lat: tokens, l = get_tokens(lines, l) if len(tokens) == 1: print('read_cell: Warning - ignoring unit specifier in') print('%BLOCK LATTICE_ABC (assuming Angstrom instead)') tokens, l = get_tokens(lines, l) a, b, c = map(float, tokens[0:3]) tokens, l = get_tokens(lines, l) alpha, beta, gamma = map(lambda phi: radians(float(phi)), tokens[0:3]) tokens, l = get_tokens(lines, l) if tokens[0].upper() != "%ENDBLOCK": print('read_cell: Warning - ignoring additional lines in') print('invalid %BLOCK LATTICE_ABC') lat_a = [a, 0, 0] lat_b = [b * cos(gamma), b * sin(gamma), 0] lat_c1 = c * cos(beta) lat_c2 = c * (cos(alpha) - cos(beta) * cos(gamma)) / sin(gamma) lat_c3 = sqrt(c * c - lat_c1 * lat_c1 - lat_c2 * lat_c2) lat_c = [lat_c1, lat_c2, lat_c3] lat = [lat_a, lat_b, lat_c] have_lat = True elif tokens[1].upper() == "POSITIONS_ABS" and not have_pos: tokens, l = get_tokens(lines, l) if len(tokens) == 1: print('read_cell: Warning - ignoring unit specifier in') print('%BLOCK POSITIONS_ABS(assuming Angstrom instead)') tokens, l = get_tokens(lines, l) while len(tokens) == 4: spec.append(tokens[0]) pos.append(map(float, tokens[1:4])) tokens, l = get_tokens(lines, l) if tokens[0].upper() != "%ENDBLOCK": print('read_cell: Warning - ignoring invalid lines in') print('%%BLOCK POSITIONS_ABS:\n\t %s' % tokens) have_pos = True elif tokens[1].upper() == "POSITIONS_FRAC" and not have_pos: pos_frac = True tokens, l = get_tokens(lines, l) while len(tokens) == 4: spec.append(tokens[0]) pos.append(map(float, tokens[1:4])) tokens, l = get_tokens(lines, l) if tokens[0].upper() != "%ENDBLOCK": print('read_cell: Warning - ignoring invalid lines') print('%%BLOCK POSITIONS_FRAC:\n\t %s' % tokens) have_pos = True elif tokens[1].upper() == 'SPECIES_POT': tokens, l = get_tokens(lines, l) while tokens and not tokens[0].upper() == '%ENDBLOCK': if len(tokens) == 2: calc.cell.species_pot = tuple(tokens) tokens, l = get_tokens(lines, l) elif tokens[1].upper() == 'IONIC_CONSTRAINTS': while True: if tokens and tokens[0].upper() == '%ENDBLOCK': break tokens, l = get_tokens(lines, l) if not len(tokens) == 6: continue _, species, nic, x, y, z = tokens nic = int(nic) if (species, nic) not in raw_constraints: raw_constraints[(species, nic)] = [] raw_constraints[(species, nic)].append(array([x, y, z])) else: print('Warning: the keyword %s is not' % tokens[1].upper()) print(' interpreted in cell files') while not tokens[0].upper() == '%ENDBLOCK': tokens, l = get_tokens(lines, l) #raise UserWarning else: key = tokens[0] value = ' '.join(tokens[1:]) try: calc.__setattr__(key, value) except: print("Problem setting calc.cell.%s = %s" % (key, value)) raise if pos_frac: atoms = ase.Atoms( calculator=calc, cell=lat, pbc=True, scaled_positions=pos, symbols=spec, ) else: atoms = ase.Atoms( calculator=calc, cell=lat, pbc=True, positions=pos, symbols=spec, ) fixed_atoms = [] for (species, nic), value in raw_constraints.iteritems(): absolute_nr = atoms.calc._get_absolute_number(species, nic) if len(value) == 3: fixed_atoms.append(absolute_nr) elif len(value) == 2: constraint = ase.constraints.FixedLine(a=absolute_nr, direction=cross( value[0], value[1])) constraints.append(constraint) elif len(value) == 1: constraint = ase.constraints.FixedPlane(a=absolute_nr, direction=array( value[0], dtype=float32)) constraints.append(constraint) else: print('Error: Found %s statements attached to atoms %s' % (len(value), absolute_nr)) constraints.append(ase.constraints.FixAtoms(fixed_atoms)) atoms.set_constraint(constraints) # needs to go here again to have the constraints in # atoms.calc.atoms.constraints as well atoms.calc.atoms = atoms atoms.calc.push_oldstate() return atoms
def read_castep_cell(filename, _=None): """Read a .cell file and return an atoms object. Any value found that does not fit the atoms API will be stored in the atoms.calc attribute. """ from ase.calculators.castep import Castep calc = Castep() fileobj = open(filename) lines = fileobj.readlines() fileobj.close() def get_tokens(lines, l): """Tokenizes one line of a *cell file.""" comment_chars = "#!" while l < len(lines): line = lines[l].strip() if len(line) == 0: l += 1 continue elif any([line.startswith(comment_char) for comment_char in comment_chars]): l += 1 continue else: for c in comment_chars: if c in line: icomment = min(line.index(c)) else: icomment = len(line) tokens = line[:icomment].split() return tokens, l + 1 tokens = "" print("read_cell: Warning - get_tokens has not found any more tokens") return tokens, l lat = [] have_lat = False pos = [] spec = [] constraints = [] raw_constraints = {} have_pos = False pos_frac = False l = 0 while l < len(lines): tokens, l = get_tokens(lines, l) if not tokens: continue elif tokens[0].upper() == "%BLOCK": if tokens[1].upper() == "LATTICE_CART" and not have_lat: tokens, l = get_tokens(lines, l) if len(tokens) == 1: print('read_cell: Warning - ignoring unit specifier in') print('%BLOCK LATTICE_CART (assuming Angstrom instead)') tokens, l = get_tokens(lines, l) for _ in range(3): lat_vec = [float(a) for a in tokens[0:3]] lat.append(lat_vec) tokens, l = get_tokens(lines, l) if tokens[0].upper() != "%ENDBLOCK": print('read_cell: Warning - ignoring more than three') print('lattice vectors in invalid %BLOCK LATTICE_CART') print('%s ...' % tokens[0].upper()) have_lat = True elif tokens[1].upper() == "LATTICE_ABC" and not have_lat: tokens, l = get_tokens(lines, l) if len(tokens) == 1: print('read_cell: Warning - ignoring unit specifier in') print('%BLOCK LATTICE_ABC (assuming Angstrom instead)') tokens, l = get_tokens(lines, l) a, b, c = map(float, tokens[0:3]) tokens, l = get_tokens(lines, l) alpha, beta, gamma = [radians(float(phi)) for phi in tokens[0:3]] tokens, l = get_tokens(lines, l) if tokens[0].upper() != "%ENDBLOCK": print('read_cell: Warning - ignoring additional lines in') print('invalid %BLOCK LATTICE_ABC') lat_a = [a, 0, 0] lat_b = [b * cos(gamma), b * sin(gamma), 0] lat_c1 = c * cos(beta) lat_c2 = c * (cos(alpha) - cos(beta) * cos(gamma)) / sin(gamma) lat_c3 = sqrt(c * c - lat_c1 * lat_c1 - lat_c2 * lat_c2) lat_c = [lat_c1, lat_c2, lat_c3] lat = [lat_a, lat_b, lat_c] have_lat = True elif tokens[1].upper() == "POSITIONS_ABS" and not have_pos: tokens, l = get_tokens(lines, l) if len(tokens) == 1: print('read_cell: Warning - ignoring unit specifier in') print('%BLOCK POSITIONS_ABS(assuming Angstrom instead)') tokens, l = get_tokens(lines, l) while len(tokens) == 4: spec.append(tokens[0]) pos.append([float(p) for p in tokens[1:4]]) tokens, l = get_tokens(lines, l) if tokens[0].upper() != "%ENDBLOCK": print('read_cell: Warning - ignoring invalid lines in') print('%%BLOCK POSITIONS_ABS:\n\t %s' % tokens) have_pos = True elif tokens[1].upper() == "POSITIONS_FRAC" and not have_pos: pos_frac = True tokens, l = get_tokens(lines, l) while len(tokens) == 4: spec.append(tokens[0]) pos.append([float(p) for p in tokens[1:4]]) tokens, l = get_tokens(lines, l) if tokens[0].upper() != "%ENDBLOCK": print('read_cell: Warning - ignoring invalid lines') print('%%BLOCK POSITIONS_FRAC:\n\t %s' % tokens) have_pos = True elif tokens[1].upper() == 'SPECIES_POT': tokens, l = get_tokens(lines, l) while tokens and not tokens[0].upper() == '%ENDBLOCK': if len(tokens) == 2: calc.cell.species_pot = tuple(tokens) tokens, l = get_tokens(lines, l) elif tokens[1].upper() == 'IONIC_CONSTRAINTS': while True: if tokens and tokens[0].upper() == '%ENDBLOCK': break tokens, l = get_tokens(lines, l) if not len(tokens) == 6: continue _, species, nic, x, y, z = tokens nic = int(nic) if (species, nic) not in raw_constraints: raw_constraints[(species, nic)] = [] raw_constraints[(species, nic)].append(array( [x, y, z])) else: print('Warning: the keyword %s is not' % tokens[1].upper()) print(' interpreted in cell files') while not tokens[0].upper() == '%ENDBLOCK': tokens, l = get_tokens(lines, l) #raise UserWarning else: key = tokens[0] value = ' '.join(tokens[1:]) try: calc.__setattr__(key, value) except: print("Problem setting calc.cell.%s = %s" % (key, value)) raise if pos_frac: atoms = ase.Atoms( calculator=calc, cell=lat, pbc=True, scaled_positions=pos, symbols=spec, ) else: atoms = ase.Atoms( calculator=calc, cell=lat, pbc=True, positions=pos, symbols=spec, ) fixed_atoms = [] for (species, nic), value in raw_constraints.items(): absolute_nr = atoms.calc._get_absolute_number(species, nic) if len(value) == 3: fixed_atoms.append(absolute_nr) elif len(value) == 2: constraint = ase.constraints.FixedLine(a=absolute_nr, direction=cross(value[0], value[1])) constraints.append(constraint) elif len(value) == 1: constraint = ase.constraints.FixedPlane(a=absolute_nr, direction=array(value[0], dtype=float32)) constraints.append(constraint) else: print('Error: Found %s statements attached to atoms %s' % (len(value), absolute_nr)) constraints.append(ase.constraints.FixAtoms(fixed_atoms)) atoms.set_constraint(constraints) # needs to go here again to have the constraints in # atoms.calc.atoms.constraints as well atoms.calc.atoms = atoms atoms.calc.push_oldstate() return atoms
def read_castep_cell(fd, index=None): """Read a .cell file and return an atoms object. Any value found that does not fit the atoms API will be stored in the atoms.calc attribute. This routine has been modified to also be able to read *.cell files even if there is no CASTEP installation or castep_keywords.py available. We wil then make use of a fallback-mode which basically just read atoms positions and unit cell information. This can very highly useful for visualization using the ASE gui. """ from ase.calculators.castep import Castep _fallback = False try: calc = Castep() except Exception as exception: print('read_cell: Warning - Was not able to initialize CASTEP ' 'calculator.') print(' This may be due to a non-existing ' '"castep.keywords.py"') print(' file or a non-existing CASTEP installation.') print(' Original error message appears below:') print('') print(' ' * 11 + exception.__str__().replace('\n', '\n' + ' ' * 11)) print('') print( ' Fallback-mode will be applied to provide at least the') print(' geometric information contained in the *.cell file.') calc = None _fallback = True # fd will be closed by embracing read() routine lines = fd.readlines() def get_tokens(lines, l, maxsplit=0): """Tokenizes one line of a *cell file.""" comment_chars = '#!;' separator_re = '[\s=:]+' while l < len(lines): line = lines[l].strip() if len(line) == 0: l += 1 continue elif any([line.startswith(comment_char) for comment_char in comment_chars]): l += 1 continue else: # Remove comments line = re.split('[{0}]+'.format(comment_chars), line, 1)[0] # Tokenize tokens = re.split(separator_re, line.strip(), maxsplit) return tokens, l + 1 tokens = '' # This print statement is definitely not necessary # print("read_cell: Warning - get_tokens has not found any more tokens") return tokens, l lat = [] have_lat = False pos = [] spec = [] # Here we extract all the possible additional info # These are marked by their type add_info = { 'SPIN': float, 'MAGMOM': float, 'LABEL': str, } add_info_arrays = dict((k, []) for k in add_info) # A convenient function that extracts this info from a line fragment def get_add_info(ai_arrays, line=''): re_keys = '({0})'.format('|'.join(add_info.keys())) ai_dict = {} sline = re.split(re_keys, line, flags=re.IGNORECASE) for t_i, tok in enumerate(sline): if tok in add_info: try: ai_dict[tok] = re.split('[:=]', sline[t_i + 1], maxsplit=1)[1].strip() except IndexError: ai_dict[tok] = None # Then turn these into values into the arrays for k in ai_arrays: if k not in ai_dict or ai_dict[k] is None: ai_arrays[k].append({str: 'NULL', float: 0.0, }[add_info[k]]) else: ai_arrays[k].append(add_info[k](ai_dict[k])) constraints = [] raw_constraints = {} have_pos = False pos_frac = False l = 0 while l < len(lines): tokens, l = get_tokens(lines, l) if not tokens: continue elif tokens[0].upper() == '%BLOCK': if tokens[1].upper() == 'LATTICE_CART' and not have_lat: tokens, l = get_tokens(lines, l) if len(tokens) == 1: print('read_cell: Warning - ignoring unit specifier in') print('%BLOCK LATTICE_CART (assuming Angstrom instead)') tokens, l = get_tokens(lines, l) for _ in range(3): lat_vec = [float(a) for a in tokens[0:3]] lat.append(lat_vec) tokens, l = get_tokens(lines, l) if tokens[0].upper() != '%ENDBLOCK': print('read_cell: Warning - ignoring more than three') print('lattice vectors in invalid %BLOCK LATTICE_CART') print('%s ...' % tokens[0].upper()) have_lat = True elif tokens[1].upper() == 'LATTICE_ABC' and not have_lat: tokens, l = get_tokens(lines, l) if len(tokens) == 1: print('read_cell: Warning - ignoring unit specifier in') print('%BLOCK LATTICE_ABC (assuming Angstrom instead)') tokens, l = get_tokens(lines, l) a, b, c = map(float, tokens[0:3]) tokens, l = get_tokens(lines, l) alpha, beta, gamma = [np.radians(float(phi)) for phi in tokens[0:3]] tokens, l = get_tokens(lines, l) if tokens[0].upper() != '%ENDBLOCK': print('read_cell: Warning - ignoring additional lines in') print('invalid %BLOCK LATTICE_ABC') lat_a = [a, 0, 0] lat_b = [b * np.cos(gamma), b * np.sin(gamma), 0] lat_c1 = c * np.cos(beta) lat_c2 = c * ((np.cos(alpha) - np.cos(beta) * np.cos(gamma)) / np.sin(gamma)) lat_c3 = np.sqrt(c * c - lat_c1 * lat_c1 - lat_c2 * lat_c2) lat_c = [lat_c1, lat_c2, lat_c3] lat = [lat_a, lat_b, lat_c] have_lat = True elif tokens[1].upper() == 'POSITIONS_ABS' and not have_pos: tokens, l = get_tokens(lines, l) if len(tokens) == 1: print('read_cell: Warning - ignoring unit specifier in') print('%BLOCK POSITIONS_ABS(assuming Angstrom instead)') tokens, l = get_tokens(lines, l, maxsplit=4) # fix to be able to read initial spin assigned on the atoms while len(tokens) >= 4: spec.append(tokens[0]) pos.append([float(p) for p in tokens[1:4]]) if len(tokens) > 4: get_add_info(add_info_arrays, tokens[4]) else: get_add_info(add_info_arrays) tokens, l = get_tokens(lines, l, maxsplit=4) if tokens[0].upper() != '%ENDBLOCK': print('read_cell: Warning - ignoring invalid lines in') print('%%BLOCK POSITIONS_ABS:\n\t %s' % tokens) have_pos = True elif tokens[1].upper() == 'POSITIONS_FRAC' and not have_pos: pos_frac = True tokens, l = get_tokens(lines, l, maxsplit=4) # fix to be able to read initial spin assigned on the atoms while len(tokens) >= 4: spec.append(tokens[0]) pos.append([float(p) for p in tokens[1:4]]) if len(tokens) > 4: get_add_info(add_info_arrays, tokens[4]) else: get_add_info(add_info_arrays) tokens, l = get_tokens(lines, l, maxsplit=4) if tokens[0].upper() != '%ENDBLOCK': print('read_cell: Warning - ignoring invalid lines') print('%%BLOCK POSITIONS_FRAC:\n\t %s' % tokens) have_pos = True elif tokens[1].upper() == 'SPECIES_POT': if not _fallback: tokens, l = get_tokens(lines, l) while tokens and not tokens[0].upper() == '%ENDBLOCK': if len(tokens) == 2: calc.cell.species_pot = tuple(tokens) tokens, l = get_tokens(lines, l) elif tokens[1].upper() == 'IONIC_CONSTRAINTS': while True: if tokens and tokens[0].upper() == '%ENDBLOCK': break tokens, l = get_tokens(lines, l) if not len(tokens) == 6: continue _, species, nic, x, y, z = tokens # convert xyz to floats x = float(x) y = float(y) z = float(z) nic = int(nic) if (species, nic) not in raw_constraints: raw_constraints[(species, nic)] = [] raw_constraints[(species, nic)].append(np.array( [x, y, z])) else: print('Warning: the keyword %s is not' % tokens[1].upper()) print(' interpreted in cell files') while not tokens[0].upper() == '%ENDBLOCK': tokens, l = get_tokens(lines, l) # raise UserWarning else: key = tokens[0] value = ' '.join(tokens[1:]) if not _fallback: try: calc.__setattr__(key, value) except: print('Problem setting calc.cell.%s = %s' % (key, value)) raise # Get the relevant additional info magmom = np.array(add_info_arrays['SPIN']) # SPIN or MAGMOM are alternative keywords magmom = np.where(magmom != 0, magmom, add_info_arrays['MAGMOM']) labels = np.array(add_info_arrays['LABEL']) if pos_frac: atoms = ase.Atoms( calculator=calc, cell=lat, pbc=True, scaled_positions=pos, symbols=spec, magmoms=magmom) else: atoms = ase.Atoms( calculator=calc, cell=lat, pbc=True, positions=pos, symbols=spec, magmoms=magmom) atoms.new_array('castep_labels', labels) fixed_atoms = [] for (species, nic), value in raw_constraints.items(): absolute_nr = atoms.calc._get_absolute_number(species, nic) if len(value) == 3: fixed_atoms.append(absolute_nr) elif len(value) == 2: constraint = ase.constraints.FixedLine( a=absolute_nr, direction=np.cross(value[0], value[1])) constraints.append(constraint) elif len(value) == 1: # catch cases in which constraints are given in a single line in # the cell file # if np.count_nonzero(value[0]) == 3: # fixed_atoms.append(absolute_nr) # elif np.count_nonzero(value[0]) == 2: # # in this case we need a FixedLine instance # # it is initialized with the atom's index # constraint = ase.constraints.FixedLine(a=absolute_nr, # direction=[not v for v in value[0]]) # constraints.append(constraint) # else: # I do not think you can have a fixed position of a fixed # line with only one constraint -- JML constraint = ase.constraints.FixedPlane( a=absolute_nr, direction=np.array(value[0], dtype=np.float32)) constraints.append(constraint) else: print('Error: Found %s statements attached to atoms %s' % (len(value), absolute_nr)) # we need to sort the fixed atoms list in order not to raise an assertion # error in FixAtoms if fixed_atoms: constraints.append( ase.constraints.FixAtoms(indices=sorted(fixed_atoms))) if constraints: atoms.set_constraint(constraints) if not _fallback: # needs to go here again to have the constraints in # atoms.calc.atoms.constraints as well atoms.calc.atoms = atoms atoms.calc.push_oldstate() return atoms