Ejemplo n.º 1
0
 def test_element_from_symbol(self, Sodium):
     na = element_from_symbol("Na")
     assert na == Sodium
     na = element_from_symbol("na")
     assert na == Sodium
     na = element_from_symbol("NA")
     assert na == Sodium
Ejemplo n.º 2
0
 def test_invalid_element_from_symbol(self):
     with pytest.raises(ElementError):
         na = element_from_symbol("Na0")
     with pytest.raises(ElementError):
         na = element_from_symbol("sodium")
     with pytest.raises(TypeError):
         na = element_from_symbol(11)
Ejemplo n.º 3
0
 def set_elements(self):
     """Set the ele.element of each particle in the compound."""
     for p in self.particles():
         try:
             p.element = element_from_symbol(p.name)
         except ElementError:
             # This is a hack for our typed mol2 files
             p.element = element_from_symbol(p.name.strip("0123456789"))
Ejemplo n.º 4
0
 def _generate_compounds(self):
     if self.system.monomer_sequence is not None:
         sequence = self.system.monomer_sequence
     else:
         sequence = "random"
     random.seed(self.system.seed)
     mb_compounds = []
     for length, n in zip(
             self.system.polymer_lengths,
             self.system.n_compounds
             ):
         for i in range(n):
             polymer, sequence = build_molecule(
                 self.system.molecule,
                 length,
                 sequence,
                 self.system.para_weight
             )
             self.system.molecule_sequences.append(sequence)
             mb_compounds.append(polymer)
             self.system.para += sequence.count("P")
             self.system.meta += sequence.count("M")
         mass = n * sum(
             [ele.element_from_symbol(p.name).mass
             for p in polymer.particles()]
         )
         self.system.system_mass += mass  # amu
     return mb_compounds
Ejemplo n.º 5
0
 def test_element_from_mass(self, Sodium, Magnesium):
     na = element_from_mass(22.98)
     assert na == Sodium
     with pytest.warns(UserWarning):
         mg = element_from_mass(24.0, exact=False)
     assert mg == Magnesium
     with pytest.warns(UserWarning):
         mg = element_from_mass(24, exact=False)
     assert mg == Magnesium
     elements = element_from_mass(289.0, duplicates="none")
     assert elements == None
     elements = element_from_mass(289.0, duplicates="all")
     fl = element_from_symbol("Fl")
     uup = element_from_symbol("Uup")
     assert elements == (fl, uup)
     elements = element_from_mass(288.5, duplicates="all", exact=False)
     assert elements == (fl, uup)
Ejemplo n.º 6
0
 def custom(self, file_path):
     system = mb.load(file_path)
     mass = sum(
             [ele.element_from_symbol(p.name).mass
             for p in system.particles()]
             )
     self.system.system_mass += mass
     self.L = self._calculate_L()
     system.box = system.get_boundingbox()
     return system
Ejemplo n.º 7
0
def _proto_to_mb(proto):
    """ Given compound_pb2.Compound, create mb.Compound 
    
    Parameters
    ----------
    proto: compound_pb2.Compound()
    
    """
    if proto.element.symbol is '':
        elem = None
    else:
        elem = ele.element_from_symbol(proto.element.symbol)
    return  mb.Compound(name=proto.name,
                pos=[proto.pos.x, proto.pos.y, proto.pos.z],
                charge=proto.charge,
                periodicity=[proto.periodicity.x, proto.periodicity.y, 
                            proto.periodicity.z],
                element=elem)
Ejemplo n.º 8
0
def _proto_to_mb(proto):
    """Given compound_pb2.Compound, create mb.Compound.

    Parameters
    ----------
    proto: compound_pb2.Compound()
    """
    if proto.element.symbol == "":
        elem = None
    else:
        elem = ele.element_from_symbol(proto.element.symbol)
    lengths = [proto.periodicity.x, proto.periodicity.y, proto.periodicity.z]
    if np.all(np.array(lengths) != 0):
        box = Box(lengths)
    else:
        box = None
    return Compound(
        name=proto.name,
        pos=[proto.pos.x, proto.pos.y, proto.pos.z],
        charge=proto.charge,
        box=box,
        element=elem,
    )
Ejemplo n.º 9
0
    def md_initialization(self):
        molecules = self.molecules
        functional = self.functional
        box = self.box
        cutoff = self.cutoff
        scf_tolerance = self.scf_tolerance
        basis_set = self.basis_set
        basis_set_filename = self.basis_set_filename
        potential_filename = self.potential_filename
        fixed_list = self.fixed_list
        periodicity = self.periodicity
        simulation_time = self.simulation_time
        time_step = self.time_step
        ensemble = self.ensemble
        project_name = self.project_name
        temperature = self.temperature
        pressure = self.pressure
        n_molecules = self.n_molecules
        thermostat = self.thermostat
        traj_type = self.traj_type
        traj_freq = self.traj_freq
        seed = self.seed
        initial_coordinate_filename = self.initial_coordinate_filename
        use_atom_name_as_symbol = self.use_atom_name_as_symbol

        atom_list = []
        mass_list = []
        symbol_list = []
        for i in range(len(molecules)):
            current_molecule = mb.clone(molecules[i])
            for particle in current_molecule.particles():
                atom_list.append(particle.name)
                if not (use_atom_name_as_symbol):
                    symbol_list.append(particle.element)
                else:
                    symbol_list.append(particle.name)
                if use_atom_name_as_symbol:
                    mass_list.append(
                        ele.element_from_symbol("{}".format(
                            particle.name)).mass)
                else:
                    mass_list.append(
                        ele.element_from_symbol("{}".format(
                            particle.element)).mass)

        for i in range(len(molecules)):
            current_molecule = mb.clone(molecules[i])
            for particle in current_molecule.particles():
                atom_list.append(particle.name)
                if not (use_atom_name_as_symbol):
                    symbol_list.append(particle.element)
                else:
                    symbol_list.append(particle.name)
                if use_atom_name_as_symbol:
                    mass_list.append(
                        ele.element_from_symbol("{}".format(
                            particle.name)).mass)
                else:
                    mass_list.append(
                        ele.element_from_symbol("{}".format(
                            particle.element)).mass)

        if project_name == None:
            self.project_name = 'sample_project'
            print('project_name not specified, set as sample_project')
        if cutoff == None:
            self.cutoff = 600
            print('cutoff not specified, set as 600')
        if scf_tolerance == None:
            self.scf_tolerance = 1e-6
            print('scf_tolerance not specified, set as 1e-6')
        if basis_set_filename == None:
            self.basis_set_filename = 'BASIS_MOLOPT'
            print('basis_set_filename not defined, set as BASIS_MOLOPT')
        if potential_filename == None:
            self.potential_filename = 'GTH_POTENTIALS'
            print('potential_filename not specified, set as GTH_POTENTIALS')
        if periodicity == None:
            self.periodicity = 'XYZ'
            print('periodicity not specified, set as XYZ')
        if simulation_time == None:
            self.simulation_time = 1 * u.ps  #ps
            print('simulation_time not specified, set as 1 ps')

        if time_step == None:
            lightest = min(mass_list)

            if lightest < 1.5:
                self.time_step = 0.5 * u.fs
                print(
                    'time_step not specified, time_step set as 0.5 fs as the lighest element has mass {} au'
                    .format(lightest))
            elif (lightest >= 1.5) and (lightest < 40):
                self.time_step = 1 * u.fs
                print(
                    'time_step not specified, time_step set as 1 fs as the lighest element has mass {} au'
                    .format(lightest))
            if lightest >= 40:

                self.time_step = 1.5 * u.fs
                print(
                    'time_step not specified, time_step set as 1.5 fs as the lighest element has mass {} au'
                    .format(lightest))

        if ensemble == None:
            self.ensemble = 'NVE'
            print('ensemble not specified, set as NVE')
        if temperature_ensemble(ensemble):
            if thermostat == None:
                self.thermostat = 'NOSE'
        if traj_type == None:
            self.traj_type = 'XYZ'
            print('output trajectory format set as XYZ')
        if traj_freq == None:
            self.traj_freq = 10
        if seed == None:
            self.seed = 0

        if self.input_filename == None:
            self.input_filename = self.project_name + '_md_input.inp'
            print('input_filename not specified, set as {}'.format(
                self.input_filename))
        if self.output_filename == None:
            self.output_filename = self.project_name + '_md_output.out'
            print('output_filename not specified, set as {}'.format(
                self.output_filename))

        output_pos_filename = self.project_name + "-pos-1.xyz"

        print('Output position filename is {}'.format(output_pos_filename))

        if self.temperature is not None:

            self.temperature = (temperature.to('K')).value
        if self.pressure is not None:
            self.pressure = (pressure.to('bar')).value

        self.simulation_time = (self.simulation_time.to('fs')).value
        self.time_step = (self.time_step.to('fs')).value

        print('You can change default settings in setter.md_files')
