Exemplo n.º 1
0
    def write_config(self, params):
        cell_params = params[:6]
        # Make a list of 3 item lists
        atom_params = [self.atoms[0].position] + zip(*[iter(params[6:])] * 3)

        new_atoms = self.atoms.copy()
        new_atoms.set_cell([[cell_params[0], 0.0, 0.0],
                            [cell_params[1], cell_params[2], 0.0],
                            [cell_params[3], cell_params[4], cell_params[5]]])

        new_atoms.set_positions(atom_params)
        info("Cell volume: {0}".format(new_atoms.get_volume()))
        if self.calc_energy:
            self.potential.calc(new_atoms,
                                energy=True,
                                forces=True,
                                virial=True)
        # Add to the xyz
        self.out.write(new_atoms)
        sp_path = '{0:03d}'.format(self.counter)
        write_filename = '{0}.{1:03d}'.format(self.atoms.info['name'],
                                              self.counter)
        os.mkdir(sp_path)
        os.chdir(sp_path)
        castep_write(new_atoms, filename=write_filename, kpoint_spacing=0.015)
        info("Wrote a configuration {0}.".format(write_filename))
        os.chdir('..')
        self.counter += 1
Exemplo n.º 2
0
def create_density_function(atoms, potential, pressure, temperature, e0=None):
    """
    Create a function that calculates the density under the given conditions
    that requires only a set of parameters. Everything else is contained
    within the closure.

    Parameters
    ----------
    atoms : ase.Atoms
        Template structure used as the basis of the density function
        calculation.
    potential : Potential or str
        A quippy potential or a potential_str to initialise a potential
        used for calculating the density function.
    pressure : float
        Pressure in eV/A^3 of the state point to calcualte the density function.
    temperature : float
        Temperature in Kelvin of the state point.
    e0 : float
        Energy of the minimised structure. This will be calculated from
        `atoms` if not given.

    """

    if e0 is None:
        potential.calc(atoms, energy=True)
        e0 = atoms.energy

    info("Zero energy: {0}.".format(e0))

    def density_function(params):
        cell_params = params[:6]
        # Make a list of 3 item lists
        atom_params = [atoms[0].position] + zip(*[iter(params[6:])] * 3)

        new_atoms = atoms.copy()
        new_atoms.set_cell([[cell_params[0], 0.0, 0.0],
                            [cell_params[1], cell_params[2], 0.0],
                            [cell_params[3], cell_params[4], cell_params[5]]])

        new_atoms.set_positions(atom_params)

        potential.calc(new_atoms, energy=True)

        # Boltzmann in eV/Kelvin
        value = math.exp(
            -(new_atoms.energy - e0 + pressure * new_atoms.get_volume()) /
            (temperature * 0.00008617343))
        return value

    return density_function
Exemplo n.º 3
0
def relax_structure(system,
                    potential,
                    potential_filename=None,
                    relax_positions=True,
                    relax_cell=True):
    """
    Run a geometry optimisation on the structure to find the energy minimum.

    Parameters
    ----------
    system : ase.Atoms
        A system of atoms to run the minimisation on. The structure is
        altered in-place.
    potential : Potential or str
        A quippy Potential object with the desired potential, or a
        potential_str to initialise a new potential.

    Returns
    -------
    minimised_structure : Atoms
        The geometry optimised structure.
    """
    info("Inside minimiser.")

    qsystem = Atoms(system)

    if not isinstance(potential, Potential):
        if potential_filename:
            potential = Potential(potential, param_filename=potential_filename)
        else:
            potential = Potential(potential)
    qsystem.set_calculator(potential)

    minimiser = Minim(qsystem,
                      relax_positions=relax_positions,
                      relax_cell=relax_cell)

    with Capturing(debug_on_exit=True):
        minimiser.run()

    system.set_cell(qsystem.cell)
    system.set_positions(qsystem.positions)
    system.energy = qsystem.get_potential_energy()

    info("Minimiser done.")

    return system
Exemplo n.º 4
0
    def __init__(self, atoms, name, potential, calc_energy=True):
        """
        Initialise the writer with the templating atoms and
        potential.
        """
        self.atoms = atoms
        self.name = name
        self.potential = potential
        self.calc_energy = calc_energy
        self.counter = 0

        # TODO: check naming
        self.base_dir = os.getcwd()
        run_path = '{0}'.format(atoms.info['name'])
        info("Putting files in {0}.".format(run_path))
        os.mkdir(run_path)
        os.chdir(run_path)

        trajectory = 'traj_{0}.xyz'.format(atoms.info['name'])
        self.out = AtomsWriter(trajectory)
