def __exit__(self, *args): os.close(self.stdout_fileno) self.extend(os.read(self.pipe_in, 99999).splitlines()) os.close(self.pipe_in) os.dup2(self.stdout_save, self.stdout_fileno) os.close(self.stdout_save) if self.debug_on_exit: for line in self: debug(line)
def kpoint_spacing_to_mesh(structure, density, spacing=None): """ Calculate the kpoint mesh that is equivalent to the given density in reciprocal Angstrom. Parameters ---------- structure : ase.Atoms A structure that can have get_reciprocal_cell called on it. density : float K-Point density in $A^{-1}$. spacing : list If a three item list is given it will be replaced with the actual calculated spacing. Returns ------- kpoint_mesh : [int, int, int] """ # No factor of 2pi in ase, otherwise need to divide through in the mesh try: r_cell = structure.get_reciprocal_cell() except NameError: r_cell = inv(structure.cell).transpose() r_x = sum(x**2 for x in r_cell[0])**0.5 r_y = sum(x**2 for x in r_cell[1])**0.5 r_z = sum(x**2 for x in r_cell[2])**0.5 kpoint_mesh = [ int(r_x / (density)) + 1, int(r_y / (density)) + 1, int(r_z / (density)) + 1 ] debug("Kpoints: {}".format(kpoint_mesh)) if spacing is not None: spacing[:] = [ r_x / kpoint_mesh[0], r_y / kpoint_mesh[1], r_z / kpoint_mesh[2] ] debug("Spacing: {}".format(spacing)) return kpoint_mesh
def add_option(section, option, default_value, parser_function, doc, multiple=False, valid_values=None): """ Add a option to the global namespace. Use as a decorator for any functions that take need options, these will be made available in the global options module. Parameters ---------- section : str Name of section in which to store the argument. option : str Name of the option. default_value : any Default value to assign to the option. May be any type that can be interpreted by the options module. parser_function : function Function to parse individual values of the data, e.g. bool, int, float, str... doc : str Description of the option. multiple : bool If True, multiple values will be returned as a tuple. valid_values : list, optional If valid_values are give, these will be the only options that can be used with this option and will be added to any other valid_values from other calls to this function. Returns ------- wrapper : function Decorating function that returns the unmodified function """ # No sections exist in the beginning if section not in DEFAULTS: DEFAULTS[section] = OrderedDict() # merge any existing valid values if valid_values and option in DEFAULTS[section]: valid_values.extend(DEFAULTS[section][option].valid_values) DEFAULTS[section][option] = Option(parser_function=parser_function, default_value=default_value, doc=doc, multiple=multiple, valid_values=valid_values) debug("New option {0}.".format(DEFAULTS[section][option])) # Return a function that does nothing so we can use this as a decorator def wrapper(func): """Do nothing but return the original function.""" return func return wrapper
def molecular_dynamics(system, potential, potential_filename=None, temperature=300, total_steps=1100000, timestep=1.0, connect_interval=200, write_interval=20000, equilibration_steps=100000, out_of_plane=None, random_seed=None): """ Run very simple molecular dynamics to generate some configurations. Writes configurations out as xyz and CASTEP files. """ info("Inside MD.") if random_seed is None: random_seed = random.SystemRandom().randint(0, 2**63) quippy.system.system_set_random_seeds(random_seed) info("Quippy Random Seed {0}.".format(random_seed)) system = Atoms(system) # Can take Potential objects, or just use a string if not isinstance(potential, Potential): if potential_filename: potential = Potential(potential, param_filename=potential_filename) else: potential = Potential(potential) system.set_calculator(potential) dynamical_system = DynamicalSystem(system) with Capturing(debug_on_exit=True): dynamical_system.rescale_velo(temperature) if out_of_plane is not None: # Stop things moving vertically in the cell dynamical_system.atoms.velo[3, :] = 0 base_dir = os.getcwd() run_path = '{0}_{1:g}/'.format(system.info['name'], temperature) info("Putting files in {0}.".format(run_path)) os.mkdir(run_path) os.chdir(run_path) trajectory = 'traj_{0}_{1:g}.xyz'.format(system.info['name'], temperature) out = AtomsWriter(trajectory) dynamical_system.atoms.set_cutoff(potential.cutoff() + 2.0) dynamical_system.atoms.calc_connect() potential.calc(dynamical_system.atoms, force=True, energy=True, virial=True) structure_count = 0 # Basic NVE molecular dynamics for step_number in range(1, total_steps + 1): dynamical_system.advance_verlet1(timestep, virial=dynamical_system.atoms.virial) potential.calc(dynamical_system.atoms, force=True, energy=True, virial=True) dynamical_system.advance_verlet2(timestep, f=dynamical_system.atoms.force, virial=dynamical_system.atoms.virial) # Maintenance of the system if not step_number % connect_interval: debug("Connect at step {0}".format(step_number)) dynamical_system.atoms.calc_connect() if step_number < equilibration_steps: with Capturing(debug_on_exit=True): dynamical_system.rescale_velo(temperature) if not step_number % write_interval: debug("Status at step {0}".format(step_number)) # Print goes to captured stdout with Capturing(debug_on_exit=True): dynamical_system.print_status( epot=dynamical_system.atoms.energy) dynamical_system.rescale_velo(temperature) if step_number > equilibration_steps: debug("Write at step {0}".format(step_number)) out.write(dynamical_system.atoms) sp_path = '{0:03d}'.format(structure_count) write_filename = '{0}_{1:g}.{2:03d}'.format( system.info['name'], temperature, structure_count) os.mkdir(sp_path) os.chdir(sp_path) castep_write(dynamical_system.atoms, filename=write_filename) espresso_write(dynamical_system.atoms, prefix=write_filename) write_extxyz("{0}.xyz".format(write_filename), dynamical_system.atoms) info("Wrote a configuration {0}.".format(write_filename)) os.chdir('..') structure_count += 1 out.close() os.chdir(base_dir) info("MD Done.")
def slice_sample(bulk, potential, potential_filename, temperature, pressure, lattice_delta, atom_delta, m_max, e0=None, init_d=0, num_configs=10, write_interval=-1, random_seed=None): info("Inside Slice Sample.") # Randomise the random seed if random_seed is None: random_seed = SystemRandom().randint(0, 2**63) quippy.system.system_set_random_seeds(random_seed) seed(random_seed) info("Quippy Random Seed {0}.".format(random_seed)) info("Python Random Seed {0}.".format(random_seed)) if not isinstance(potential, Potential): if potential_filename: potential = Potential(potential, param_filename=potential_filename) else: potential = Potential(potential) bulk = Atoms(bulk) # pressure in GPa -> eV/A^3 pressure = pressure / quippy.GPA density_function = create_density_function(bulk, potential, pressure, temperature, e0) # Convert to triangle lattice representation (lower triangle in cell) scaled_positions = bulk.get_scaled_positions() lattice_params = quippy.get_lattice_params(bulk.lattice) new_cell = quippy.make_lattice(*lattice_params).T bulk.set_cell(new_cell) bulk.set_scaled_positions(scaled_positions) params = [ bulk.cell[0][0], bulk.cell[1][0], bulk.cell[1][1], bulk.cell[2][0], bulk.cell[2][1], bulk.cell[2][2] ] info("Re-orineted to cell: {0}.".format(new_cell.tolist())) for atom in bulk.positions.tolist()[1:]: params.extend(atom) # value for the first iteration df_value = density_function(params) # Floating count so that division by write_interval make it integer for # written configurations count = 0.0 idx = init_d output = ParamWriter(bulk, bulk.info['name'], potential) # Only write once everything has changed if write_interval < 1: write_interval = len(params) info("Writing configurations after {0} steps.".format(write_interval)) # Loop just iterates to the next value of x while count / write_interval < num_configs: # Determine the delta value outside the incrementer, so we can make it # do less work if idx < 6: delta = lattice_delta else: delta = atom_delta # Here's where the magic happens params, df_value = increment_params(density_function, params, idx, delta, m_max, df_0=df_value) debug("SLICE_SAMPLE: {0:g}".format(count / write_interval)) debug("Params: {0}.".format(", ".join("{0}".format(x) for x in params))) if not count % write_interval: output.write_config(params) count += 1 idx += 1 if idx >= len(params): idx = 0 output.close() info("Slice Sample Done.")