Ejemplo n.º 10
0
def write_qcc_pair_input(snap,
                         chromo_i,
                         chromo_j,
                         j_shift,
                         conversion_dict=None):
    """Write a quantum chemical input string for chromophore pairs.

    Pair input requires taking periodic images into account.
    Input string for pySCF containing elements and positions in Angstroms
    (e.g., "C 0.0 0.0 0.0; H 1.54 0.0 0.0")
    See https://pyscf.org/quickstart.html for more information.

    Parameters
    ----------
    snap : gsd.hoomd.Snapshot
        Atomistic simulation snapshot from a GSD file. It is expected that the
        lengths in this file have been converted to Angstroms.
    chromo_i : Chromophore
        One of the chromophores to be written.
    chromo_j : Chromophore
        One of the chromophores to be written.
    j_shift : numpy.ndarray(3)
        Vector to shift chromo_j.
        (chromo_j minimum image center - unwrapped center)
    conversion_dict : dictionary, default None
        A dictionary that maps the atom type to its element. e.g., `{'c3': C}`.
        An instance that maps AMBER types to their element can be found in
        `amber_dict`. If None is given, assume the particles already have
        element names.

    Returns
    -------
    str
        The input for the MINDO3 quantum chemical calculation run in pySCF.
    """
    box = snap.configuration.box[:3]
    unwrapped_pos = snap.particles.position + snap.particles.image * box

    # chromophore i is shifted into 0,0,0 image
    positions = [
        i + chromo_i.image * box for i in unwrapped_pos[chromo_i.atom_ids]
    ]
    # shift chromophore j's unwrapped positions
    positions += [i for i in unwrapped_pos[chromo_j.atom_ids] + j_shift]

    atom_ids = np.concatenate((chromo_i.atom_ids, chromo_j.atom_ids))
    typeids = snap.particles.typeid[atom_ids]
    if conversion_dict is not None:
        atoms = [
            conversion_dict[snap.particles.types[i]].symbol for i in typeids
        ]
    else:
        atoms = [
            ele.element_from_symbol(snap.particles.types[i]) for i in typeids
        ]

    # To determine where to add hydrogens, check the bonds that go to
    # particles outside of the ids provided
    for i, j in snap.bonds.group:
        if i in atom_ids and j not in atom_ids:
            # If bond is to chromophore j, additional shifting might be needed
            if i in chromo_j.atom_ids:
                shift = j_shift
            else:
                shift = chromo_i.image * box
            if conversion_dict is not None:
                element = conversion_dict[snap.particles.types[
                    snap.particles.typeid[j]]]
            else:
                element = ele.element_from_symbol(
                    snap.particles.types[snap.particles.typeid[j]])
            # If it's already a Hydrogen, just add it
            if element.atomic_number == 1:
                atoms.append(element.symbol)
                positions.append(unwrapped_pos[j] + shift)
            # If it's not a hydrogen, use the existing bond vector to
            # determine the direction and scale it to a more reasonable
            # length for C-H bond
            else:
                # Average sp3 C-H bond is 1.094 Angstrom
                v = unwrapped_pos[j] - unwrapped_pos[i]
                unit_vec = v / np.linalg.norm(v)
                new_pos = unit_vec * 1.094 + unwrapped_pos[i] + shift
                atoms.append("H")
                positions.append(new_pos)

        # Same as above but j->i instead of i->j
        elif j in atom_ids and i not in atom_ids:
            if j in chromo_j.atom_ids:
                shift = j_shift
            else:
                shift = chromo_i.image * box
            if conversion_dict is not None:
                element = conversion_dict[snap.particles.types[
                    snap.particles.typeid[i]]]
            else:
                element = ele.element_from_symbol(
                    snap.particles.types[snap.particles.typeid[i]])

            if element.atomic_number == 1:
                atoms.append(element.symbol)
                positions.append(unwrapped_pos[i] + shift)
            else:
                v = unwrapped_pos[i] - unwrapped_pos[j]
                unit_vec = v / np.linalg.norm(v)
                new_pos = unit_vec * 1.094 + unwrapped_pos[j] + shift
                atoms.append("H")
                positions.append(new_pos)

    # Shift center to origin
    positions = np.stack(positions)
    positions -= np.mean(positions, axis=0)

    qcc_input = " ".join(
        [f"{atom} {x} {y} {z};" for atom, (x, y, z) in zip(atoms, positions)])
    return qcc_input
Ejemplo n.º 11
0
def write_poscar(compound,
                 filename,
                 lattice_constant=1.0,
                 coord_style="cartesian"):
    """Write a VASP POSCAR file from a Compound.

    See //https://www.vasp.at formore information.

    Parameters
    ----------
    compound : mbuild.Compound
        The Compound to write to the POSCAR file
    filename : str
        Path of the output file
    lattice_constant : float
        Scaling constant for POSCAR file, used to scale all lattice vectors
        and atomic coordinates
        (default 1.0)
    coord_style : str
        Coordinate style of atom positions 'cartesian' or 'direct'
        (default 'cartesian')
    """
    try:
        atoms = [p.element.symbol for p in compound.particles()]
    except AttributeError:
        atoms = [
            element_from_symbol(p.name).symbol for p in compound.particles()
        ]

    # This automatically sorts element names alphabetically
    unique_atoms = np.unique(atoms)

    count_list = [str(atoms.count(i)) for i in unique_atoms]

    # This sorts the coordinates so they are in the same
    # order as the elements
    # mBuild (nm) --> (x10) --> VASP (angstroms)
    sorted_xyz = compound.xyz[np.argsort(atoms)] * 10

    try:
        lattice = compound.box.vectors
    except AttributeError:
        lattice = compound.get_boundingbox().vectors
        if coord_style == "direct":
            warnings.warn(
                "'direct' coord_style specified, but compound has no box "
                "-- using 'cartesian' instead")
            coord_style = "cartesian"

    if coord_style == "cartesian":
        sorted_xyz /= lattice_constant
    elif coord_style == "direct":
        sorted_xyz = sorted_xyz.dot(inv(lattice)) / lattice_constant
    else:
        raise ValueError("coord_style must be either 'cartesian' or 'direct'")

    with open(filename, "w") as f:
        f.write(filename + " - created by mBuild\n")
        f.write(f"\t{lattice_constant:.15f}\n")

        f.write("\t{0:.15f} {1:.15f} {2:.15f}\n".format(*lattice[0]))
        f.write("\t{0:.15f} {1:.15f} {2:.15f}\n".format(*lattice[1]))
        f.write("\t{0:.15f} {1:.15f} {2:.15f}\n".format(*lattice[2]))
        f.write("{}\n".format("\t".join(unique_atoms)))
        f.write("{}\n".format("\t".join(count_list)))
        f.write(f"{coord_style}\n")
        for row in sorted_xyz:
            f.write(f"{row[0]:.15f} {row[1]:.15f} {row[2]:.15f}\n")