Exemplo n.º 5
0
Arquivo: bulk.py Projeto: tdaff/sats
def liquid(species, min_atoms=0, supercell=None):
    """
    Generic bcc solid with liquid volume scaling.

    Parameters
    ----------
    species : str or sats.core.elements.Element
        Atomic species used to generate the structures.
    min_atoms : int, optional
        Minimum number of atoms to include in a supercell of the
        bulk. If not specified, you get a unit cell.
    supercell : (int, int, int), optional
        Request a specific supercell of bulk material. If not given,
        max_atoms will be used instead to create a cubic cell.

    Returns
    -------
    liquid : ase.Atoms
        Atoms with liquid equivalent volume (scaled to 0K).

    """

    lattice_constant = properties[species]['lattice_constant']['liquid']

    atoms = ase_bulk(species, 'bcc', a=lattice_constant, orthorhombic=True)

    # TODO: non cubic supercells
    n_cells = int(ceil((min_atoms / len(atoms))**(1 / 3))) or 1

    if supercell is None:
        info("Generated supercell of {0}x{0}x{0}.".format(n_cells))
        super_atoms = atoms.repeat((n_cells, n_cells, n_cells))
    else:
        info("Generated supercell of {0}.".format(supercell))
        super_atoms = atoms.repeat(supercell)

    info("Structure contains {0} atoms.".format(len(super_atoms)))
    # This ensures that we can find the lattice constant later on
    super_atoms.info['lattice_constant'] = lattice_constant

    return super_atoms
Exemplo n.º 6
0
Arquivo: bulk.py Projeto: tdaff/sats
def bulk(species,
         lattice,
         min_atoms=0,
         supercell=None,
         lattice_constant=None,
         covera=None):
    """
    Helper to generate a generic bulk configuration.

    Parameters
    ----------
    species : str or sats.core.elements.Element
        Atomic species used to generate the structures.
    lattice : str
        Bulk lattice type to generate.
    min_atoms : int, optional
        Minimum number of atoms to include in a supercell of the
        bulk. If not specified, you get a unit cell.
    supercell : (int, int, int), optional
        Request a specific supercell of bulk material. If not given,
        max_atoms will be used instead to create a cubic cell.
    lattice_constant : float, optional
        Manually set the lattice constant, rather than using a lookup
        to find the value.

    Returns
    -------
    bulk : ase.Atoms
        Atoms in the desired lattice.
    """

    if lattice_constant is None:
        lattice_constant = properties[species]['lattice_constant'][lattice]

    if lattice == 'a15':
        atoms = a15(lattice_constant, species)
    elif lattice in ['fcc']:
        # Special check for fcc since the cubic and orthorhombic are
        # different.
        atoms = ase_bulk(species, lattice, a=lattice_constant, cubic=True)
    elif lattice in ['hcp']:
        if covera is None:
            covera = properties[species]['covera'][lattice]
        atoms = ase_bulk(species, lattice, a=lattice_constant, covera=covera)
    else:
        atoms = ase_bulk(species,
                         lattice,
                         a=lattice_constant,
                         orthorhombic=True)

    # TODO: non cubic supercells
    n_cells = int(ceil((min_atoms / len(atoms))**(1 / 3))) or 1

    if supercell is None:
        info("Generated supercell of {0}x{0}x{0}.".format(n_cells))
        super_atoms = atoms.repeat((n_cells, n_cells, n_cells))
    else:
        info("Generated supercell of {0}.".format(supercell))
        super_atoms = atoms.repeat(supercell)

    info("Structure contains {0} atoms.".format(len(super_atoms)))
    # This ensures that we can find the lattice constant later on
    super_atoms.info['lattice_constant'] = lattice_constant

    return super_atoms
Exemplo n.º 7
0
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.")
Exemplo n.º 8
0
def get(section, option=None):
    """
    Return the value for the specified option. The value type will be the
    type expected by the default value.

    Parameters
    ----------
    section : str
        The section where the option is located.
    option : str, optional
        The name of the option to retrieve. If not given, the section is
        split as section.option.

    Returns
    -------
    value : Any
        The value of the section.option option. The type is the type specified
        by the default option.
    """

    if option is None:
        section, option = section.split('.')

    try:
        signature = DEFAULTS[section][option]
    except KeyError:
        raise ValueError("Unknown option %s.%s" % (section, option))

    parser_function = DEFAULTS[section][option].parser_function

    if args.get(section, option) is not None:
        value = args.get(section, option)
        where = 'arguments'
    elif sats_ini.has_section(option) and sats_ini.has_option(section, option):
        value = sats_ini.get(section, option)
        where = 'sats ini'
    else:
        value = DEFAULTS[section][option].default_value
        where = 'defaults'

    # Parse through options
    if signature.multiple:
        try:
            # split on spaces and commas, throw away brackets
            value = [x for x in re.split('[\s,\(\)\[\]]*', value) if x]
        except TypeError:
            # Already a list or single value
            pass

        try:
            value = [parser_function(x) for x in value]
        except TypeError:
            # Make a list of a single value
            value = [parser_function(value)]
    else:
        value = parser_function(value)

    # Only announce the first time we see an option
    if (section, option) not in _seen:
        info("Option {0}.{1} = {2} found in {3}.".format(
            section, option, value, where))
        _seen.append((option, section))

    return value
Exemplo n.º 9
0
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.")