def testing_calculator(testing_keywords, tmp_path, pspot_tmp_path): castep_path = os.path.join(tmp_path, 'CASTEP') os.mkdir(castep_path) return Castep(castep_keywords=testing_keywords, directory=castep_path, castep_pp_path=pspot_tmp_path)
def create_hfine_castep_calculator(mu_symbol='H:mu', calc=None, param_file=None, kpts=[1, 1, 1]): """Create a calculator containing all the necessary parameters for a hyperfine calculation.""" if not isinstance(calc, Castep): calc = Castep() else: calc = deepcopy(calc) gamma_block = calc.cell.species_gamma.value calc.cell.species_gamma = add_to_castep_block(gamma_block, mu_symbol, constants.m_gamma, 'gamma') calc.cell.kpoint_mp_grid = kpts if param_file is not None: calc.param = read_param(param_file).param calc.param.task = 'Magres' calc.param.magres_task = 'Hyperfine' return calc
def __init__(self, client_id, exe, env=None, npj=1, ppn=1, block=None, corner=None, shape=None, jobname='socketcalc', rundir=None, fmt='REFTRAJ', parmode=None, mpirun='mpirun', mpirun_args=['-np'], logger=screen, max_pos_diff=MAX_POS_DIFF_CASTEP, max_cell_diff=MAX_CELL_DIFF_CASTEP, **castep_args): Client.__init__(self, client_id, exe, env, npj, ppn, block, corner, shape, jobname, rundir, fmt, parmode, mpirun, mpirun_args, logger, max_pos_diff, max_cell_diff) if 'task' not in castep_args: self.logger.pr('No task key in castep_args, setting task=MD') castep_args['task'] = 'MD' if 'md_ensemble' not in castep_args: self.logger.pr('No md_ensemble key in castep_args, setting md_ensemble=SKT') castep_args['md_ensemble'] = 'SKT' if 'md_num_iter' not in castep_args: self.logger.pr('No md_num_iter key in castep_args, setting md_num_iter=1000000') castep_args['md_num_iter'] = 1000000 castep_args['_rename_existing_dir'] = False self.castep_args = castep_args self.logger.pr('constructing Castep instance with args %r' % castep_args) self.castep = Castep(directory=self.subdir, **castep_args) self._orig_devel_code = '' if self.castep.param.devel_code.value is not None: self._orig_devel_code = self.castep.param.devel_code.value.strip()+'\n'
def start(test_name): global calculator calculator = Castep( directory="./_CASTEP", cut_off_energy=600, #700 max_scf_cycles=250, calculate_stress=True, finite_basis_corr='automatic', smearing_width='0.1', #elec_method='edft', mixing_scheme='Pulay', kpoints_mp_spacing='0.02', #0.015 write_checkpoint='none')
def read_param(filename, calc=None): """Reads a param file. If an Castep object is passed as the second argument, the parameter setings are merged into the existing object and returned. Otherwise a new Castep() calculator instance gets created and returned. Parameters: filename: the .param file. Only opens reading calc: [Optional] calculator object to hang parameters onto """ if calc is None: from ase.calculators.castep import Castep calc = Castep(check_castep_version=False) calc.merge_param(filename) return calc
def __init__(self,directory_path,seedname): r""" Initialises an instance of the :class:`~effmass.inputs.DataCastep` class. Args: directory_path (str): The path to a directory containing seedname.cell, seedname.out and seedname.bands seedname (str): The name (without suffix) of the input and output files Returns: None. """ Castep_calculator = Castep(directory_path) Castep_calculator.atoms = io.read(directory_path+"./"+seedname+".cell", format='castep-cell') ASE_bandstructure = Castep_calculator.band_structure(directory_path+"./"+seedname+".bands") ASE_atoms = Castep_calculator.atoms super().__init__(ASE_bandstructure, ASE_atoms)
def read_data(self): # Read the output from all completed jobs jobstate = self.check() jobdata = OrderedDict([('E', {}), ('F', {}), ('S', {})]) def get_vals(c): nrg = c._energy_total frc = c._forces strs = c._stress return nrg, frc, strs for name, job in self._worktree.items(): if jobstate[name] != C_COMPLETE: # Job isn't finished utils.warn('Results for {0} missing, skipping.'.format(name)) continue ccalc = Castep(keyword_tolerance=3) ccalc.read(job.castep) nrg, frc, strs = get_vals(ccalc) jobdata['E'][name] = nrg jobdata['F'][name] = max(np.linalg.norm(frc, axis=1)) jobdata['S'][name] = np.linalg.norm(strs) # Organise it by ranges wtree = self._worktree data_curves = OrderedDict() for X, jobrange in self._ranges.items(): # Get an effective jobrange jobcomplete = [j for j in jobrange if jobstate[j] == C_COMPLETE] data_curves[X] = { 'values': np.array([wtree[j].values[X] for j in jobcomplete]), 'labels': [wtree[j].labels[X] for j in jobcomplete], 'Ys': OrderedDict() } for Y, data in jobdata.items(): data_curves[X]['Ys'][Y] = np.array( [data[j] for j in jobcomplete]) return data_curves
def read_castep_castep(fd, index=None): """ Reads a .castep file and returns an atoms object. The calculator information will be stored in the calc attribute. There is no use of the "index" argument as of now, it is just inserted for convenience to comply with the generic "read()" in ase.io Please note that this routine will return an atom ordering as found within the castep file. This means that the species will be ordered by ascending atomic numbers. The atoms witin a species are ordered as given in the original cell file. Note: This routine returns a single atoms_object only, the last configuration in the file. Yet, if you want to parse an MD run, use the novel function `read_md()` """ from ase.calculators.castep import Castep try: calc = Castep() except Exception as e: # No CASTEP keywords found? warnings.warn( 'WARNING: {0} Using fallback .castep reader...'.format(e)) # Fall back on the old method return read_castep_castep_old(fd, index) calc.read(castep_file=fd) # now we trick the calculator instance such that we can savely extract # energies and forces from this atom. Basically what we do is to trick the # internal routine calculation_required() to always return False such that # we do not need to re-run a CASTEP calculation. # # Probably we can solve this with a flag to the read() routine at some # point, but for the moment I do not want to change too much in there. calc._old_atoms = calc.atoms calc._old_param = calc.param calc._old_cell = calc.cell return [calc.atoms] # Returning in the form of a list for next()
def castep_write_input(a, folder, calc=None, name=None, script=None): """Writes input files for an Atoms object with a Castep calculator. | Args: | a (ase.Atoms): Atoms object to write. Can have a Castep | calculator attached to carry cell/param | keywords. | folder (str): Path to save the input files to. | calc (ase.Calculator): Calculator to attach to Atoms. If | present, the pre-existent one will | be ignored. | name (str): Seedname to save the files with. If not | given, use the name of the folder. | script (str): Path to a file containing a submission script | to copy to the input folder. The script can | contain the argument {seedname} in curly braces, | and it will be appropriately replaced. """ if name is None: name = os.path.split(folder)[-1] # Same as folder name if calc is not None: a.set_calculator(calc) if not isinstance(a.calc, Castep): a = a.copy() calc = Castep(atoms=a) a.set_calculator(calc) io.write(os.path.join(folder, name + '.cell'), a, magnetic_moments='initial') write_param(os.path.join(folder, name + '.param'), a.calc.param, force_write=True) if script is not None: stxt = open(script).read() stxt = stxt.format(seedname=name) with open(os.path.join(folder, 'script.sh'), 'w') as sf: sf.write(stxt)
def _create_calculator(self, calc_type=None): with silence_stdio(): if self._calc is not None and isinstance(self._calc, Castep): calc = deepcopy(self._calc) else: calc = Castep() mu_symbol = self.params.get('mu_symbol', 'H:mu') # Start by ensuring that the muon mass and gyromagnetic ratios are # included gamma_block = calc.cell.species_gamma.value if gamma_block is None: calc.cell.species_gamma = add_to_castep_block( gamma_block, mu_symbol, constants.m_gamma, 'gamma') mass_block = calc.cell.species_mass.value calc.cell.species_mass = add_to_castep_block( mass_block, mu_symbol, constants.m_mu_amu, 'mass') # Now assign the k-points k_points_param = self.params.get('k_points_grid') if k_points_param is not None: calc.cell.kpoint_mp_grid = list_to_string(k_points_param) else: if calc.cell.kpoint_mp_grid is None: calc.cell.kpoint_mp_grid = list_to_string([1, 1, 1]) # Read the parameters pfile = self.params.get('castep_param', None) if pfile is not None: with silence_stdio(): calc.param = read_param(self.params['castep_param']).param self._calc = calc if calc_type == "MAGRES": calc = self._create_hfine_castep_calculator() elif calc_type == "GEOM_OPT": calc = self._create_geom_opt_castep_calculator() return self._calc
def start(test_name): global calculator calculator = Castep(directory=test_name, cut_off_energy=250, spin_polarized=False, opt_strategy='speed', xc_functional='PW91', elec_energy_tol='0.0000001', max_scf_cycles=250, fix_occupancy=False, calculate_stress=True, finite_basis_corr='automatic', smearing_width='0.05', fine_grid_scale=4, mixing_scheme='pulay', mix_history_length=20, num_dump_cycles=0, kpoints_mp_spacing='0.030', # note that other values were used for some tests, e.g. 0.015 for bulk E(V), 0.07 for minimization of GAP amorphous structures, and perhaps some other variations species_pot = ("Si","{}/Si_OTF.usp".format(model_abs_dir)), perc_extra_bands=200)
def start(test_name): global calculator calculator = Castep() calculator._directory = "./_CASTEP" calculator.param.cut_off_energy = 700 calculator.param.elec_energy_tol = 1E-7 calculator.param.elec_force_tol = 1E-3 calculator.param.spin_polarised = False calculator.param.mixing_scheme = 'Pulay' calculator.param.write_checkpoint = 'none' calculator.param.fine_grid_scale = 2 calculator.param.smearing_width = 0.1 calculator.param.finite_basis_corr = 'automatic' calculator.param.calculate_stress = True calculator.cell.kpoints_mp_spacing = 0.02 return calculator
def read_param(filename='', calc=None, fd=None, get_interface_options=False): if fd is None: if filename == '': raise ValueError('One between filename and fd must be provided') fd = open(filename) elif filename: warnings.warn('Filestream used to read param, file name will be ' 'ignored') # If necessary, get the interface options if get_interface_options: int_opts = {} optre = re.compile(r'# ASE_INTERFACE ([^\s]+) : ([^\s]+)') lines = fd.readlines() fd.seek(0) for l in lines: m = optre.search(l) if m: int_opts[m.groups()[0]] = m.groups()[1] data = read_freeform(fd) if calc is None: from ase.calculators.castep import Castep calc = Castep(check_castep_version=False, keyword_tolerance=2) for kw, (val, otype) in data.items(): if otype == 'block': val = val.split('\n') # Avoids a bug for one-line blocks calc.param.__setattr__(kw, val) if not get_interface_options: return calc else: return calc, int_opts
def save_muonconf_castep(a, folder, params): # Muon mass and gyromagnetic ratio mass_block = 'AMU\n{0} 0.1138' gamma_block = 'radsectesla\n{0} 851586494.1' if isinstance(a.calc, Castep): ccalc = a.calc else: ccalc = Castep() ccalc.cell.kpoint_mp_grid.value = list_to_string(params['k_points_grid']) ccalc.cell.species_mass = mass_block.format( params['mu_symbol']).split('\n') ccalc.cell.species_gamma = gamma_block.format( params['mu_symbol']).split('\n') ccalc.cell.fix_all_cell = True # To make sure for older CASTEP versions a.set_calculator(ccalc) name = os.path.split(folder)[-1] io.write(os.path.join(folder, '{0}.cell'.format(name)), a) ccalc.atoms = a if params['castep_param'] is not None: castep_params = yaml.load(open(params['castep_param'], 'r')) else: castep_params = {} # Parameters from .yaml will overwrite parameters from .param castep_params['task'] = "GeometryOptimization" castep_params['geom_max_iter'] = params['geom_steps'] castep_params['geom_force_tol'] = params['geom_force_tol'] castep_params['max_scf_cycles'] = params['max_scc_steps'] parameter_file = os.path.join(folder, '{0}.param'.format(name)) yaml.safe_dump(castep_params, open(parameter_file, 'w'), default_flow_style=False)
def create_muairss_castep_calculator(a, params={}, calc=None): """Create a calculator containing all the necessary parameters for a geometry optimization.""" if not isinstance(calc, Castep): calc = Castep() else: calc = deepcopy(calc) musym = params.get('mu_symbol', 'H:mu') # Start by ensuring that the muon mass and gyromagnetic ratios are included mass_block = calc.cell.species_mass.value calc.cell.species_mass = add_to_castep_block(mass_block, musym, cnst.m_mu_amu, 'mass') gamma_block = calc.cell.species_gamma.value calc.cell.species_gamma = add_to_castep_block(gamma_block, musym, 851586494.1, 'gamma') # Now assign the k-points calc.cell.kpoint_mp_grid = list_to_string( params.get('k_points_grid', [1, 1, 1])) calc.cell.fix_all_cell = True # Necessary for older CASTEP versions calc.param.charge = params.get('charged', False)*1.0 # Read the parameters pfile = params.get('castep_param', None) if pfile is not None: calc.param = read_param(params['castep_param']).param calc.param.task = 'GeometryOptimization' calc.param.geom_max_iter = params.get('geom_steps', 30) calc.param.geom_force_tol = params.get('geom_force_tol', 0.05) calc.param.max_scf_cycles = params.get('max_scc_steps', 30) return calc
def read_castep_new(filename, index=None): """ This routine is supposed to replace the former read_castep() routine at some point. Basically it does the same job, but it uses the read() functionality from the Castep calculator class. This allows a much more complete parsing and we do not have to take care of syncing the respective routine with each other. Note: This routine returns a single atoms_object only, whereas the former routine, in principle, returned a list of atoms objects. Yet, if you want to parse an MD run, use the novel function `read_md()` There is no use of the "index" argument as of now, it is just inserted for convenience to comply with the generic "read()" in ase.io Please note that this routine will return an atom ordering as found within the castep file. This means that the species will be ordered by ascending atomic numbers. The atoms witin a species are ordered as given in the original cell file. """ from ase.calculators.castep import Castep calc = Castep() calc.read(castep_file=filename) # now we trick the calculator instance such that we can savely extract # energies and forces from this atom. Basically what we do is to trick the # internal routine calculation_required() to always return False such that # we do not need to re-run a CASTEP calculation. # # Probably we can solve this with a flag to the read() routine at some # point, but for the moment I do not want to change too much in there. calc._old_atoms = calc.atoms calc._old_param = calc.param calc._old_cell = calc.cell return calc.atoms
def convert_single_structure(gen_file, param_file, directory="."): atoms = io.read(gen_file) params = load_input_file(param_file) # Muon mass and gyromagnetic ratio mass_block = 'AMU\n{0} 0.1138' gamma_block = 'radsectesla\n{0} 851586494.1' ccalc = Castep(castep_command=params['castep_command']) ccalc.cell.kpoint_mp_grid.value = list_to_string(params['k_points_grid']) ccalc.cell.species_mass = mass_block.format( params['mu_symbol']).split('\n') ccalc.cell.species_gamma = gamma_block.format( params['mu_symbol']).split('\n') ccalc.cell.fix_all_cell = True # To make sure for older CASTEP versions atoms.set_calculator(ccalc) symbols = atoms.get_chemical_symbols() symbols[-1] = params['mu_symbol'] atoms.set_array('castep_custom_species', np.array(symbols)) name = "{}.cell".format(params['name']) cell_file = os.path.join(directory, name) io.write(cell_file, atoms) # Param file? if params['castep_param'] is not None: p = read_param(params['castep_param']).param # Set up task and geometry optimization steps p.task.value = 'GeometryOptimization' p.geom_max_iter.value = params['geom_steps'] p.geom_force_tol.value = params['geom_force_tol'] param_file = os.path.join(directory, "{}.param".format(params['name'])) write_param(param_file, p, force_write=True)
"""Simple shallow test of the CASTEP interface""" import os import shutil import tempfile import ase import re 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(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.calc = c param_fn = os.path.join(tmp_dir, 'myParam.param') with open(param_fn, 'w') as param: 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') 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.calc = 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.calc = 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.calc = 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.calc = 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.calc = 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]
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')
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')
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: # 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').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').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) if pos_block is None: pos_type = 'scaled_positions' pos_block = celldict.pop('positions_frac', None) aargs[pos_type] = [] lines = pos_block.split('\n') line_tokens = [l.split() for l in lines] if not 'scaled' 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').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').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').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 in celldict.items(): try: 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
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
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 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()
def calc(self, **kwargs): from ase.calculators.castep import Castep return Castep(castep_command=self.executable, **kwargs)
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 test_castep_interface(): # check if we have castep installed installed() # check if we can import everything ase_castep_dir = "ase" try: castep_calc = __import__( ase_castep_dir + ".calculators.castep", globals(), locals(), ["Castep", "CastepParam", "create_castep_keywords"]) Castep = castep_calc.Castep CastepParam = castep_calc.CastepParam create_castep_keywords = castep_calc.create_castep_keywords except Exception as e: traceback.print_exc() print(e) raise AssertionError('Castep calculator module could not be loaded') try: __import__(ase_castep_dir + ".io.castep") except Exception as e: raise AssertionError('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 as e: traceback.print_exc() print(e) raise AssertionError('Could not instantiate castep calculator') try: c.xc_functional = 'PBE' except Exception as e: traceback.print_exc() print(e) raise AssertionError('Setting xc_functional failed') import ase.lattice.cubic 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') try: lattice.set_calculator(c) except Exception as e: traceback.print_exc() print(e) raise AssertionError('Setting the calculator %s failed' % c) try: create_castep_keywords(castep_command=os.environ['CASTEP_COMMAND'], path=tmp_dir, fetch_only=20) except Exception as e: traceback.print_exc() print(e) raise AssertionError("Cannot create castep_keywords, this usually means a bug"\ + " in the interface or the castep binary cannot be called") 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() try: c.merge_param(param_fn) except Exception as e: traceback.print_exc() print(e) raise AssertionError("Error in merge_param_filename, go figure") # check if the CastepOpt, CastepCell comparison mechanism works p1 = CastepParam() p2 = CastepParam() if not p1._options == p2._options: raise AssertionError( "Print two newly created CastepParams are not the same") p1._options['xc_functional'].value = 'PBE' p1.xc_functional = 'PBE' if p1._options == p2._options: raise AssertionError( "Changed one CastepParam, but the still look the same") if not c.calculation_required(lattice): raise AssertionError( "Calculator does not fetch that a calculation is required") if not c.dryrun_ok(): print(c._error) raise AssertionError("Dryrun_ok does not work, where it should") else: print("Dryrun is ok") c.prepare_input_files(lattice) os.chdir(cwd) shutil.rmtree(tmp_dir) print("Test finished without errors")
if exists: complete.append(el) psstr = [(e, p) for e, p in psstr if e not in complete] pspotcalc[lib]['elements'] += complete # Remove all existing cell files for f in glob.glob(os.path.join(libdir, '*.cell')): os.remove(f) for i in range(0, len(psstr), args.n): block = [(e, s + ('' if s[-2:] == '[]' else '[]')) for e, s in psstr[i:i + args.n]] elems = list(zip(*block))[0] a = Atoms(elems, cell=[1, 1, 1]) calc = Castep(castep_command=args.cbin) calc.cell.species_pot = block a.set_calculator(calc) seed = '{0}-{1}'.format(elems[0], elems[-1]) io.write(os.path.join(libdir, seed + '.cell'), a) # Run calculation if not args.nocalc: print('Calculation - Library: {0}, Elements: {1}'.format( lib, seed)) proc = sp.Popen([args.cbin, '--dryrun', seed], cwd=libdir, stdout=sp.PIPE, stderr=sp.PIPE) out, err = proc.communicate()