Ejemplo n.º 12
0
def get_chromo_ids_smiles(snap, smarts_str, conversion_dict=None):
    """Get the atom indices in a snapshot associated with a SMARTS string.

    This function can be used to determine the atom indices for each
    chromophore. SMARTS matching depends on the molecular structures making
    chemical sense (e.g., aromatic structures are planar, etc). Often snapshots
    from molecular simulations based on classical methods (e.g., MC, MD) may
    have distortions that are chemically unphysical, in which case this function
    may not find all chromophores. A solution is to use this function on a
    snapshot of the initial frame of the trajectory, and then apply these
    indices to a later frame.

    Parameters
    ----------
    snap : gsd.hoomd.Snapshot
        Atomistic simulation snapshot from a GSD file. It is expected that the
        lengths in this file have been converted to Angstroms.
    smarts_str : str
        SMARTS string used to find the atom indices.
    conversion_dict : dictionary, default None
        A dictionary that maps the atom type to its element. e.g., `{'c3': C}`.
        An instance that maps AMBER types to their element can be found in
        `amber_dict`. If None is given, assume the particles already have
        element names.

    Returns
    -------
    list of numpy.ndarray of int
        atom indices of each SMARTS match

    Note
    ----
    If no matches are found, a warning is raised and the pybel.Molecule object
    is returned for debugging.
    """
    box = snap.configuration.box[:3]
    unwrapped_positions = snap.particles.position + snap.particles.image * box

    mol = openbabel.OBMol()
    for i, typeid in enumerate(snap.particles.typeid):
        a = mol.NewAtom()
        if conversion_dict is not None:
            element = conversion_dict[snap.particles.types[typeid]]
        else:
            element = ele.element_from_symbol(snap.particles.types[typeid])
        a.SetAtomicNum(element.atomic_number)
        a.SetVector(*[float(x) for x in unwrapped_positions[i]])

    for i, j in snap.bonds.group:
        # openbabel indexes atoms from 1
        # AddBond(i_index, j_index, bond_order)
        mol.AddBond(int(i + 1), int(j + 1), 1)

    # This will correctly set the bond order
    # (necessary for smarts matching)
    mol.PerceiveBondOrders()
    mol.SetAromaticPerceived()

    pybelmol = pybel.Molecule(mol)

    smarts = pybel.Smarts(smarts_str)
    # shift indices by 1
    atom_ids = [np.array(i) - 1 for i in smarts.findall(pybelmol)]
    if not atom_ids:
        warn(f"No matches found for smarts string {smarts_str}. " +
             "Please check the returned pybel.Molecule for errors.\n")
        return pybelmol
    print(f"Found {len(atom_ids)} chromophores.")
    return atom_ids
Ejemplo n.º 13
0
def read_xyz(filename, compound=None):
    """Read an XYZ file.

    The expected format is as follows:
    The first line contains the number of atoms in the file. The second line
    contains a comment, which is not read. Remaining lines, one for each
    atom in the file, include an elemental symbol followed by X, Y, and Z
    coordinates in Angstroms. Columns are expected tbe separated by
    whitespace. See https://openbabel.org/wiki/XYZ_(format).

    Parameters
    ----------
    filename : str
        Path of the input file

    Returns
    -------
    compound : Compound

    Notes
    -----
    The XYZ file format neglects many important details, including bonds,
    residues, and box information.

    There are some other flavors of the XYZ file format and not all are
    guaranteed to be compatible with this reader. For example, the TINKER
    XYZ format is not expected to be properly read.
    """
    if compound is None:
        compound = mb.Compound()

    guessed_elements = set()

    with open(filename, "r") as xyz_file:
        n_atoms = int(xyz_file.readline())
        xyz_file.readline()
        coords = np.zeros(shape=(n_atoms, 3), dtype=np.float64)
        for row, _ in enumerate(coords):
            line = xyz_file.readline().split()
            if not line:
                msg = (
                    "Incorrect number of lines in input file. Based on the "
                    "number in the first line of the file, {} rows of atoms "
                    "were expected, but at least one fewer was found.")
                raise MBuildError(msg.format(n_atoms))
            coords[row] = line[1:4]
            coords[row] *= 0.1
            name = line[0]
            try:
                element = name.capitalize()
                element = element_from_symbol(element)
            except ElementError:
                if name not in guessed_elements:
                    warn("No matching element found for {}; the particle will "
                         "be added to the compound without an element "
                         "attribute.".format(name))
                    guessed_elements.add(name)
                element = None

            particle = mb.Compound(pos=coords[row], name=name, element=element)
            compound.add(particle)

        # Verify we have read the last line by ensuring the next line in blank
        line = xyz_file.readline().split()
        if line:
            msg = ("Incorrect number of lines in input file. Based on the "
                   "number in the first line of the file, {} rows of atoms "
                   "were expected, but at least one more was found.")
            raise MBuildError(msg.format(n_atoms))

    return compound
