print(e) assert False, 'Castep calculator module could not be loaded' try: __import__(ase_castep_dir + ".io.castep") except Exception, e: assert False, 'Castep io module could not be loaded' tmp_dir = tempfile.mkdtemp() cwd = os.getcwd() from ase.calculators.castep import Castep try: c = Castep(directory=tmp_dir, label='test_label') except Exception, e: traceback.print_exc() print(e) assert False, 'Could not instantiate castep calculator' try: c.xc_functional = 'PBE' except Exception, e: traceback.print_exc() print(e) assert False, 'Setting xc_functional failed' import ase.lattice.cubic lattice = ase.lattice.cubic.BodyCenteredCubic('Li' )
"""Simple shallow test of the CASTEP interface""" import os import shutil import tempfile import ase.lattice.cubic from ase.calculators.castep import (Castep, CastepParam, create_castep_keywords, import_castep_keywords) tmp_dir = tempfile.mkdtemp() cwd = os.getcwd() c = Castep(directory=tmp_dir, label='test_label') c.xc_functional = 'PBE' lattice = ase.lattice.cubic.BodyCenteredCubic('Li') print('For the sake of evaluating this test, warnings') print('about auto-generating pseudo-potentials are') print('normal behavior and can be safely ignored') lattice.set_calculator(c) create_castep_keywords( castep_command=os.environ['CASTEP_COMMAND'], path=tmp_dir, fetch_only=20) param_fn = os.path.join(tmp_dir, 'myParam.param') param = open(param_fn, 'w')
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 write_castep_cell(fd, atoms, positions_frac=False, force_write=False, precision=6, magnetic_moments=None, castep_cell=None): """ This CASTEP export function write minimal information to a .cell file. If the atoms object is a trajectory, it will take the last image. Note that function has been altered in order to require a filedescriptor rather than a filename. This allows to use the more generic write() function from formats.py Note that the "force_write" keywords has no effect currently. Arguments: positions_frac: boolean. If true, positions are printed as fractional rather than absolute. Default is false. castep_cell: if provided, overrides the existing CastepCell object in the Atoms calculator precision: number of digits to which lattice and positions are printed magnetic_moments: if None, no SPIN values are initialised. If 'initial', the values from get_initial_magnetic_moments() are used. If 'calculated', the values from get_magnetic_moments() are used. If an array of the same length as the atoms object, its contents will be used as magnetic moments. """ if atoms is None: print('Atoms object not initialized') return False if isinstance(atoms, list): if len(atoms) > 1: atoms = atoms[-1] # Header fd.write('#######################################################\n') fd.write('#CASTEP cell file: %s\n' % fd.name) fd.write('#Created using the Atomic Simulation Environment (ASE)#\n') fd.write('#######################################################\n\n') # To write this we simply use the existing Castep calculator, or create # one from ase.calculators.castep import Castep, CastepCell try: has_cell = isinstance(atoms.calc.cell, CastepCell) except AttributeError: has_cell = False if has_cell: cell = deepcopy(atoms.calc.cell) else: cell = Castep(keyword_tolerance=2).cell # Write lattice fformat = '%{0}.{1}f'.format(precision + 3, precision) cell_block_format = ' '.join([fformat] * 3) cell.lattice_cart = [ cell_block_format % tuple(line) for line in atoms.get_cell() ] if positions_frac: pos_keyword = 'positions_frac' positions = atoms.get_scaled_positions() else: pos_keyword = 'positions_abs' positions = atoms.get_positions() if atoms.has('castep_custom_species'): elems = atoms.get_array('castep_custom_species') else: elems = atoms.get_chemical_symbols() if atoms.has('castep_labels'): labels = atoms.get_array('castep_labels') else: labels = ['NULL'] * len(elems) if str(magnetic_moments).lower() == 'initial': magmoms = atoms.get_initial_magnetic_moments() elif str(magnetic_moments).lower() == 'calculated': magmoms = atoms.get_magnetic_moments() elif np.array(magnetic_moments).shape == (len(elems), ): magmoms = np.array(magnetic_moments) else: magmoms = [0] * len(elems) pos_block = [] pos_block_format = '%s ' + cell_block_format for i, el in enumerate(elems): xyz = positions[i] line = pos_block_format % tuple([el] + list(xyz)) # ADD other keywords if necessary if magmoms[i] != 0: line += ' SPIN={0} '.format(magmoms[i]) if labels[i].strip() not in ('NULL', ''): line += ' LABEL={0} '.format(labels[i]) pos_block.append(line) setattr(cell, pos_keyword, pos_block) constraints = atoms.constraints if len(constraints): _supported_constraints = (FixAtoms, FixedPlane, FixedLine, FixCartesian) constr_block = [] for constr in constraints: if not isinstance(constr, _supported_constraints): print('Warning: you have constraints in your atoms, that are') print(' not supported by the CASTEP ase interface') break if isinstance(constr, FixAtoms): for i in constr.index: try: symbol = atoms.get_chemical_symbols()[i] nis = atoms.calc._get_number_in_species(i) except KeyError: raise UserWarning('Unrecognized index in' + ' constraint %s' % constr) for j in range(3): L = '%6d %3s %3d ' % (len(constr_block) + 1, symbol, nis) L += ['1 0 0', '0 1 0', '0 0 1'][j] constr_block += [L] elif isinstance(constr, FixCartesian): n = constr.a symbol = atoms.get_chemical_symbols()[n] nis = atoms.calc._get_number_in_species(n) for i, m in enumerate(constr.mask): if m == 1: continue L = '%6d %3s %3d ' % (len(constr_block) + 1, symbol, nis) L += ' '.join(['1' if j == i else '0' for j in range(3)]) constr_block += [L] elif isinstance(constr, FixedPlane): n = constr.a symbol = atoms.get_chemical_symbols()[n] nis = atoms.calc._get_number_in_species(n) L = '%6d %3s %3d ' % (len(constr_block) + 1, symbol, nis) L += ' '.join([str(d) for d in constr.dir]) constr_block += [L] elif isinstance(constr, FixedLine): n = constr.a symbol = atoms.get_chemical_symbols()[n] nis = atoms.calc._get_number_in_species(n) direction = constr.dir ((i1, v1), (i2, v2)) = sorted(enumerate(direction), key=lambda x: abs(x[1]), reverse=True)[:2] n1 = np.zeros(3) n1[i2] = v1 n1[i1] = -v2 n1 = n1 / np.linalg.norm(n1) n2 = np.cross(direction, n1) l1 = '%6d %3s %3d %f %f %f' % (len(constr_block) + 1, symbol, nis, n1[0], n1[1], n1[2]) l2 = '%6d %3s %3d %f %f %f' % (len(constr_block) + 2, symbol, nis, n2[0], n2[1], n2[2]) constr_block += [l1, l2] cell.ionic_constraints = constr_block write_freeform(fd, cell) return True
def read_castep_cell(fd, index=None, calculator_args={}, find_spg=False, units=units_CODATA2002): """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. By default, the Castep calculator will be tolerant and in the absence of a castep_keywords.json file it will just accept all keywords that aren't automatically parsed. """ from ase.calculators.castep import Castep cell_units = { # Units specifiers for CASTEP 'bohr': units_CODATA2002['a0'], 'ang': 1.0, 'm': 1e10, 'cm': 1e8, 'nm': 10, 'pm': 1e-2 } calc = Castep(**calculator_args) if calc.cell.castep_version == 0 and calc._kw_tol < 3: # No valid castep_keywords.json was found print('read_cell: Warning - Was not able to validate CASTEP input.') print(' This may be due to a non-existing ' '"castep_keywords.json"') print(' file or a non-existing CASTEP installation.') print(' Parsing will go on but keywords will not be ' 'validated and may cause problems if incorrect during a CASTEP ' 'run.') celldict = read_freeform(fd) def parse_blockunit(line_tokens, blockname): u = 1.0 if len(line_tokens[0]) == 1: usymb = line_tokens[0][0].lower() u = cell_units.get(usymb, 1) if usymb not in cell_units: warnings.warn( ('read_cell: Warning - ignoring invalid ' 'unit specifier in %BLOCK {0} ' '(assuming Angstrom instead)').format(blockname)) line_tokens = line_tokens[1:] return u, line_tokens # Arguments to pass to the Atoms object at the end aargs = {'pbc': True} # Start by looking for the lattice lat_keywords = [w in celldict for w in ('lattice_cart', 'lattice_abc')] if all(lat_keywords): warnings.warn('read_cell: Warning - two lattice blocks present in the' ' same file. LATTICE_ABC will be ignored') elif not any(lat_keywords): raise ValueError('Cell file must contain at least one between ' 'LATTICE_ABC and LATTICE_CART') if 'lattice_abc' in celldict: lines = celldict.pop('lattice_abc')[0].split('\n') line_tokens = [l.split() for l in lines] u, line_tokens = parse_blockunit(line_tokens, 'lattice_abc') if len(line_tokens) != 2: warnings.warn('read_cell: Warning - ignoring additional ' 'lines in invalid %BLOCK LATTICE_ABC') abc = [float(p) * u for p in line_tokens[0][:3]] angles = [float(phi) for phi in line_tokens[1][:3]] aargs['cell'] = cellpar_to_cell(abc + angles) if 'lattice_cart' in celldict: lines = celldict.pop('lattice_cart')[0].split('\n') line_tokens = [l.split() for l in lines] u, line_tokens = parse_blockunit(line_tokens, 'lattice_cart') if len(line_tokens) != 3: warnings.warn('read_cell: Warning - ignoring more than ' 'three lattice vectors in invalid %BLOCK ' 'LATTICE_CART') aargs['cell'] = [[float(x) * u for x in lt[:3]] for lt in line_tokens] # Now move on to the positions pos_keywords = [w in celldict for w in ('positions_abs', 'positions_frac')] if all(pos_keywords): warnings.warn('read_cell: Warning - two lattice blocks present in the' ' same file. POSITIONS_FRAC will be ignored') del celldict['positions_frac'] elif not any(pos_keywords): raise ValueError('Cell file must contain at least one between ' 'POSITIONS_FRAC and POSITIONS_ABS') aargs['symbols'] = [] pos_type = 'positions' pos_block = celldict.pop('positions_abs', [None])[0] if pos_block is None: pos_type = 'scaled_positions' pos_block = celldict.pop('positions_frac', [None])[0] aargs[pos_type] = [] lines = pos_block.split('\n') line_tokens = [l.split() for l in lines] if 'scaled' not in pos_type: u, line_tokens = parse_blockunit(line_tokens, 'positions_abs') else: u = 1.0 # Here we extract all the possible additional info # These are marked by their type add_info = { 'SPIN': (float, 0.0), # (type, default) 'MAGMOM': (float, 0.0), 'LABEL': (str, 'NULL') } add_info_arrays = dict((k, []) for k in add_info) def parse_info(raw_info): re_keys = (r'({0})\s*[=:\s]{{1}}\s' r'*([^\s]*)').format('|'.join(add_info.keys())) # Capture all info groups info = re.findall(re_keys, raw_info) info = {g[0]: add_info[g[0]][0](g[1]) for g in info} return info # Array for custom species (a CASTEP special thing) # Usually left unused custom_species = None for tokens in line_tokens: # Now, process the whole 'species' thing spec_custom = tokens[0].split(':', 1) elem = spec_custom[0] if len(spec_custom) > 1 and custom_species is None: # Add it to the custom info! custom_species = list(aargs['symbols']) if custom_species is not None: custom_species.append(tokens[0]) aargs['symbols'].append(elem) aargs[pos_type].append([float(p) * u for p in tokens[1:4]]) # Now for the additional information info = ' '.join(tokens[4:]) info = parse_info(info) for k in add_info: add_info_arrays[k] += [info.get(k, add_info[k][1])] # Now on to the species potentials... if 'species_pot' in celldict: lines = celldict.pop('species_pot')[0].split('\n') line_tokens = [l.split() for l in lines] for tokens in line_tokens: if len(tokens) == 1: # It's a library all_spec = (set(custom_species) if custom_species is not None else set(aargs['symbols'])) for s in all_spec: calc.cell.species_pot = (s, tokens[0]) else: calc.cell.species_pot = tuple(tokens[:2]) # Ionic constraints raw_constraints = {} if 'ionic_constraints' in celldict: lines = celldict.pop('ionic_constraints')[0].split('\n') line_tokens = [l.split() for l in lines] for tokens in line_tokens: 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])) # Symmetry operations if 'symmetry_ops' in celldict: lines = celldict.pop('symmetry_ops')[0].split('\n') line_tokens = [l.split() for l in lines] # Read them in blocks of four blocks = np.array(line_tokens).astype(float) if (len(blocks.shape) != 2 or blocks.shape[1] != 3 or blocks.shape[0] % 4 != 0): warnings.warn('Warning: could not parse SYMMETRY_OPS' ' block properly, skipping') else: blocks = blocks.reshape((-1, 4, 3)) rotations = blocks[:, :3] translations = blocks[:, 3] # Regardless of whether we recognize them, store these calc.cell.symmetry_ops = (rotations, translations) # Anything else that remains, just add it to the cell object: for k, (val, otype) in celldict.items(): try: if otype == 'block': val = val.split('\n') # Avoids a bug for one-line blocks calc.cell.__setattr__(k, val) except Exception: raise RuntimeError('Problem setting calc.cell.%s = %s' % (k, val)) # Get the relevant additional info aargs['magmoms'] = np.array(add_info_arrays['SPIN']) # SPIN or MAGMOM are alternative keywords aargs['magmoms'] = np.where(aargs['magmoms'] != 0, aargs['magmoms'], add_info_arrays['MAGMOM']) labels = np.array(add_info_arrays['LABEL']) aargs['calculator'] = calc atoms = ase.Atoms(**aargs) # Spacegroup... if find_spg: # Try importing spglib try: import spglib except ImportError: try: from pyspglib import spglib except ImportError: # spglib is not present warnings.warn('spglib not found installed on this system - ' 'automatic spacegroup detection is not possible') spglib = None if spglib is not None: symmd = spglib.get_symmetry_dataset(atoms) atoms_spg = Spacegroup(int(symmd['number'])) atoms.info['spacegroup'] = atoms_spg atoms.new_array('castep_labels', labels) if custom_species is not None: atoms.new_array('castep_custom_species', np.array(custom_species)) fixed_atoms = [] constraints = [] for (species, nic), value in raw_constraints.items(): absolute_nr = atoms.calc._get_absolute_number(species, nic) if len(value) == 3: # Check if they are linearly independent if np.linalg.det(value) == 0: print('Error: Found linearly dependent constraints attached ' 'to atoms %s' % (absolute_nr)) continue fixed_atoms.append(absolute_nr) elif len(value) == 2: direction = np.cross(value[0], value[1]) # Check if they are linearly independent if np.linalg.norm(direction) == 0: print('Error: Found linearly dependent constraints attached ' 'to atoms %s' % (absolute_nr)) continue constraint = ase.constraints.FixedLine(a=absolute_nr, direction=direction) constraints.append(constraint) elif len(value) == 1: 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) atoms.calc.atoms = atoms atoms.calc.push_oldstate() return atoms
0.0 0.0 1.0 0.0 0.0 0.0""" in mock_ccell.symmetry_ops.value # check if the CastepOpt, CastepCell comparison mechanism works if castep_keywords: p1 = CastepParam(castep_keywords) p2 = CastepParam(castep_keywords) assert p1._options == p2._options p1._options['xc_functional'].value = 'PBE' p1.xc_functional = 'PBE' assert p1._options != p2._options c = Castep(directory=tmp_dir, label='test_label', keyword_tolerance=2) if castep_keywords: c.xc_functional = 'PBE' else: c.param.xc_functional = 'PBE' # In "forgiving" mode, we need to specify lattice = ase.lattice.cubic.BodyCenteredCubic('Li') print('For the sake of evaluating this test, warnings') print('about auto-generating pseudo-potentials are') print('normal behavior and can be safely ignored') lattice.set_calculator(c) param_fn = os.path.join(tmp_dir, 'myParam.param') param = open(param_fn, 'w')
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
def test_castep_interface(): """Simple shallow test of the CASTEP interface""" import os import re import tempfile import warnings import numpy as np import ase import ase.lattice.cubic from ase.calculators.castep import (Castep, CastepOption, CastepParam, CastepCell, make_cell_dict, make_param_dict, CastepKeywords, create_castep_keywords, import_castep_keywords, CastepVersionError) # XXX on porting this test to pytest it wasn't skipped as it should be. # At any rate it failed then. Maybe someone should look into that ... # # Hence, call the constructor to trigger our test skipping hack: Castep() tmp_dir = tempfile.mkdtemp() # We have fundamentally two sets of tests: one if CASTEP is present, the other # if it isn't has_castep = False # Try creating and importing the castep keywords first try: create_castep_keywords(castep_command=os.environ['CASTEP_COMMAND'], path=tmp_dir, fetch_only=20) has_castep = True # If it worked, it must be present except KeyError: print('Could not find the CASTEP_COMMAND environment variable - please' ' set it to run the full set of Castep tests') except CastepVersionError: print( 'Invalid CASTEP_COMMAND provided - please set the correct one to ' 'run the full set of Castep tests') try: castep_keywords = import_castep_keywords( castep_command=os.environ.get('CASTEP_COMMAND', '')) except CastepVersionError: castep_keywords = None # Start by testing the fundamental parts of a CastepCell/CastepParam object boolOpt = CastepOption('test_bool', 'basic', 'defined') boolOpt.value = 'TRUE' assert boolOpt.raw_value is True float3Opt = CastepOption('test_float3', 'basic', 'real vector') float3Opt.value = '1.0 2.0 3.0' assert np.isclose(float3Opt.raw_value, [1, 2, 3]).all() # Generate a mock keywords object mock_castep_keywords = CastepKeywords(make_param_dict(), make_cell_dict(), [], [], 0) mock_cparam = CastepParam(mock_castep_keywords, keyword_tolerance=2) mock_ccell = CastepCell(mock_castep_keywords, keyword_tolerance=2) # Test special parsers mock_cparam.continuation = 'default' mock_cparam.reuse = 'default' assert mock_cparam.reuse.value is None mock_ccell.species_pot = ('Si', 'Si.usp') mock_ccell.species_pot = ('C', 'C.usp') assert 'Si Si.usp' in mock_ccell.species_pot.value assert 'C C.usp' in mock_ccell.species_pot.value symops = (np.eye(3)[None], np.zeros(3)[None]) mock_ccell.symmetry_ops = symops assert """1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0""" in mock_ccell.symmetry_ops.value # check if the CastepOpt, CastepCell comparison mechanism works if castep_keywords: p1 = CastepParam(castep_keywords) p2 = CastepParam(castep_keywords) assert p1._options == p2._options p1._options['xc_functional'].value = 'PBE' p1.xc_functional = 'PBE' assert p1._options != p2._options c = Castep(directory=tmp_dir, label='test_label', keyword_tolerance=2) if castep_keywords: c.xc_functional = 'PBE' else: c.param.xc_functional = 'PBE' # In "forgiving" mode, we need to specify lattice = ase.lattice.cubic.BodyCenteredCubic('Li') print('For the sake of evaluating this test, warnings') print('about auto-generating pseudo-potentials are') print('normal behavior and can be safely ignored') lattice.set_calculator(c) param_fn = os.path.join(tmp_dir, 'myParam.param') param = open(param_fn, 'w') param.write('XC_FUNCTIONAL : PBE #comment\n') param.write('XC_FUNCTIONAL : PBE #comment\n') param.write('#comment\n') param.write('CUT_OFF_ENERGY : 450.\n') param.close() c.merge_param(param_fn) assert c.calculation_required(lattice) if has_castep: assert c.dryrun_ok() c.prepare_input_files(lattice) # detecting pseudopotentials tests # typical filenames files = [ 'Ag_00PBE.usp', 'Ag_00.recpot', 'Ag_C18_PBE_OTF.usp', 'ag-optgga1.recpot', 'Ag_OTF.usp', 'ag_pbe_v1.4.uspp.F.UPF', 'Ni_OTF.usp', 'fe_pbe_v1.5.uspp.F.UPF', 'Cu_01.recpot' ] pp_path = os.path.join(tmp_dir, 'test_pp') os.makedirs(pp_path) for f in files: with open(os.path.join(pp_path, f), 'w') as _f: _f.write('DUMMY PP') c = Castep(directory=tmp_dir, label='test_label_pspots', castep_pp_path=pp_path) c._pedantic = True atoms = ase.build.bulk('Ag') atoms.set_calculator(c) # I know, unittest would be nicer... maybe at a later point # disabled, but may be useful still # try: # # this should yield no files # atoms.calc.find_pspots(suffix='uspp') # raise AssertionError # # this should yield no files # atoms.calc.find_pspots(suffix='uspp') # raise AssertionError # except RuntimeError as e: # #print(e) # pass # # print(e) # pass try: # this should yield non-unique files atoms.calc.find_pspots(suffix='recpot') raise AssertionError except RuntimeError: pass # now let's see if we find all... atoms.calc.find_pspots(pspot='00PBE', suffix='usp') assert atoms.calc.cell.species_pot.value.split()[-1] == 'Ag_00PBE.usp' atoms.calc.find_pspots(pspot='00', suffix='recpot') assert atoms.calc.cell.species_pot.value.split()[-1] == 'Ag_00.recpot' atoms.calc.find_pspots(pspot='C18_PBE_OTF', suffix='usp') assert atoms.calc.cell.species_pot.value.split( )[-1] == 'Ag_C18_PBE_OTF.usp' atoms.calc.find_pspots(pspot='optgga1', suffix='recpot') assert atoms.calc.cell.species_pot.value.split()[-1] == 'ag-optgga1.recpot' atoms.calc.find_pspots(pspot='OTF', suffix='usp') assert atoms.calc.cell.species_pot.value.split()[-1] == 'Ag_OTF.usp' atoms.calc.find_pspots(suffix='UPF') assert (atoms.calc.cell.species_pot.value.split()[-1] == 'ag_pbe_v1.4.uspp.F.UPF') # testing regular workflow c = Castep(directory=tmp_dir, label='test_label_pspots', castep_pp_path=pp_path, find_pspots=True, keyword_tolerance=2) c._build_missing_pspots = False atoms = ase.build.bulk('Ag') atoms.set_calculator(c) # this should raise an error due to ambuiguity try: c._fetch_pspots() raise AssertionError except RuntimeError: pass for e in ['Ni', 'Fe', 'Cu']: atoms = ase.build.bulk(e) atoms.set_calculator(c) c._fetch_pspots() # test writing to file tmp_dir = os.path.join(tmp_dir, 'input_files') c = Castep(directory=tmp_dir, find_pspots=True, castep_pp_path=pp_path, keyword_tolerance=2) c._label = 'test' atoms = ase.build.bulk('Cu') atoms.set_calculator(c) c.prepare_input_files() with open(os.path.join(tmp_dir, 'test.cell'), 'r') as f: assert re.search(r'Cu Cu_01\.recpot', ''.join(f.readlines())) is not None # test keyword conflict management c = Castep(cut_off_energy=300.) assert float(c.param.cut_off_energy.value) == 300.0 with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") c.basis_precision = 'MEDIUM' assert issubclass(w[-1].category, UserWarning) assert "conflicts" in str(w[-1].message) assert c.param.cut_off_energy.value is None assert c.param.basis_precision.value.strip() == 'MEDIUM' with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") c.cut_off_energy = 200.0 assert c.param.basis_precision.value is None assert issubclass(w[-1].category, UserWarning) assert 'option "cut_off_energy" conflicts' in str(w[-1].message) # test kpoint setup options with warnings.catch_warnings(): warnings.simplefilter("ignore") # This block of tests is going to generate a lot of conflict warnings. # We already tested that those work, so just hide them from the output. c = Castep(kpts=[ (0.0, 0.0, 0.0, 1.0), ]) assert c.cell.kpoint_list.value == '0.0 0.0 0.0 1.0' c.set_kpts(((0.0, 0.0, 0.0, 0.25), (0.25, 0.25, 0.3, 0.75))) assert c.cell.kpoint_list.value == '0.0 0.0 0.0 0.25\n0.25 0.25 0.3 0.75' c.set_kpts(c.cell.kpoint_list.value.split('\n')) assert c.cell.kpoint_list.value == '0.0 0.0 0.0 0.25\n0.25 0.25 0.3 0.75' c.set_kpts([3, 3, 2]) assert c.cell.kpoint_mp_grid.value == '3 3 2' c.set_kpts(None) assert c.cell.kpoints_list.value is None assert c.cell.kpoint_list.value is None assert c.cell.kpoint_mp_grid.value is None c.set_kpts('2 2 3') assert c.cell.kpoint_mp_grid.value == '2 2 3' c.set_kpts({'even': True, 'gamma': True}) assert c.cell.kpoint_mp_grid.value == '2 2 2' assert c.cell.kpoint_mp_offset.value == '0.25 0.25 0.25' c.set_kpts({'size': (2, 2, 4), 'even': False}) assert c.cell.kpoint_mp_grid.value == '3 3 5' assert c.cell.kpoint_mp_offset.value == '0.0 0.0 0.0' atoms = ase.build.bulk('Ag') atoms.set_calculator(c) c.set_kpts({'density': 10, 'gamma': False, 'even': None}) assert c.cell.kpoint_mp_grid.value == '27 27 27' assert c.cell.kpoint_mp_offset.value == '0.018519 0.018519 0.018519' c.set_kpts({ 'spacing': (1 / (np.pi * 10)), 'gamma': False, 'even': True }) assert c.cell.kpoint_mp_grid.value == '28 28 28' assert c.cell.kpoint_mp_offset.value == '0.0 0.0 0.0' # test band structure setup from ase.dft.kpoints import BandPath atoms = ase.build.bulk('Ag') bp = BandPath(cell=atoms.cell, path='GX', special_points={ 'G': [0, 0, 0], 'X': [0.5, 0, 0.5] }) bp = bp.interpolate(npoints=10) c = Castep(bandpath=bp) kpt_list = c.cell.bs_kpoint_list.value.split('\n') assert len(kpt_list) == 10 assert list(map(float, kpt_list[0].split())) == [0., 0., 0.] assert list(map(float, kpt_list[-1].split())) == [0.5, 0.0, 0.5]
def main(): datenow = datetime.datetime.now() datenow = datenow.strftime("%d/%m/%Y %H:%M:%S") sys.argv[0] = sys.argv[0].replace(" ", "\ ") start = time.perf_counter() # Write a log file log = open("nc_cryst.log", "a+") log_line = " ".join(sys.argv) log.write(datenow + " " + log_line + "\n") log.close() warnings.filterwarnings("ignore") # Disable ################################################################################## def blockPrint(): sys.stdout = open(os.devnull, 'w') # Restore def enablePrint(): sys.stdout = sys.__stdout__ def complementary(hex): """returns RGB components of complementary color""" hex = hex.lstrip('#') r, g, b = tuple(int(hex[i:i + 2], 16) for i in (0, 2, 4)) hsv = rgb_to_hsv(r, g, b) return (r / 255, g / 255, b / 255), tuple( np.array(hsv_to_rgb(((hsv[0] + 0.5) % 1), hsv[1], hsv[2])) / 255) def update_axes_label_color(axes_actor, color=None): """Set the axes label color (internale helper).""" if color is None: color = rcParams['font']['color'] color = parse_color(color) if isinstance(axes_actor, vtk.vtkAxesActor): prop_x = axes_actor.GetXAxisCaptionActor2D( ).GetCaptionTextProperty() prop_y = axes_actor.GetYAxisCaptionActor2D( ).GetCaptionTextProperty() prop_z = axes_actor.GetZAxisCaptionActor2D( ).GetCaptionTextProperty() for prop in [prop_x, prop_y, prop_z]: prop.SetColor(color[0], color[1], color[2]) prop.SetShadow(False) elif isinstance(axes_actor, vtk.vtkAnnotatedCubeActor): axes_actor.GetTextEdgesProperty().SetColor(color) return def create_axes_marker2(label_color=None, x_color=None, y_color=None, z_color=None, xlabel='a', ylabel='b', zlabel='c', labels_off=False, line_width=50): """Return an axis actor to add in the scene.""" if x_color is None: x_color = rcParams['axes']['x_color'] if y_color is None: y_color = rcParams['axes']['y_color'] if z_color is None: z_color = rcParams['axes']['z_color'] axes_actor = vtk.vtkAxesActor() axes_actor.GetXAxisShaftProperty().SetColor(parse_color(x_color)) axes_actor.GetXAxisTipProperty().SetColor(parse_color(x_color)) axes_actor.GetYAxisShaftProperty().SetColor(parse_color(y_color)) axes_actor.GetYAxisTipProperty().SetColor(parse_color(y_color)) axes_actor.GetZAxisShaftProperty().SetColor(parse_color(z_color)) axes_actor.GetZAxisTipProperty().SetColor(parse_color(z_color)) transform = vtk.vtkTransform() mat = transform.GetMatrix() latt_or = np.array(latt) latt_or[:, 0] = 2 * latt_or[:, 0] / np.linalg.norm(latt_or[:, 0]) latt_or[:, 1] = 2 * latt_or[:, 1] / np.linalg.norm(latt_or[:, 1]) latt_or[:, 2] = 2 * latt_or[:, 2] / np.linalg.norm(latt_or[:, 2]) for i in range(len(latt)): for j in range(len(latt)): mat.SetElement(i, j, 2 * latt_or[i, j]) axes_actor.SetUserTransform(transform) text = vtk.vtkTextProperty() text.SetFontSize(100) text.SetBold(True) text.SetFontFamilyAsString("Times") # Set labels axes_actor.SetXAxisLabelText(xlabel) axes_actor.SetYAxisLabelText(ylabel) axes_actor.SetZAxisLabelText(zlabel) axes_actor.SetNormalizedLabelPosition((1.3, 1.3, 1.3)) axes_actor.GetXAxisCaptionActor2D().SetCaptionTextProperty(text) axes_actor.GetYAxisCaptionActor2D().SetCaptionTextProperty(text) axes_actor.GetZAxisCaptionActor2D().SetCaptionTextProperty(text) if labels_off: axes_actor.AxisLabelsOff() # Set Line width axes_actor.GetXAxisShaftProperty().SetLineWidth(line_width) axes_actor.GetYAxisShaftProperty().SetLineWidth(line_width) axes_actor.GetZAxisShaftProperty().SetLineWidth(line_width) #axes_actor.SetNormalizedTipLength(1,1,1) #axes_actor.SetNormalizedShaftLength(2,2,2) update_axes_label_color(axes_actor, label_color) #axes_actor.SetNormalizedShaftLength(1.6,1.6,1.6) #axes_actor.SetNormalizedTipLength(0.4,0.4,0.4) #axes_actor.SetTotalLength(2,2,2) return axes_actor #atomic valency valency = np.array([ 1, 0, 1, 2, 3, 4, 5, 8, 1, 0, 1, 2, 3, 4, 5, 6, 6, 0, 1, 2, 3, 6, 5, 6, 7, 6, 5, 6, 4, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 8, 6, 4, 3, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 4, 4, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 6, 6, 7, 2, 3, 4, 5, 6, 4, 7, 0, 1, 2, 3, 4, 5, 6, 7, 7, 7, 6, 4, 5, 4, 4, 3, 3, 3, 4, 5, 6, 7, 8, 6, 6, 3, 2, 1, 2, 3, 4, 0, 8 ]) # Some functions track_r = np.array([[-10, -10, -10]]) def field_lines(point_grid): #ds=(V**(1/3))/N #ads=2/N mag_2 = [] N = 100 #ds=0.01 track_n = [] track_r = np.array([[-10, -10, -10]]) lines = np.zeros((2 * len(point_grid), N, 3)) mag = np.zeros((2 * len(point_grid), N)) j = -1 for o, r0 in enumerate(point_grid): F_old = [0, 0, 0] F_mag = 0 for one in [1, -1]: j += 1 xs = [] ys = [] zs = [] r = r0 for n in range(N): s = 0.01 xs.append(r[0]) ys.append(r[1]) zs.append(r[2]) lines[j, n, :] = r mag[j, n] = np.linalg.norm(F_mag) #mag.append(np.linalg.norm(F_mag)) #mag_2.append(np.linalg.norm(F_mag)) if nc_fort.is_close(track_r, r, len(track_r), 5e-3): break track_r = np.vstack((track_r, r)) if r[0] >= np.max(X) or r[0] <= np.min( X) or r[1] >= np.max(Y) or r[1] <= np.min( Y) or r[2] >= np.max(Z) or r[2] <= np.min(Z): break x, y, z = r try: vec_x = f_x([x, y, z]) except: vec_x = [0, 0, 0] try: vec_y = f_y([x, y, z]) except: vec_y = [0, 0, 0] try: vec_z = f_z([x, y, z]) except: vec_z = [0, 0, 0] F = np.array([vec_x[0], vec_y[0], vec_z[0]]) #line_gen(r) F_mag = F F = F / np.linalg.norm(F) phi = np.dot(F, F_old) phi = np.arccos(phi) / np.pi #print(n,phi) if np.round(phi, 4) == 1: break r = r + one * F * s F_old = F track_n.append(n) xs = np.array(xs) ys = np.array(ys) zs = np.array(zs) line_points = np.column_stack((xs, ys, zs)) return lines, mag, track_n ############################################################# #import matplotlib.pyplot as plt # LEFT BLANK ############################################################### ### ####### ###### # # # # # ## ##### #### ###### ##### # # # # # # # # # # # # # # # # ##### ###### # # # # #### ##### # # # # # # ###### ##### # # ##### # # # # # # # # # # # # # ### ####### # # # # # #### ###### # # ################################################################ # We want to read the parameters from a file called seed.nc_param # get from the commandline the seed parser = argparse.ArgumentParser( description= "Visualisation of cell structures and non-collinear magentic properties from a CASTEP job." ) parser.add_argument("seed", help="The seed from the CASTEP calculation.") parser.add_argument("-v", "--verbose", action="store_true", help="Turn on verbose output.") #parser.add_argument("-s","--sym",help="Tolerance for specifying reproduction of atoms outside unit cell (Ang)",default=1) parser.add_argument("-i", "--initmag", action="store_true", help="Plot initial magnetic moment vectors.") parser.add_argument( "-c", "--castep", action="store_true", help= "Read <seed>.castep file to determine moments. Only for NCM calculation (BETA)" ) parser.add_argument( "-f", "--field", help= "Read formatted potential or density to produce field. Only from VECTOR magnetic run.", action="store_true") parser.add_argument( "-o", "--orient", help= "Orientation of the crystal structure, takes values 'a,b,c,a*,b*,c*'.", default="sd") parser.add_argument("-B", "--bond", help="Set maximun bond length.", default=2.4) parser.add_argument("--save", help="Save image.", action="store_true") parser.add_argument("-d", "--delete", help="Delete atoms", nargs='+') parser.add_argument("-p", "--position", help="Camera position vector", nargs=6, default=np.array([0., 0., 0., 0., 0., 0.])) parser.add_argument( "-V", "--volumetric", help= "Provide file with volumetric data: .xsf .den_fmt .pot_fmt accepted.") parser.add_argument("-I", "--iso", help="Isosurface value for volumetric data", nargs="*") parser.add_argument("--colour", help="HEX code for Isosurface colouring", default="#0000FF") parser.add_argument("-z", "--zoom", help="Zoom multiplier", default=1) parser.add_argument("-e", "--exclude", help="Exclude atoms outside first unitcell", action="store_false") parser.add_argument( "-l", "--lines", help="Disable plotting of field lines of a provoded field line", action="store_true") parser.add_argument( "-P", "--plane", help= "Three points in fractional coordinates to define a plane for B-field.", nargs="*") parser.add_argument("-w", "--widget", help="Disable interactive widgets", action="store_false") parser.add_argument("-s", "--saturation", help="Saturation level for sections.", default=1) parser.add_argument("-S", "--spin", help="Plot spin isosurfaces from .den_fmt", action="store_true") parser.add_argument("-C", "--charge", help="Plot charge isosurfaces from .den_fmt", action="store_true") parser.add_argument( "-r", "--reduction", help= "Factor used to reduce the size of atoms, useful for visualising volumetric data without loss of context.", default=1.0) args = parser.parse_args() seed = args.seed #do_legend = args.legend do_verbose = args.verbose do_init_mag = args.initmag do_magmom = args.castep #do_Bfield=args.B_XC field = args.field #sym_tol=np.float(args.sym) orient = args.orient bond_cut = np.float(args.bond) save = args.save hide = args.delete cam_pos = args.position z = np.float(args.zoom) hide_lines = args.lines plane = args.plane exclude = args.exclude widgets = args.widget sat = np.float(args.saturation) docharge = args.charge dospin = args.spin reduction = np.float(args.reduction) iso = args.iso if plane == None: do_plane = False elif len(plane) == 9: do_plane = True elif len(plane) == 0: do_plane = True widgets = True else: print("Insufficient points provided for plane") sys.exit() if iso == None: do_iso = False elif len(iso) == 0: do_iso = True iso = None elif len(iso) > 1: print("Incorrect number of ISO arguments") sys.exit() else: do_iso = True # Make Charge the default if not docharge and not dospin: docharge = True if dospin: docharge = False xsf_file = args.volumetric hex_col = args.colour sym_tol = bond_cut for i in range(len(cam_pos)): cam_pos[i] = np.float(cam_pos[i]) if hide == None: hide = [""] # Define all the options (and the defaults) do_bonds = True #do_magmom = False #do_Bfield = False do_proj = False h = 0.5 #b_xc_file=seed+".B_xc.pot_fmt" xsffile = False denfile = False potfile = False noncollinear = False # Set iso surface colourmap iso_colours = complementary(hex_col) colors = list(iso_colours) colours = [colors[1], colors[0]] cmap_name = "iso_colors" cm = LinearSegmentedColormap.from_list(cmap_name, colours, N=2) # Open the tkinter window window = Tk() window.resizable(False, False) window.title("NC_CRYST: " + seed) output = Text(window) output.grid(row=1, column=0, columnspan=4) #,sticky=N+S+W) ################################################################## # Define all of the atom positions blockPrint() #if do_verbose: # print("Parsing .cell") cell = io.read(seed + ".cell") ccalc = Castep() ase_cell = cell.get_cell() a, b, c, alpha, beta, gamma = cell.get_cell_lengths_and_angles() pos = cell.get_positions() prim_pos = pos cell.set_calculator(ccalc) latt = np.transpose(np.matmul(np.identity(3), cell.get_cell())) init_mag = np.zeros((len(pos), 3)) init_spin = np.zeros((len(pos), 3)) if do_init_mag: try: with open(seed + ".cell") as init_cell: data = init_cell.readlines() except: print("No file: " + seed + ".cell") sys.exit() pos_i = [] counter = 0 for i in data: if "spin" in i.lower(): try: i = i.replace("=", " ") i = i.strip("\n") except: None i = i.split() if len(i) > 6: pos_i.append([np.float(j) for j in i[1:4]]) init_mag[counter] = [np.float(j) for j in i[5:8]] else: pos_i.append([np.float(j) for j in i[1:4]]) temp = [0., 0., np.float(i[5])] init_mag[counter] = temp counter += 1 init_spin = np.zeros((len(pos), 3)) sum_dat = "".join(data).lower() for i in range(len(pos_i)): for j in range(len(pos)): if "positions_frac" in sum_dat: dist = np.sum((np.matmul(latt, pos_i[i]) - pos[j])**2) if dist < 0.00001: init_spin[j] = init_mag[i] else: dist = np.sum((pos_i[i] - pos[j])**2) if dist < 0.00001: init_spin[j] = init_mag[i] cell.set_velocities(init_spin) # Make the supercell (gets all of the positions for me) scell = make_supercell(cell, 3 * np.identity(3)) pos = scell.get_positions() atoms = scell.get_atomic_numbers() symb = scell.get_chemical_symbols() Vol = cell.get_volume() enablePrint() #print(pos) atom_colours = jmol_colors[atoms] atom_radii = vdw_radii[atoms] inv_latt = np.linalg.inv(np.array(ase_cell.T)) init_spin = scell.get_velocities() #prim_count=0 #prim_list=np.zeros(len(pos),order="F") prim_count, prim_list, pos, keep, n_atoms, bonds, n_bonds = nc_fort.sym_positions( bond_cut, len(pos), pos, latt, inv_latt, exclude) prim_list = np.array(keep[0:prim_count]) #sys.exit() pos = pos[prim_list] atoms = atoms[prim_list] #pos=pos[prim_list] atom_radii = atom_radii[prim_list] atom_colours = atom_colours[prim_list] symb = np.array(symb)[prim_list] init_spin = init_spin[prim_list] unique_atom, atom_counts = np.unique(atoms, return_counts=True) atom_label = [] sort = [] ###################################### SYMMETRY POSITIONS ################### for j in unique_atom: for i in range(len(cell.get_atomic_numbers())): if atoms[i] == j: sort.append(i) sort = np.array(sort) if do_magmom: with open(seed + ".castep") as castep: castep_lines = castep.readlines() for no, test in enumerate(castep_lines): if "Noncollinear Spin Vectors" in test: line_no = no mom_vec = [] vec_symb = [] for i in range(line_no + 4, line_no + 4 + len(prim_pos)): cline = castep_lines[i] cline = cline.split() vec = [np.float(cline[2]), np.float(cline[3]), np.float(cline[4])] vec_symb.append(cline[0]) mom_vec.append(vec) #print(mom_vec) vec_symb_2 = list(vec_symb) mom_vec_2 = list(mom_vec) for i in range(len(sort)): vec_symb_2[sort[i]] = vec_symb[i] mom_vec_2[sort[i]] = mom_vec[i] vec_symb = list(vec_symb_2) mom_vec = list(mom_vec_2) if do_magmom: ccell = cell.copy() ccell.set_velocities(mom_vec) ccell = make_supercell(ccell, 3 * np.identity(3)) mom_vec = ccell.get_velocities() sort = np.argsort(atoms) atoms = atoms[sort] pos = pos[sort] atom_radii = atom_radii[sort] atom_colours = atom_colours[sort] symb = np.array(symb)[sort] if do_init_mag: init_spin = init_spin[sort] if do_magmom: mom_vec = np.array(mom_vec) mom_vec = mom_vec[sort] for i in atom_counts: for j in range(i): atom_label.append(j + 1) hide_num = [] for i in range(len(symb)): for j in hide: if j == symb[i]: hide_num.append(i) # Time for some plotting pv.set_plot_theme("document") if save: p = pv.Plotter(off_screen=True) else: p = pv.Plotter() p.enable_parallel_projection() orientation = [latt[:, 0], latt[:, 1], latt[:, 2]] for i in range(3): orientation[i] = orientation[i] / 2 * np.linalg.norm(orientation[i]) arrow_or=pv.Arrow([0,0,0],orientation[0],tip_length=0.25, tip_radius=0.09, tip_resolution=20, shaft_radius=0.03, shaft_resolution=20) +\ pv.Arrow([0,0,0],orientation[1],tip_length=0.25, tip_radius=0.09, tip_resolution=20, shaft_radius=0.03, shaft_resolution=20) +\ pv.Arrow([0,0,0],orientation[2],tip_length=0.25, tip_radius=0.09, tip_resolution=20, shaft_radius=0.03, shaft_resolution=20)+\ pv.Sphere(0.1,[0,0,0]) test = create_axes_marker2(label_color="black", line_width=4, x_color="r", y_color="g", z_color="b", xlabel="a", ylabel="b", zlabel="c", labels_off=False) p.add_orientation_widget(test) ######################################################################################################################## # _______ _ _ _ ______ _ _ _ #(_______|_) | | | | / _____) | | | | _ (_) # _____ _ ____| | _ | | | / ____| | ____ _ _| | ____| |_ _ ___ ____ ___ #| ___) | |/ _ ) |/ || | | | / _ | |/ ___) | | | |/ _ | _)| |/ _ \| _ \ /___) #| | | ( (/ /| ( (_| | | \____( ( | | ( (___| |_| | ( ( | | |__| | |_| | | | |___ | #|_| |_|\____)_|\____| \______)_||_|_|\____)\____|_|\_||_|\___)_|\___/|_| |_(___/ ######################################################################################################################## if xsf_file != None: # See what sort of file we have. if ".den_fmt" in xsf_file: denfile = True elif ".pot_fmt" in xsf_file: potfile = True elif ".xsf" in xsf_file: xsffile = True nx, ny, nz, mesh_mag = xsf.read_xsf(xsf_file) if potfile: docharge = False dospin = False if potfile or denfile: with open(xsf_file) as header: data = header.readlines()[0:11] nx, ny, nz = data[8].split()[0:3] nx, ny, nz = int(nx), int(ny), int(nz) #latt=np.array([data[3].split()[0:3],data[4].split()[0:3],data[5].split()[0:3]]).astype(float) #latt=np.transpose(latt) V = np.loadtxt(xsf_file, skiprows=11) #n= np.round(h*nz) #mask=(V[:,2] == n ) V3D = V #V=V[mask] if potfile: if np.shape(V3D)[1] == 9: noncollinear = True V1 = V3D[:, 3] + 1j * V3D[:, 4] V2 = V3D[:, 5] + 1j * V3D[:, 6] V3 = V3D[:, 7] + 1j * V3D[:, 8] B_x = np.real((V3 + np.conj(V3)) / 2) B_y = np.real(1j * (V3 - np.conj(V3)) / 2) B_z = np.real((V1 - V2) / 2) else: noncollinear = False print("3D data only accepted for NCM .pot_fmt, Exiting...") sys.exit() if denfile: if np.shape(V3D)[1] == 7: noncollinear = True B_x = V3D[:, 4] B_y = V3D[:, 5] B_z = V3D[:, 6] charge = V3D[:, 3] mesh_mag = np.zeros((nx, ny, nz)) if docharge: for i in range(len(V3D)): mesh_mag[int(V3D[i, 0] - 1), int(V3D[i, 1] - 1), int(V3D[i, 2] - 1)] = charge[i] else: noncollinear = False charge = V3D[:, 3] spin = V3D[:, 4] mesh_mag = np.zeros((nx, ny, nz)) if docharge: for i in range(len(V3D)): mesh_mag[int(V3D[i, 0] - 1), int(V3D[i, 1] - 1), int(V3D[i, 2] - 1)] = charge[i] if dospin: for i in range(len(V3D)): mesh_mag[int(V3D[i, 0] - 1), int(V3D[i, 1] - 1), int(V3D[i, 2] - 1)] = spin[i] mesh_mag = mesh_mag / (nx * ny * nz) #print("NCM: ",noncollinear) #print("POT: ",potfile) #print("DEN: ",denfile) #print("Charge: ",docharge) #print("Spin: ",dospin) if noncollinear and not docharge: mesh3D_x = np.zeros((nx, ny, nz)) mesh3D_y = np.zeros((nx, ny, nz)) mesh3D_z = np.zeros((nx, ny, nz)) for i in range(len(V3D)): mesh3D_x[int(V3D[i, 0] - 1), int(V3D[i, 1] - 1), int(V3D[i, 2] - 1)] = B_x[i] mesh3D_y[int(V3D[i, 0] - 1), int(V3D[i, 1] - 1), int(V3D[i, 2] - 1)] = B_y[i] mesh3D_z[int(V3D[i, 0] - 1), int(V3D[i, 1] - 1), int(V3D[i, 2] - 1)] = B_z[i] mesh_mag = np.sqrt(mesh3D_x**2 + mesh3D_y**2 + mesh3D_z**2) # Calcuate the Div x_flux = np.sum(mesh3D_x[0:-1, 0, 0]) - np.sum(mesh3D_x[0:-1, -1, -1]) y_flux = np.sum(mesh3D_x[0, 0:-1, 0]) - np.sum(mesh3D_x[-1, 0:-1, -1]) z_flux = np.sum(mesh3D_x[0, 0, 0:-1]) - np.sum(mesh3D_x[-1, -1, 0:-1]) flux = x_flux + y_flux + z_flux # Play here might cause asymmetry Vx = (V[:, 0] - 1) / (nx) Vy = (V[:, 1] - 1) / (ny) Vz = (V3D[:, 2] - 1) / (nz) # Set up for fieldlines calc X = np.linspace(np.min(Vx), np.max(Vx), nx, endpoint=True) Y = np.linspace(np.min(Vy), np.max(Vy), ny, endpoint=True) Z = np.linspace(np.min(Vz), np.max(Vz), nz, endpoint=True) f_x = RegularGridInterpolator((X, Y, Z), mesh3D_x) f_y = RegularGridInterpolator((X, Y, Z), mesh3D_y) f_z = RegularGridInterpolator((X, Y, Z), mesh3D_z) #X,Y=np.meshgrid(X,Y) n_grid = 175 ix = int(np.round(a / np.cbrt(Vol / n_grid))) iy = int(np.round(b / np.cbrt(Vol / n_grid))) iz = int(np.round(c / np.cbrt(Vol / n_grid))) if ix == 0: ix = 1 if iy == 0: iy = 1 if iz == 0: iz = 1 #sys.exit() #ix,iy,iz=[8,8,3] N = 500 k = 0.5 x = np.linspace(0.1, 0.9, ix, endpoint=True) y = np.linspace(0.1, 0.9, iy, endpoint=True) z = np.linspace(0.1, 0.9, iz, endpoint=True) gx, gy, gz = np.meshgrid(x, y, z) gx = gx.reshape(ix * iy * iz) gy = gy.reshape(ix * iy * iz) gz = gz.reshape(ix * iy * iz) point_grid = np.zeros((ix * iy * iz, 3)) point_grid[:, 0] = gx point_grid[:, 1] = gy point_grid[:, 2] = gz if field: #for m,gpoint in enumerate(point_grid): #i,j,k=gpoint # track_r=field_lines(np.array([i,j,k]),track_r)#map[m][0:counter[m]]) lines, mags, n_track = field_lines(point_grid) mags = np.power(mags, 0.45) #mags=mags/np.max(mags) for k in range(2 * len(point_grid)): if n_track[k] > 1: line_points = lines[k, 0:n_track[k], :] mag = mags[k, 0:n_track[k]] line_points = nc_fort.multmatmul( latt, line_points, len(line_points)) mag[0] = mag[1] #mag=mag/np.max(mag) #mag[mag<0.3*np.max(mag)]=0#0.5*np.max(mag) r = 0.008 polyLine = pv.PolyData(line_points) polyLine.points = line_points polyLine["scalars"] = np.array(mag) theCell = np.arange(0, len(line_points), dtype=np.int) theCell = np.insert(theCell, 0, len(line_points)) polyLine.lines = theCell tube = polyLine.tube(radius=r) #p.add_mesh(tube,color="black", smooth_shading=True) p.add_mesh( tube, show_scalar_bar=False, cmap="gist_yarg", opacity=mag, pickable=False ) #,color="black")#cmap='binary',opacity=mag) #Flatten the mesh if not xsffile: mesh_mag = mesh_mag.flatten('F') else: mesh_mag = mesh_mag.flatten('C') #if not xsffile: xs = np.linspace(0, 1, nx, endpoint=True) ys = np.linspace(0, 1, ny, endpoint=True) zs = np.linspace(0, 1, nz, endpoint=True) points = np.zeros((mesh_mag.shape[0], 3)) counter = 0 for z in zs: for y in ys: for x in xs: points[counter, :] = np.matmul(latt, [x, y, z]) counter += 1 sgrid = pv.StructuredGrid() sgrid.points = points sgrid.dimensions = [nx, ny, nz] sgrid.point_arrays["values"] = mesh_mag if do_plane: # calculate the vectors if len(plane) > 1: plane = nc_fort.multmatmul(latt, plane, 3) v1 = plane[0] - plane[1] v2 = plane[0] - plane[2] print(v1, v2) norm = np.cross(v1, v2) if np.linalg.norm(norm) == 0: print("Plane vectors colinear: Exiting...") sys.exit() v2 = np.cross(norm, v1) cmap = "autumn" #"plasma" if save or len(plane) > 1: slice = sgrid.slice(norm, plane[0]) val = slice.point_arrays["values"] p.add_mesh(slice, cmap=cmap, show_scalar_bar=False, clim=(np.min(val), sat * np.max(val)), pickable=False) else: p.add_mesh_slice(sgrid, show_scalar_bar=False, cmap=cmap, show_edges=False, implicit=False, clim=(np.min(mesh_mag), sat * np.max(mesh_mag)), pickable=False) if do_iso: if iso == None: iso = 0.05 * np.max([np.max(mesh_mag), abs(np.min(mesh_mag))]) else: iso = np.float(iso[0]) output.insert(END, "VOLUMETRIC\n") output.insert(END, "----------\n") output.insert( END, "Max: " + str(np.max(mesh_mag)) + " Min: " + str(np.min(mesh_mag)) + "\n") output.insert(END, "Isovalue: " + str(iso) + "\n") output.insert(END, " \n") if save or not widgets: contours = sgrid.contour([iso, -iso]) slider_contour = p.add_mesh(contours, opacity=0.4, cmap=cm, smooth_shading=True, show_scalar_bar=False, lighting=True, name="contour", pickable=False) else: def cont(iso_val): contours = sgrid.contour([iso_val, -iso_val]) slider_contour = p.add_mesh(contours, opacity=0.4, cmap=cm, smooth_shading=True, show_scalar_bar=False, lighting=True, name="contour", pickable=False) return p.add_slider_widget(cont, rng=(np.min(mesh_mag), np.max(mesh_mag)), value=iso, style="modern", title="Isosurface Value", pointa=(0.1, 0.9), pointb=(0.3, 0.9)) ######################################################################################################################## ######################################################################################################################## ######################################################################################################################## ######################################################################################################################## ######################################################################################################################## ######################################################################################################################## # Add the box edges = np.array([[[0., 0., 0.], [0., 0., 1.]], [[0., 0., 0.], [0., 1., 0.]], [[0., 0., 0.], [1., 0., 0.]], [[1., 1., 1.], [1., 0., 1.]], [[1., 1., 1.], [1., 1., 0.]], [[1., 1., 1.], [0., 1., 1.]], [[0., 1., 1.], [0., 0., 1.]], [[0., 1., 1.], [0., 1., 0.]], [[1., 1., 0.], [1., 0., 0.]], [[1., 1., 0.], [0., 1., 0.]], [[1., 0., 0.], [1., 0., 1.]], [[0., 0., 1.], [1., 0., 1.]]]) for i, main in enumerate(edges): for j, sub in enumerate(main): edges[i, j] = np.matmul(latt, sub) box = pv.Box([0, 1, 0, 1, 0, 1]) for i in range(0, 12): p.add_lines(edges[i], color="black", width=1.5) # Do the atoms for i, vec in enumerate(pos): if i in hide_num: continue sphere = pv.Sphere(atom_radii[i] / (reduction * 5), vec, theta_resolution=200, phi_resolution=200) p.add_mesh(sphere, color=atom_colours[i], specular=0.3, specular_power=30, ambient=0.2, diffuse=1, pickable=True) def pick_atom(actor, two): if actor != None: centre = actor.center #np.matmul(np.linalg.inv(latt),actor.center) for i in range(len(pos)): if (np.isclose(pos[i], centre)).all(): break vec = np.round(np.matmul(np.linalg.inv(latt), pos[i]), 4) string = "Atom " + str(i) + ":" + " " + symb[i] + " (" + str( vec[0]) + " , " + str(vec[1]) + " , " + str(vec[2]) + ")\n" #print(string) output.insert(END, string) #p.enable_cell_picking(through=True,callback=pick_atom,show_message=False,use_mesh=True) p.enable_point_picking(callback=pick_atom, use_mesh=True, show_message=False, show_point=False) p.window_size = 1000, 1000 p.store_image = True # Do the bonds #print("No. atoms: ",len(atoms)) output.insert(END, "No. atoms: " + str(len(atoms)) + "\n") if do_bonds: bond_length = [] bond_name = [] bond_ind = [] bonds = np.zeros(len(atoms)) for atom1 in range(len(pos)): for atom2 in range(atom1, len(pos)): dist = np.sqrt(np.sum((pos[atom1] - pos[atom2])**2)) if dist > 0 and dist < bond_cut: bond_length.append(dist) bond_name.append(symb[atom1] + str(atom_label[atom1]) + "-" + symb[atom2] + str(atom_label[atom2])) bond_ind.append([atom1, atom2]) bonds[atom1] += 1 bonds[atom2] += 1 bond_length = np.array(bond_length) valence = valency[atoms - 1] over = -valence + bonds pop = [] for atom in range(len(over)): if over[atom] <= 0: continue atom_loc = [] for n, i in enumerate(bond_ind): if atom == i[0] or atom == i[1]: atom_loc.append(n) lengths = bond_length[atom_loc] loc = np.argsort(lengths)[-int(over[atom]):] for i in loc: pop.append(atom_loc[i]) bond_length = list(bond_length) pop = np.flip(np.unique(pop)) for pi in pop: bond_length.pop(pi) bond_name.pop(pi) bond_ind.pop(pi) for i, index in enumerate(bond_ind): i, j = index if i in hide_num or j in hide_num: continue points = np.array([pos[i], (pos[j] + pos[i]) / 2]) direc = points[0] - points[1] mid = (points[1] + points[0]) / 2 height = np.sqrt(np.sum((points[0] - points[1])**2)) cyl = pv.Cylinder(mid, direc, np.min(atom_radii) / 20, height) p.add_mesh(cyl, color=atom_colours[i], pickable=False) points = np.array([pos[j], (pos[j] + pos[i]) / 2]) direc = points[0] - points[1] mid = (points[1] + points[0]) / 2 height = np.sqrt(np.sum((points[0] - points[1])**2)) cyl = pv.Cylinder(mid, direc, np.min(atom_radii) / 20, height) p.add_mesh(cyl, color=atom_colours[j], specular=0.3, specular_power=30, ambient=0.2, diffuse=1, pickable=False) #for i in bond_name: # print(i) if do_magmom: for i in range(len(pos)): if i in hide_num: continue temp_pos = pos[i] - np.array(mom_vec[i]) / 2 arrow = pv.Arrow(temp_pos, mom_vec[i], tip_length=0.15, tip_radius=0.09, tip_resolution=20, shaft_radius=0.04, shaft_resolution=20, scale='auto') p.add_mesh(arrow, color='b', pickable=False) if do_init_mag: for i in range(len(pos)): if i in hide_num: continue temp_pos = pos[i] - np.array(1.5 * init_spin[i]) / 2 arrow = pv.Arrow(temp_pos, 1.5 * init_spin[i], tip_length=0.15, tip_radius=0.09, tip_resolution=20, shaft_radius=0.04, shaft_resolution=20, scale='auto') p.add_mesh(arrow, color='g', pickable=False) try: p.camera.zoom(z) except: output.insert(END, "Zoom not implemented in this version of PyVista.\n") #print("Zoom not implemented in this version of PyVista.") ############################################################################################### def screenshot(): wind = p.window_size height = 2560 p.window_size = [ height, int(height * p.window_size[1] / p.window_size[0]) ] p.save_graphic(seed + ".eps") p.window_size = wind if do_verbose: output.insert(END, "Graphic saved!\n") p.add_key_event("s", screenshot) def perpendicular(a): b = np.empty_like(a) b[0] = -a[1] b[1] = a[0] return b # calculate the reciprocal lattice vectors a1 = latt[:, 0] a2 = latt[:, 1] a3 = latt[:, 2] b1 = np.cross(a2, a3) b2 = np.cross(a3, a1) b3 = np.cross(a1, a2) focus = np.matmul(latt, np.array([0.5, 0.5, 0.5])) if orient != None: cp = p.camera_position if orient == 'a': o = a3 vpvec = a1 / np.linalg.norm(a1) elif orient == 'a*': o = a3 vpvec = b1 / np.linalg.norm(b1) elif orient == 'b': o = a3 vpvec = a2 / np.linalg.norm(a2) elif orient == 'b*': o = a3 vpvec = b2 / np.linalg.norm(b2) elif orient == 'c': o = a2 vpvec = a3 / np.linalg.norm(a3) elif orient == 'c*': o = a2 vpvec = b3 / np.linalg.norm(b3) elif orient == "sd": o = a3 #T=[latt[i,i] for i in range(0,3)] T = 0.9 * a1 + 0.4 * a2 + 0.1 * a3 vpvec = T / np.linalg.norm(T) vp = focus + 15 * vpvec p.camera_position = [vp, focus, o] if np.sum(cam_pos) != 0: v = (cam_pos[0], cam_pos[1], cam_pos[2]) o = (cam_pos[3], cam_pos[4], cam_pos[5]) p.camera_position = [v, focus, o] def button_sd(): o = a3 T = 0.9 * a1 + 0.4 * a2 + 0.1 * a3 vpvec = T / np.linalg.norm(T) vp = focus + 15 * vpvec p.camera_position = [vp, focus, o] def button_a(): o = a3 vpvec = a1 / np.linalg.norm(a1) vp = focus + 15 * vpvec p.camera_position = [vp, focus, o] def button_b(): o = a3 vpvec = a2 / np.linalg.norm(a2) vp = focus + 15 * vpvec p.camera_position = [vp, focus, o] def button_c(): o = a2 vpvec = a3 / np.linalg.norm(a3) vp = focus + 15 * vpvec p.camera_position = [vp, focus, o] end = time.perf_counter() p.add_key_event("o", button_sd) p.add_key_event("a", button_a) p.add_key_event("b", button_b) p.add_key_event("c", button_c) output.insert(END, "Time: " + str(np.round(end - start, 4)) + " s\n") if save: p.window_size = [5000, 5000] #p.save_graphic(seed+".pdf") p.show(title=seed, screenshot=seed + ".png") else: p.show(title=seed) if do_verbose: print("Final Camera Position:") print(p.camera_position[0][0], p.camera_position[0][1], p.camera_position[0][2], p.camera_position[2][0], p.camera_position[2][1], p.camera_position[2][2]) sys.exit() window.mainloop()
traceback.print_exc() print(e) assert False, 'Castep calculator module could not be loaded' try: __import__(ase_castep_dir + ".io.castep") except Exception, e: assert False, 'Castep io module could not be loaded' tmp_dir = tempfile.mkdtemp() cwd = os.getcwd() from ase.calculators.castep import Castep try: c = Castep(directory=tmp_dir, label='test_label') except Exception, e: traceback.print_exc() print(e) assert False, 'Could not instantiate castep calculator' try: c.xc_functional = 'PBE' except Exception, e: traceback.print_exc() print(e) assert False, 'Setting xc_functional failed' import ase.lattice.cubic lattice = ase.lattice.cubic.BodyCenteredCubic('Li')