Ejemplo n.º 14
0
def system_builder(seed,
                   chainlength=17,
                   backbone=Alkylsilane,
                   terminal_group='methyl',
                   num_chains=100):
    """ Define system variable"""
    chainlength = chainlength
    backbone = backbone
    seed = seed
    pattern_type = "random"
    terminal_group = terminal_group
    num_chains = num_chains
    """
    -----------------------------------
    Generate amorphous silica interface
    -----------------------------------
    """
    surface_a = SilicaInterface(thickness=1.2, seed=seed)
    surface_b = SilicaInterface(thickness=1.2, seed=seed)
    """
    ------------------------------------------------------
    Generate prototype of functionalized alkylsilane chain
    ------------------------------------------------------
    """
    chain_prototype_A = backbone(chain_length=chainlength,
                                 terminal_group=terminal_group)
    chain_prototype_B = backbone(chain_length=chainlength,
                                 terminal_group=terminal_group)
    """
    ----------------------------------------------------------
    Create monolayer on surface, backfilled with hydrogen caps
    ----------------------------------------------------------
    """
    # bottom monolayer is backfilled with the other terminal group
    # num_chains = num_chains * a_fraction
    monolayer_a = SurfaceMonolayer(
        surface=surface_a,
        chains=chain_prototype_A,
        n_chains=num_chains,
        seed=seed,
        backfill=H(),
        rotate=False,
    )
    monolayer_a.name = "Bottom"
    monolayer_b = SurfaceMonolayer(
        surface=surface_b,
        chains=chain_prototype_B,
        n_chains=num_chains,
        seed=seed,
        backfill=H(),
        rotate=False,
    )
    monolayer_b.name = "Top"
    """
    ----------------------
    Create dual monolayers
    ----------------------
    """
    dual_monolayer = DualSurface(bottom=monolayer_a,
                                 top=monolayer_b,
                                 separation=2.0)
    """
    --------------------------------------------------------
    Make sure box is elongated in z to be pseudo-2D periodic
    --------------------------------------------------------
    """
    box = mb.Box(lengths=[
        dual_monolayer.box.lengths[0], dual_monolayer.box.lengths[1],
        dual_monolayer.box.lengths[2] * 5.0
    ])
    dual_monolayer.box = box
    #dual_monolayer.periodicity += np.array([0, 0, 5.0 * box.lengths[2]])
    """
    -------------------------------------------------------------------
    - Save to .GRO, .TOP, and .LAMMPS formats
    - Atom-type the system using Foyer, with parameters from the OPLS
    force field obtained from GROMACS. Parameters are located in a
    Foyer XML file in  "../util/forcefield/oplsaa.xml".
    -------------------------------------------------------------------
    """

    if os.path.isfile("../util/forcefield/oplsaa.xml"):
        forcefield_filepath = "../util/forcefield/oplsaa.xml"
    elif os.path.isfile("../../../util/forcefield/oplsaa.xml"):
        forcefield_filepath = "../../../util/forcefield/oplsaa.xml"
    else:
        raise Exception('Forcefield file is not found')

    dual_monolayer.save("init.gro", residues=["Top", "Bottom"], overwrite=True)

    for particle in list(dual_monolayer.particles()):
        if 'Si' in particle.name[:2]:
            particle.element = element_from_symbol('Si')
        elif particle.name[0] == 'O':
            particle.element = element_from_symbol('O')
        else:
            particle.element = element_from_symbol(particle.name)
    structure = dual_monolayer.to_parmed(box=None, residues=["Top", "Bottom"])
    ff = Forcefield(forcefield_files=forcefield_filepath)
    structure = ff.apply(structure)
    structure.combining_rule = "geometric"

    structure.save("init.top", overwrite=True)
    write_lammpsdata(filename="init.lammps", structure=structure)
    """
    --------------------------------------
    Specify index groups and write to file
    --------------------------------------
    """
    index_groups = generate_index_groups(
        system=dual_monolayer,
        terminal_group=terminal_group,
        freeze_thickness=0.5,
    )
    write_monolayer_ndx(rigid_groups=index_groups, filename="init.ndx")
    return dual_monolayer
Ejemplo n.º 15
0
def read_poscar(filename, conversion=0.1):
    """Read a VASP POSCAR or CONTCAR file into a Compound.

    Parameters
    ----------
    filename : str
        Path to the POSCAR file
    conversion : float
        Conversion factor multiplied to coordinates when converting between
        VASP units (angstroms) and mbuild units (nm) (default = 0.1)

    Returns
    -------
    mbuild.Compound
    """
    comp = mb.Compound()

    with open(filename, "r") as f:
        data = f.readlines()

    title = data.pop(0)
    comp.name = title

    scale = float(data.pop(0).strip())

    a = np.fromiter(data.pop(0).split(), dtype="float64")
    b = np.fromiter(data.pop(0).split(), dtype="float64")
    c = np.fromiter(data.pop(0).split(), dtype="float64")

    lattice_vectors = np.stack((a, b, c))

    # POSCAR files do not require atom types to be specified
    # this block handles unspecified types
    line = data.pop(0).split()
    try:
        n_types = np.fromiter(line, dtype="int")
        types = ["_" + chr(i + 64) for i in range(1, len(n_types) + 1)]
        # if no types exist, assign placeholder types "_A", "_B", "_C", etc
    except ValueError:
        types = line
        n_types = np.fromiter(data.pop(0).split(), dtype="int")

    total_atoms = np.sum(n_types)
    all_types = list(
        chain.from_iterable([[itype] * n for itype, n in zip(types, n_types)]))

    # handle optional argument "Selective dynamics"
    # and required arguments "Cartesian" or "Direct"
    switch = data.pop(0)[0].upper()

    # If we ever want to do something with selective dynamics,
    # the following lines could be uncommented
    # selective_dynamics = False
    if switch == "S":
        # selective_dynamics = True
        switch = data.pop(0)[0].upper()

    if switch == "C":
        cartesian = True
    else:
        cartesian = False

    # Slice is necessary to handle files using selective dynamics
    coords = np.stack([
        np.fromiter(line.split()[:3], dtype="float64")
        for line in data[:total_atoms]
    ])

    if cartesian:
        coords = coords * scale
    else:
        coords = coords.dot(lattice_vectors) * scale

    comp.box = mb.Box.from_vectors(lattice_vectors)

    for i, xyz in enumerate(coords):
        comp.add(
            mb.Particle(
                name=all_types[i],
                element=element_from_symbol(all_types[i]),
                pos=xyz * conversion,
            ))

    return comp
Ejemplo n.º 16
0
            j_shift = centers[imin] - chromo_j.unwrapped_center
            chromo_i.neighbors.append([j, rel_image])
            chromo_i.neighbors_delta_e.append(None)
            chromo_i.neighbors_ti.append(None)
            chromo_j.neighbors.append([i, -rel_image])
            chromo_j.neighbors_delta_e.append(None)
            chromo_j.neighbors_ti.append(None)
            neighbors.append((i, j))
            qcc_input = eqcc.write_qcc_pair_input(snap, chromo_i, chromo_j,
                                                  j_shift, conversion_dict)
            qcc_pairs.append(((i, j), qcc_input))
    return qcc_pairs


conversion_dict = {
    "S1": ele.element_from_symbol("S"),
    "H1": ele.element_from_symbol("H"),
    "C5": ele.element_from_symbol("C"),
    "C1": ele.element_from_symbol("C"),
    "C4": ele.element_from_symbol("C"),
    "C6": ele.element_from_symbol("C"),
    "C8": ele.element_from_symbol("C"),
    "C9": ele.element_from_symbol("C"),
    "C3": ele.element_from_symbol("C"),
    "C7": ele.element_from_symbol("C"),
    "C2": ele.element_from_symbol("C"),
    "C10": ele.element_from_symbol("C"),
}

amber_dict = {
    "H": ele.element_from_symbol("H"),
Ejemplo n.º 17
0
def md_files(instance):
    molecules = instance.molecules
    functional = instance.functional
    box = instance.box
    cutoff = instance.cutoff
    scf_tolerance = instance.scf_tolerance
    basis_set = instance.basis_set
    basis_set_filename = instance.basis_set_filename
    potential_filename = instance.potential_filename
    fixed_list = instance.fixed_list
    periodicity = instance.periodicity
    simulation_time = instance.simulation_time
    time_step = instance.time_step
    ensemble = instance.ensemble
    project_name = instance.project_name
    temperature = instance.temperature
    pressure = instance.pressure
    n_molecules = instance.n_molecules
    thermostat = instance.thermostat
    traj_type = instance.traj_type
    traj_freq = instance.traj_freq
    seed = instance.seed
    use_atom_name_as_symbol = self.use_atom_name_as_symbol
    input_filename = instance.input_filename
    output_filename = instance.output_filename

    initial_coordinate_filename = instance.initial_coordinate_filename

    if initial_coordinate_filename is None:

        filled_box = mb.packing.fill_box(compound=molecules,
                                         n_compounds=n_molecules,
                                         box=box)
        initial_coord_file = project_name + ".xyz"
        filled_box.save(initial_coord_file, overwrite="True")
        with open(initial_coord_file, "r") as fin:
            data = fin.read().splitlines(True)
        with open(initial_coord_file, "w") as fout:
            fout.writelines(data[2:])  # deleting first two lines
    else:
        filled_box = mb.load(initial_coordinate_filename)
        initial_coord_file = project_name + ".xyz"
        filled_box.save(initial_coord_file, overwrite="True")
        with open(initial_coord_file, "r") as fin:
            data = fin.read().splitlines(True)
        with open(initial_coord_file, "w") as fout:
            fout.writelines(data[2:])  # deleting first two lines
    print("MD initial structure saved as {}".format(initial_coord_file))

    atom_list = []
    mass_list = []
    symbol_list = []
    for i in range(len(molecules)):
        current_molecule = mb.clone(molecules[i])
        for particle in current_molecule.particles():
            atom_list.append(particle.name)
            if not (use_atom_name_as_symbol):
                symbol_list.append(particle.element)
            else:
                symbol_list.append(particle.name)
            if use_atom_name_as_symbol:
                mass_list.append(
                    ele.element_from_symbol("{}".format(particle.name)).mass)
            else:
                mass_list.append(
                    ele.element_from_symbol("{}".format(
                        particle.element)).mass)

    unique_atom_list, indices = np.unique(atom_list, return_index=True)
    unique_atom_list = list(unique_atom_list)

    unique_symbol_list = [symbol_list[i] for i in indices]
    num_atoms = len(atom_list)
    num_unique_atoms = len(unique_atom_list)

    mySim = sim.SIM()
    # setting defaults

    n_steps = int(simulation_time / time_step)
    mySim.GLOBAL.RUN_TYPE = "MD"
    mySim.GLOBAL.PROJECT_NAME = project_name
    mySim.GLOBAL.PRINT_LEVEL = "LOW"
    mySim.GLOBAL.SEED = seed

    # FORCE EVAL SECTION
    mySim.FORCE_EVAL.METHOD = "QUICKSTEP"
    mySim.FORCE_EVAL.STRESS_TENSOR = "ANALYTICAL"

    mySim.FORCE_EVAL.DFT.BASIS_SET_FILE_NAME = basis_set_filename
    mySim.FORCE_EVAL.DFT.POTENTIAL_FILE_NAME = potential_filename
    mySim.FORCE_EVAL.DFT.CHARGE = 0
    mySim.FORCE_EVAL.DFT.MULTIPLICITY = 1
    mySim.FORCE_EVAL.DFT.MGRID.CUTOFF = cutoff
    mySim.FORCE_EVAL.DFT.MGRID.REL_CUTOFF = 50
    mySim.FORCE_EVAL.DFT.MGRID.NGRIDS = 4
    mySim.FORCE_EVAL.DFT.QS.METHOD = "GPW"
    mySim.FORCE_EVAL.DFT.QS.EPS_DEFAULT = 1e-8
    mySim.FORCE_EVAL.DFT.QS.EXTRAPOLATION = "ASPC"
    mySim.FORCE_EVAL.DFT.POISSON.PERIODIC = periodicity
    mySim.FORCE_EVAL.DFT.PRINT.E_DENSITY_CUBE.SECTION_PARAMETERS = "OFF"
    mySim.FORCE_EVAL.DFT.SCF.SCF_GUESS = "ATOMIC"
    mySim.FORCE_EVAL.DFT.SCF.MAX_SCF = 30
    mySim.FORCE_EVAL.DFT.SCF.EPS_SCF = scf_tolerance

    mySim.FORCE_EVAL.DFT.SCF.OT.SECTION_PARAMETERS = ".TRUE."
    mySim.FORCE_EVAL.DFT.SCF.OT.PRECONDITIONER = "FULL_SINGLE_INVERSE"
    mySim.FORCE_EVAL.DFT.SCF.OT.MINIMIZER = "DIIS"
    mySim.FORCE_EVAL.DFT.SCF.OUTER_SCF.SECTION_PARAMETERS = ".TRUE."

    mySim.FORCE_EVAL.DFT.SCF.OUTER_SCF.MAX_SCF = 10
    mySim.FORCE_EVAL.DFT.SCF.OUTER_SCF.EPS_SCF = 1e-6
    mySim.FORCE_EVAL.DFT.SCF.PRINT.RESTART.SECTION_PARAMETERS = "OFF"
    mySim.FORCE_EVAL.DFT.SCF.PRINT.DM_RESTART_WRITE = ".TRUE."

    mySim.FORCE_EVAL.DFT.XC.XC_FUNCTIONAL.SECTION_PARAMETERS = functional
    mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.POTENTIAL_TYPE = "PAIR_POTENTIAL"
    mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.PAIR_POTENTIAL.TYPE = "DFTD3"
    mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.PAIR_POTENTIAL.PARAMETER_FILE_NAME = (
        "dftd3.dat")
    mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.PAIR_POTENTIAL.REFERENCE_FUNCTIONAL = (
        functional)
    mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.PAIR_POTENTIAL.R_CUTOFF = 8

    mySim.FORCE_EVAL.SUBSYS.COORD.DEFAULT_KEYWORD = project_name + ".xyz"
    mySim.FORCE_EVAL.SUBSYS.init_atoms(num_atoms)

    for i in range(num_unique_atoms):
        mySim.FORCE_EVAL.SUBSYS.KIND[
            i + 1].SECTION_PARAMETERS = unique_atom_list[i]
        if basis_set == [None]:

            mySim.FORCE_EVAL.SUBSYS.KIND[i + 1].BASIS_SET = basis_set_setter(
                unique_atom_list[i])
        else:
            mySim.FORCE_EVAL.SUBSYS.KIND[i + 1].BASIS_SET = basis_set[
                unique_atom_list[i]]
        if not (use_atom_name_as_symbol):
            mySim.FORCE_EVAL.SUBSYS.KIND[i + 1].ELEMENT = unique_symbol_list[i]

        mySim.FORCE_EVAL.SUBSYS.KIND[i + 1].POTENTIAL = potential(
            unique_atom_list[i], functional)

    mySim.FORCE_EVAL.SUBSYS.CELL.ABC = "{a} {b} {c}".format(
        a=10 * box.lengths[0], b=10 * box.lengths[1], c=10 * box.lengths[2])
    mySim.FORCE_EVAL.SUBSYS.CELL.ALPHA_BETA_GAMMA = "{a} {b} {c}".format(
        a=box.angles[0], b=box.angles[1], c=box.angles[2])

    # MOTION SECTION
    mySim.MOTION.GEO_OPT.OPTIMIZER = "BFGS"
    mySim.MOTION.GEO_OPT.MAX_ITER = 100
    mySim.MOTION.GEO_OPT.MAX_DR = 0.003

    mySim.MOTION.MD.ENSEMBLE = ensemble
    mySim.MOTION.MD.STEPS = n_steps
    mySim.MOTION.MD.TIMESTEP = time_step

    if temperature_ensemble(ensemble):
        mySim.MOTION.MD.TEMPERATURE = temperature
        mySim.MOTION.MD.THERMOSTAT.TYPE = thermostat

        mySim.MOTION.MD.THERMOSTAT.REGION = "MASSIVE"
        mySim.MOTION.MD.THERMOSTAT.NOSE.LENGTH = 5
        mySim.MOTION.MD.THERMOSTAT.NOSE.YOSHIDA = 3
        mySim.MOTION.MD.THERMOSTAT.NOSE.TIMECON = 1000.0
        mySim.MOTION.MD.THERMOSTAT.NOSE.MTS = 2

    if pressure_ensemble(ensemble):

        mySim.MOTION.MD.BAROSTAT.PRESSURE = pressure
        if pressure == None:
            print("you need to define pressure")
    if fixed_list is not None:

        mySim.MOTION.CONSTRAINT.FIXED_ATOMS.LIST = fixed_list
        mySim.MOTION.MD.THERMOSTAT.REGION = "GLOBAL"

    if periodicity == "NONE":
        mySim.FORCE_EVAL.DFT.POISSON.PERIODIC = "NONE"
        mySim.FORCE_EVAL.DFT.POISSON.POISSON_SOLVER = "WAVELET"
    if not is_cubic(box) and periodicity == "NONE":
        print(
            "The box should be cubic for non-periodic calculations and the box must be around 15 times the size of the molecule"
        )

    mySim.MOTION.PRINT.STRESS.SECTION_PARAMETERS = "OFF"
    mySim.MOTION.PRINT.TRAJECTORY.EACH.MD = traj_freq
    mySim.MOTION.PRINT.TRAJECTORY.FORMAT = traj_type
    mySim.MOTION.PRINT.VELOCITIES.SECTION_PARAMETERS = "OFF"
    mySim.MOTION.PRINT.FORCES.SECTION_PARAMETERS = "OFF"
    mySim.MOTION.PRINT.RESTART_HISTORY.SECTION_PARAMETERS = "ON"
    mySim.MOTION.PRINT.RESTART_HISTORY.EACH.MD = 500
    mySim.MOTION.PRINT.RESTART.SECTION_PARAMETERS = "ON"
    mySim.MOTION.PRINT.RESTART.BACKUP_COPIES = 3

    mySim.MOTION.PRINT.RESTART.EACH.MD = 1

    mySim.write_changeLog(fn="md-changeLog.out")
    mySim.write_errorLog()
    mySim.write_inputFile(fn=input_filename)
    print("MD input file saved as {}".format(input_filename))
Ejemplo n.º 18
0
def calculate_charges(smiles, resp_type, overwrite=False):
    """Run the charge calculation for the molecule defined by smiles

    Parameters
    ----------
    smiles: str
        the smiles string of the desired molecule
    resp_type: str
        the type of RESP to perform (RESP1 or RESP2)
    overwrite: boolean, optional, default=False
        overwrite the previous results if they exist
    Returns
    -------

    Raises
    ------
    InvalidMoleculeError
        if the smiles string is invalid
    """
    smiles = canonicalize_smiles(smiles)
    iupac_name = smiles_to_iupac(smiles)
    mol_path = Path.joinpath(LIB_PATH, iupac_name)
    if not mol_path.is_dir():
        raise RESPLibraryError(
            f"The directory for the molecule: '{smiles}' with name "
            f"{iupac_name} does not exist. Please run "
            "resp_library.prepare_charge_calculation() first.")

    # Initialize RESP stuff
    resp_type = resp_type.upper()
    resp_path = Path.joinpath(mol_path, resp_type)
    if not overwrite:
        if Path.joinpath(resp_path, "results").is_dir():
            raise RESPLibraryError(
                "It appears that this partial charge calculation "
                "has already been attempted or performed. If you wish "
                "to re-run this calculation, please remove the 'results', "
                "'structures', and 'esp_grids' directories before proceeding.")
    inp, log = _initialize_resp(resp_path)

    # Molecule definition
    assert inp['smiles'] == smiles
    assert inp['resp']['type'].upper() == resp_type
    # Parse the YAML file
    n_conformers = inp['conformer_generation']['n_conformers']
    rms_threshold = inp['conformer_generation']['rms_threshold']
    energy_threshold = inp['conformer_generation'][
        'energy_threshold']  # kJ/mol
    conformer_seed = inp['conformer_generation']['random_seed']
    charge_constraints = inp['resp']['charge_constraints']
    equality_constraints = inp['resp']['equality_constraints']
    point_density = inp['resp']['point_density']
    vdw_scale_factors = inp['resp']['vdw_scale_factors']

    if resp_type == "RESP1":
        esp_method = "hf"
        esp_basis_set = "6-31g*"
    elif resp_type == "RESP2":
        esp_method = "pw6b95"
        esp_basis_set = "aug-cc-pV(D+d)Z"
    else:
        raise RESPLibraryError(
            "Invalid RESP type. Only 'RESP1' and 'RESP2' are supported.")

    # Create the molecule, add H's
    rdmol = Chem.MolFromSmiles(smiles)
    rdmol = Chem.AddHs(rdmol)

    # Get the net charge
    net_charge = Chem.rdmolops.GetFormalCharge(rdmol)
    log.log(f"The net charge is {net_charge}.")

    # Get the elements
    elements = [a.GetSymbol() for a in rdmol.GetAtoms()]
    vdw_radii = {
        elem_sym: ele.element_from_symbol(elem_sym).radius_bondi
        for elem_sym in elements
    }

    # Generate conformers
    cids = AllChem.EmbedMultipleConfs(rdmol,
                                      numConfs=500,
                                      pruneRmsThresh=rms_threshold,
                                      randomSeed=conformer_seed)
    AllChem.AlignMolConformers(rdmol)
    if len(cids) < n_conformers:
        raise ValueError(
            "Not enough conformers found. Please reduce the "
            "'rms_threshold' or the 'n_conformers'. For molecules "
            "with < 5 atoms it may be difficult to generate more "
            "than 2 conformers.")

    # Select n_conformers at random
    np.random.seed(conformer_seed)
    conformer_ids = np.random.choice([i for i in range(len(cids))],
                                     size=n_conformers,
                                     replace=False)
    remove = [i for i in range(len(cids)) if i not in conformer_ids]
    for idx in remove:
        rdmol.RemoveConformer(idx)
    # Renumber conformers
    for idx, c in enumerate(rdmol.GetConformers()):
        c.SetId(idx)

    optimized_p4molecules = []
    optimized_energies = []

    # For each conformer, geometry optimize with psi4
    for conformer in rdmol.GetConformers():
        p4mol = build_p4mol(elements, conformer.GetPositions(), net_charge)
        p4mol, energy = _geometry_optimize(p4mol, resp_type)
        # Save optimized structure and energy
        optimized_p4molecules.append(p4mol)
        optimized_energies.append(energy)

        # Extract optimized coordinates; update coordinates RDKIT molecule
        coords = p4mol.geometry().to_array() * BOHR_TO_ANGSTROM
        for i in range(rdmol.GetNumAtoms()):
            x, y, z = coords[i]
            conformer.SetAtomPosition(i, Point3D(x, y, z))

    # Check energies and remove high energy conformers
    _check_relative_conformer_energies(rdmol, optimized_p4molecules,
                                       optimized_energies, energy_threshold,
                                       log)

    # Align conformers for easier visual comparison
    _save_aligned_conformers(rdmol, log)

    # Save the conformers used for resp
    Path("structures/optimized_geometries.pdb").write_text(
        Chem.rdmolfiles.MolToPDBBlock(rdmol))
    log.log(
        "Wrote the final optimized gemoetries to 'optimized_geometries.pdb'.\n\n"
    )

    # Finally we do multi-conformer RESP
    pcm = False
    charges = _perform_resp(
        optimized_p4molecules,
        charge_constraints,
        equality_constraints,
        esp_method,
        esp_basis_set,
        pcm,
        point_density,
        vdw_radii,
        vdw_scale_factors,
        log,
    )
    _write_results(elements, charges, equality_constraints, "vacuum", log)

    if resp_type == "RESP2":
        pcm = True
        charges = _perform_resp(
            optimized_p4molecules,
            charge_constraints,
            equality_constraints,
            esp_method,
            esp_basis_set,
            pcm,
            point_density,
            vdw_radii,
            vdw_scale_factors,
            log,
        )
        _write_results(elements, charges, equality_constraints, "pcm", log)

    log.close()
Ejemplo n.º 19
0
def single_molecule_opt_files(instance):
    molecule = instance.molecule
    functional = instance.functional
    box = instance.box
    cutoff = instance.cutoff
    scf_tolerance = instance.scf_tolerance
    basis_set = instance.basis_set
    basis_set_filename = instance.basis_set_filename
    potential_filename = instance.potential_filename
    fixed_list = instance.fixed_list
    periodicity = instance.periodicity
    n_iter = instance.n_iter
    use_atom_name_as_symbol = self.use_atom_name_as_symbol
    input_filename = instance.input_filename
    output_filename = instance.output_filename

    name = molecule.name
    filled_box = mb.packing.fill_box(molecule, 1, box)
    mol_unopt_coord = name + "_unoptimized_coord.xyz"
    # opt_inp_file=name+'_optimization_input.inp'
    filled_box.save(mol_unopt_coord, overwrite="True")
    with open(mol_unopt_coord, "r") as fin:
        data = fin.read().splitlines(True)
    with open(mol_unopt_coord, "w") as fout:
        fout.writelines(data[2:])  # deleting first two lines
    print("Initial structure saved as {}".format(mol_unopt_coord))

    molecules = [molecule]
    atom_list = []
    mass_list = []
    symbol_list = []
    for i in range(len(molecules)):
        current_molecule = mb.clone(molecules[i])
        for particle in current_molecule.particles():
            atom_list.append(particle.name)
            if not (use_atom_name_as_symbol):
                symbol_list.append(particle.element)
            else:
                symbol_list.append(particle.name)
            if use_atom_name_as_symbol:
                mass_list.append(
                    ele.element_from_symbol("{}".format(particle.name)).mass)
            else:
                mass_list.append(
                    ele.element_from_symbol("{}".format(
                        particle.element)).mass)

    unique_atom_list, indices = np.unique(atom_list, return_index=True)
    unique_atom_list = list(unique_atom_list)

    unique_symbol_list = [symbol_list[i] for i in indices]
    num_atoms = len(atom_list)
    num_unique_atoms = len(unique_atom_list)

    mySim = sim.SIM()
    # setting defaults

    mySim.GLOBAL.RUN_TYPE = "GEO_OPT"
    mySim.GLOBAL.PROJECT_NAME = name + "_opt"
    mySim.GLOBAL.PRINT_LEVEL = "LOW"
    # FORCE EVAL SECTION
    mySim.FORCE_EVAL.METHOD = "QUICKSTEP"
    mySim.FORCE_EVAL.SUBSYS.CELL.ABC = "{a} {b} {c}".format(
        a=10 * box.lengths[0], b=10 * box.lengths[1], c=10 * box.lengths[2])
    mySim.FORCE_EVAL.SUBSYS.CELL.ALPHA_BETA_GAMMA = "{a} {b} {c}".format(
        a=box.angles[0], b=box.angles[1], c=box.angles[2])
    mySim.FORCE_EVAL.SUBSYS.CELL.PERIODIC = periodicity
    mySim.FORCE_EVAL.SUBSYS.COORD.DEFAULT_KEYWORD = mol_unopt_coord
    mySim.FORCE_EVAL.SUBSYS.init_atoms(num_atoms)

    for i in range(num_unique_atoms):
        mySim.FORCE_EVAL.SUBSYS.KIND[
            i + 1].SECTION_PARAMETERS = unique_atom_list[i]

        if basis_set == [None]:

            mySim.FORCE_EVAL.SUBSYS.KIND[i + 1].BASIS_SET = basis_set_setter(
                unique_atom_list[i])
        else:
            mySim.FORCE_EVAL.SUBSYS.KIND[i + 1].BASIS_SET = basis_set[
                unique_atom_list[i]]
        if not (use_atom_name_as_symbol):
            mySim.FORCE_EVAL.SUBSYS.KIND[i + 1].ELEMENT = unique_symbol_list[i]

        mySim.FORCE_EVAL.SUBSYS.KIND[i + 1].POTENTIAL = potential(
            unique_atom_list[i], functional)

    mySim.FORCE_EVAL.DFT.BASIS_SET_FILE_NAME = basis_set_filename
    mySim.FORCE_EVAL.DFT.POTENTIAL_FILE_NAME = potential_filename
    mySim.FORCE_EVAL.DFT.QS.EPS_DEFAULT = 1e-7

    mySim.FORCE_EVAL.DFT.MGRID.CUTOFF = cutoff
    mySim.FORCE_EVAL.DFT.MGRID.REL_CUTOFF = 50
    mySim.FORCE_EVAL.DFT.MGRID.NGRIDS = 4

    mySim.FORCE_EVAL.DFT.XC.XC_FUNCTIONAL.SECTION_PARAMETERS = functional
    mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.POTENTIAL_TYPE = "PAIR_POTENTIAL"
    mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.PAIR_POTENTIAL.TYPE = "DFTD3"
    mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.PAIR_POTENTIAL.PARAMETER_FILE_NAME = (
        "dftd3.dat")
    mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.PAIR_POTENTIAL.REFERENCE_FUNCTIONAL = (
        functional)
    mySim.FORCE_EVAL.DFT.XC.VDW_POTENTIAL.PAIR_POTENTIAL.R_CUTOFF = 8

    mySim.FORCE_EVAL.DFT.SCF.SCF_GUESS = "ATOMIC"
    mySim.FORCE_EVAL.DFT.SCF.MAX_SCF = 30
    mySim.FORCE_EVAL.DFT.SCF.EPS_SCF = scf_tolerance
    if periodicity == "NONE":
        mySim.FORCE_EVAL.DFT.POISSON.PERIODIC = "NONE"
        mySim.FORCE_EVAL.DFT.POISSON.POISSON_SOLVER = "WAVELET"
        print(
            "The box should be cubic for non-periodic calculations and the box must be around 15 times the size of the molecule when periodicity is NONE"
        )
    if not is_cubic(box) and periodicity == "NONE":
        print(
            "The box should be cubic for non-periodic calculations and the box must be around 15 times the size of the molecule"
        )

    mySim.MOTION.GEO_OPT.TYPE = "MINIMIZATION"
    mySim.MOTION.GEO_OPT.OPTIMIZER = "BFGS"
    mySim.MOTION.GEO_OPT.MAX_ITER = n_iter
    mySim.MOTION.GEO_OPT.MAX_DR = 1e-3

    mySim.MOTION.CONSTRAINT.FIXED_ATOMS.LIST = fixed_list
    mySim.write_changeLog(fn="mol_opt-changeLog.out")
    mySim.write_errorLog()
    mySim.write_inputFile(fn=input_filename)
    print("Molecule optimization file saved as {}".format(input_filename))
Ejemplo n.º 20
0
def write_qcc_inp(snap, atom_ids, conversion_dict=None):
    """Write a quantum chemical input string.

    Input string for pySCF containing elements and positions in Angstroms
    (e.g., "C 0.0 0.0 0.0; H 1.54 0.0 0.0")
    See https://pyscf.org/quickstart.html for more information.

    Parameters
    ----------
    snap : gsd.hoomd.Snapshot
        Atomistic simulation snapshot from a GSD file. It is expected that the
        lengths in this file have been converted to Angstroms.
    atom_ids : numpy.ndarray of int
        Snapshot indices of the particles to include in the input string.
    conversion_dict : dictionary, default None
        A dictionary that maps the atom type to its element. e.g., `{'c3': C}`.
        An instance that maps AMBER types to their element can be found in
        `amber_dict`. If None is given, assume the particles already have
        element names.

    Returns
    -------
    str
        The input for the MINDO3 quantum chemical calculation run in pySCF.
    """
    atoms = []
    positions = []

    box = snap.configuration.box[:3]
    unwrapped_pos = snap.particles.position + snap.particles.image * box

    for i in atom_ids:
        if conversion_dict is not None:
            element = conversion_dict[snap.particles.types[
                snap.particles.typeid[i]]]
        else:
            element = ele.element_from_symbol(
                snap.particles.types[snap.particles.typeid[i]])
        atoms.append(element.symbol)
        positions.append(unwrapped_pos[i])

    # To determine where to add hydrogens, check the bonds that go to
    # particles outside of the ids provided
    for i, j in snap.bonds.group:
        if i in atom_ids and j not in atom_ids:
            if conversion_dict is not None:
                element = conversion_dict[snap.particles.types[
                    snap.particles.typeid[j]]]
            else:
                element = ele.element_from_symbol(
                    snap.particles.types[snap.particles.typeid[j]])
            # If it's already a Hydrogen, just add it
            if element.atomic_number == 1:
                atoms.append(element.symbol)
                positions.append(unwrapped_pos[j])
            # If it's not a hydrogen, use the existing bond vector to
            # determine the direction and scale it to a more reasonable
            # length for C-H bond
            else:
                # Average sp3 C-H bond is 1.094 Angstrom
                v = unwrapped_pos[j] - unwrapped_pos[i]
                unit_vec = v / np.linalg.norm(v)
                new_pos = unit_vec * 1.094 + unwrapped_pos[i]
                atoms.append("H")
                positions.append(new_pos)

        # Same as above but j->i instead of i->j
        elif j in atom_ids and i not in atom_ids:
            if conversion_dict is not None:
                element = conversion_dict[snap.particles.types[
                    snap.particles.typeid[i]]]
            else:
                element = ele.element_from_symbol(
                    snap.particles.types[snap.particles.typeid[i]])
            if element.atomic_number == 1:
                atoms.append(element.symbol)
                positions.append(unwrapped_pos[i])

            else:
                v = unwrapped_pos[i] - unwrapped_pos[j]
                unit_vec = v / np.linalg.norm(v)
                new_pos = unit_vec * 1.094 + unwrapped_pos[j]
                atoms.append("H")
                positions.append(new_pos)

    # Shift center to origin
    positions = np.stack(positions)
    positions -= np.mean(positions, axis=0)
    qcc_input = " ".join(
        [f"{atom} {x} {y} {z};" for atom, (x, y, z) in zip(atoms, positions)])
    return qcc_input
Ejemplo n.º 21
0
def find_atomtypes(structure, forcefield, max_iter=10):
    """Determine atomtypes for all atoms.

    Parameters
    ----------
    structure : parmed.Structure, or gmso.Topology, or TopologyGraph
        The topology that we are trying to atomtype. If a parmed.Structure or
        gmso.Topology is provided, it will be convert to a TopologyGraph before
        atomtyping.
    forcefield : AtomTypingRulesProvider, foyer.ForceField, foyer.general_forcefield.Forcefield
        The atomtyping rules provider object/foyer forcefield.
    max_iter : int, optional, default=10
        The maximum number of iterations.
    """
    # ToDo: This function eventually should be refactored into chunks
    #  for a less painful conversion process

    from foyer.forcefield import Forcefield
    from foyer.general_forcefield import Forcefield as GeneralForcefield

    topology_graph = structure

    if isinstance(structure, Structure):
        topology_graph = TopologyGraph.from_parmed(structure)
    elif isinstance(structure, Topology):
        topology_graph = TopologyGraph.from_gmso_topology(structure)

    if isinstance(forcefield, (Forcefield, GeneralForcefield)):
        forcefield = AtomTypingRulesProvider.from_foyer_forcefield(forcefield)

    typemap = {
        atom_index: {
            "whitelist": set(),
            "blacklist": set(),
            "atomtype": None
        }
        for atom_index in topology_graph.atoms(data=False)
    }

    rules = _load_rules(forcefield, typemap)

    # Only consider rules for elements found in topology
    subrules = dict()

    system_elements = set()
    for _, atom_data in topology_graph.atoms(data=True):
        # First add non-element types, which are strings, then elements
        name = atom_data.name
        if name.startswith("_"):
            if name in forcefield.non_element_types:
                system_elements.add(name)
        else:
            atomic_number = atom_data.atomic_number
            atomic_symbol = atom_data.element
            try:
                element_from_num = ele.element_from_atomic_number(
                    atomic_number).symbol
                element_from_sym = ele.element_from_symbol(
                    atomic_symbol).symbol
                assert element_from_num == element_from_sym
                system_elements.add(element_from_num)
            except ElementError:
                raise FoyerError("Parsed atom {} as having neither an element "
                                 "nor non-element type.".format(name))
            except AssertionError:
                raise FoyerError(
                    f"Parsed atom {name} has mismatching atom number ({atomic_number}) "
                    f"and atom symbol ({atomic_symbol}).")

    for key, val in rules.items():
        atom = val.nodes[0]["atom"]
        if len(list(atom.find_data("atom_symbol"))) == 1 and not list(
                atom.find_data("not_expression")):
            try:
                element = next(atom.find_data("atom_symbol")).children[0]
            except IndexError:
                try:
                    atomic_num = next(atom.find_data("atomic_num")).children[0]
                    element = ele.element_from_atomic_number(atomic_num).symbol
                except IndexError:
                    element = None
        else:
            element = None
        if element is None or element in system_elements:
            subrules[key] = val
    rules = subrules

    _iterate_rules(rules, topology_graph, typemap, max_iter=max_iter)
    _resolve_atomtypes(topology_graph, typemap)

    return typemap