コード例 #1
0
ファイル: packing.py プロジェクト: ctk3b/mbuild
def fill_box(compound, n_compounds, box, overlap=0.2, seed=12345):
    """Fill a box with a compound using packmol.

    Parameters
    ----------
    compound : mb.Compound or list of mb.Compound
    n_compounds : int or list of int
    box : mb.Box
    overlap : float

    Returns
    -------
    filled : mb.Compound

    """
    if not PACKMOL:
        msg = "Packmol not found."
        if sys.platform.startswith("win"):
            msg = (msg + " If packmol is already installed, make sure that the "
                         "packmol.exe is on the path.")
        raise IOError(msg)

    box = _validate_box(box)
    if not isinstance(compound, (list, set)):
        compound = [compound]
    if not isinstance(n_compounds, (list, set)):
        n_compounds = [n_compounds]

    # In angstroms for packmol.
    box_mins = box.mins * 10
    box_maxs = box.maxs * 10
    overlap *= 10

    # Build the input file for each compound and call packmol.
    filled_pdb = tempfile.mkstemp(suffix='.pdb')[1]
    input_text = PACKMOL_HEADER.format(overlap, filled_pdb, seed)

    for comp, m_compounds in zip(compound, n_compounds):
        m_compounds = int(m_compounds)
        compound_pdb = tempfile.mkstemp(suffix='.pdb')[1]
        comp.save(compound_pdb, overwrite=True)
        input_text += PACKMOL_BOX.format(compound_pdb, m_compounds,
                           box_mins[0], box_mins[1], box_mins[2],
                           box_maxs[0], box_maxs[1], box_maxs[2])

    proc = Popen(PACKMOL, stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True)
    out, err = proc.communicate(input=input_text)
    if err:
        _packmol_error(out, err)

    # Create the topology and update the coordinates.
    filled = Compound()
    for comp, m_compounds in zip(compound, n_compounds):
        for _ in range(m_compounds):
            filled.add(clone(comp))
    filled.update_coordinates(filled_pdb)
    return filled
コード例 #2
0
ファイル: packing.py プロジェクト: TengyuMaVandy/mbuild
def fill_box(compound, n_compounds, box, overlap=0.2, seed=12345):
    """Fill a box with a compound using packmol.

    Parameters
    ----------
    compound : mb.Compound
    n_compounds : int
    box : mb.Box
    overlap : float

    Returns
    -------
    filled : mb.Compound

    """
    if not PACKMOL:
        msg = "Packmol not found."
        if sys.platform.startswith("win"):
            msg = (msg + " If packmol is already installed, make sure that the "
                         "packmol.exe is on the path.")
        raise IOError(msg)

    if isinstance(box, (list, tuple)):
        box = Box(lengths=box)

    n_compounds = int(n_compounds)
    compound_pdb = tempfile.mkstemp(suffix='.pdb')[1]
    compound.save(compound_pdb, overwrite=True)
    filled_pdb = tempfile.mkstemp(suffix='.pdb')[1]

    # In angstroms for packmol.
    box_lengths = box.lengths * 10
    overlap *= 10

    # Build the input file and call packmol.
    input_text = (PACKMOL_HEADER.format(overlap, filled_pdb, seed) +
                  PACKMOL_BOX.format(compound_pdb, n_compounds, *box_lengths))


    proc = Popen(PACKMOL, stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True)
    out, err = proc.communicate(input=input_text)
    if err:
        _packmol_error(out, err)

    # Create the topology and update the coordinates.
    filled = Compound()
    for _ in range(n_compounds):
        filled.add(clone(compound))
    filled.update_coordinates(filled_pdb)
    return filled
コード例 #3
0
ファイル: packing.py プロジェクト: ctk3b/mbuild
def solvate(solute, solvent, n_solvent, box, overlap=0.2, seed=12345):
    """Solvate a compound in a box of solvent using packmol.

    Parameters
    ----------
    solute : mb.Compound
    solvent : mb.Compound
    n_solvent : int
    box : mb.Box
    overlap : float

    Returns
    -------
    solvated : mb.Compound

    """
    if not PACKMOL:
        raise IOError("Packmol not found")

    box = _validate_box(box)
    if not isinstance(solvent, (list, set)):
        solvent = [solvent]
    if not isinstance(n_solvent, (list, set)):
        n_solvent = [n_solvent]

    # In angstroms for packmol.
    box_mins = box.mins * 10
    box_maxs = box.maxs * 10
    overlap *= 10
    center_solute = (box_maxs + box_mins) / 2

    # Build the input file for each compound and call packmol.
    solvated_pdb = tempfile.mkstemp(suffix='.pdb')[1]
    solute_pdb = tempfile.mkstemp(suffix='.pdb')[1]
    solute.save(solute_pdb, overwrite=True)
    input_text = (PACKMOL_HEADER.format(overlap, solvated_pdb, seed) +
                  PACKMOL_SOLUTE.format(solute_pdb, *center_solute))

    for solv, m_solvent in zip(solvent, n_solvent):
        m_solvent = int(m_solvent)
        solvent_pdb = tempfile.mkstemp(suffix='.pdb')[1]
        solv.save(solvent_pdb, overwrite=True)
        input_text += PACKMOL_BOX.format(solvent_pdb, m_solvent,
                           box_mins[0], box_mins[1], box_mins[2],
                           box_maxs[0], box_maxs[1], box_maxs[2])

    proc = Popen(PACKMOL, stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True)
    out, err = proc.communicate(input=input_text)
    if err:
        _packmol_error(out, err)

    # Create the topology and update the coordinates.
    solvated = Compound()
    solvated.add(solute)
    for solv, m_solvent in zip(solvent, n_solvent):
        for _ in range(m_solvent):
            solvated.add(clone(solv))
    solvated.update_coordinates(solvated_pdb)
    return solvated
コード例 #4
0
ファイル: packing.py プロジェクト: TengyuMaVandy/mbuild
def solvate(solute, solvent, n_solvent, box, overlap=0.2, seed=12345):
    """Solvate a compound in a box of solvent using packmol.

    Parameters
    ----------
    solute : mb.Compound
    solvent : mb.Compound
    n_solvent : int
    box : mb.Box
    overlap : float

    Returns
    -------
    solvated : mb.Compound

    """
    if not PACKMOL:
        raise IOError("Packmol not found")

    if isinstance(box, (list, tuple)):
        box = Box(lengths=box)

    n_solvent = int(n_solvent)

    solute_pdb = tempfile.mkstemp(suffix='.pdb')[1]
    solute.save(solute_pdb, overwrite=True)
    solvent_pdb = tempfile.mkstemp(suffix='.pdb')[1]
    solvent.save(solvent_pdb, overwrite=True)
    solvated_pdb = tempfile.mkstemp(suffix='.pdb')[1]

    # In angstroms for packmol.
    box_lengths = box.lengths * 10
    overlap *= 10
    # center_solute = (-solute.center) * 10
    center_solute = box_lengths/2

    # Build the input file and call packmol.
    input_text = (PACKMOL_HEADER.format(overlap, solvated_pdb, seed) +
                  PACKMOL_SOLUTE.format(solute_pdb, *center_solute) +
                  PACKMOL_BOX.format(solvent_pdb, n_solvent, *box_lengths))

    proc = Popen(PACKMOL, stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True)
    out, err = proc.communicate(input=input_text)
    if err:
        _packmol_error(out, err)

    # Create the topology and update the coordinates.
    solvated = Compound()
    solvated.add(solute)
    for _ in range(n_solvent):
        solvated.add(clone(solvent))
    solvated.update_coordinates(solvated_pdb)
    return solvated
コード例 #5
0
ファイル: port.py プロジェクト: ctk3b/mbuild
    def __init__(self, anchor=None, orientation=None, separation=0):
        super(Port, self).__init__(name='Port', port_particle=True)
        self.anchor = anchor

        up = Compound(name='subport', port_particle=True)
        up.add(Particle(name='G', pos=[0.005, 0.0025, -0.0025],
                        port_particle=True), 'middle')
        up.add(Particle(name='G', pos=[0.005, 0.0225, -0.0025],
                        port_particle=True), 'top')
        up.add(Particle(name='G', pos=[-0.015, -0.0075, -0.0025],
                        port_particle=True), 'left')
        up.add(Particle(name='G', pos=[0.005, -0.0175, 0.0075],
                        port_particle=True), 'right')

        down = clone(up)
        down.rotate(np.pi, [0, 0, 1])

        self.add(up, 'up')
        self.add(down, 'down')
        self.used = False

        if orientation is None:
            orientation = [0, 1, 0]

        default_direction = [0, 1, 0]
        if np.array_equal(
                np.asarray(default_direction), unit_vector(-np.asarray(orientation))):
            self.rotate(np.pi, [1, 0, 0])
        elif np.array_equal(
                np.asarray(default_direction), unit_vector(np.asarray(orientation))):
            pass
        else:
            normal = np.cross(default_direction, orientation)
            self.rotate(angle(default_direction, orientation), normal)

        if anchor:
            self.translate_to(anchor.pos)

        self.translate(separation*unit_vector(orientation))
コード例 #6
0
ファイル: port.py プロジェクト: oliviacane/mbuild
    def __init__(self, anchor=None):
        super(Port, self).__init__(name='Port', port_particle=True)
        self.anchor = anchor

        up = Compound(name='subport', port_particle=True)
        up.add(Particle(name='G', pos=[0, 0, 0], port_particle=True), 'middle')
        up.add(Particle(name='G', pos=[0, 0.02, 0], port_particle=True), 'top')
        up.add(Particle(name='G', pos=[-0.02, -0.01, 0], port_particle=True), 'left')
        up.add(Particle(name='G', pos=[0.0, -0.02, 0.01], port_particle=True), 'right')

        down = clone(up)

        rotate_around_z(down, np.pi)

        self.add(up, 'up')
        self.add(down, 'down')
コード例 #7
0
def reverse_map_solvent(cg_molecule,
                        target,
                        sol_per_bead=4,
                        cutoff=2,
                        scaling_factor=5):
    """
    molecules: list of water beads
    target: single atomistic solvent molecule
    sol_per_bead: number of atomistic solvent molecules per CG bead
    cutoff: max distance an atomistic molecule can be placed from the
        center of the CG bead
    """

    solvent = Compound()  # will contain all the solvent molecules in a bead
    solvent_molecule = Compound()  # placeholder for each single molecule

    # for each atomistic molcule
    print("unique")
    for i in range(sol_per_bead):
        # get a random vector by which to shift the atomistic molecule
        """
        randx = cutoff * (1 - 2 * np.random.rand())
        randy = np.sqrt(cutoff**2 - randx**2) * (1 - 2 * np.random.rand()) # randy bobandy
        randz = np.sqrt(cutoff**2 - randx**2 - randy**2) * (1 - 2 * np.random.rand())
        """
        randx = 0.0
        randy = 0.0
        randz = 0.0
        if i == 0:
            randx += 1.0
        elif i == 1:
            randx -= 1.0
        elif i == 2:
            randy += 1.0
        elif i == 3:
            randy -= 1.0

        shift_vec = np.array([randx, randy, randz])
        shift_vec *= 0.2
        #np.random.shuffle(shift_vec)

        # get random angles to spin the solvent molecule by
        theta = 2 * np.pi * np.random.rand()
        phi = np.pi * np.random.rand()

        # make a solvent molecule compound and shift it to the correct position
        solvent_molecule = clone(target)
        solvent_molecule.translate_to(cg_molecule.pos + shift_vec)
        solvent_molecule.spin(theta, [0, 0, 1])
        solvent_molecule.spin(phi, [1, 0, 0])

        # add the molecule to the bead compound
        solvent.add(solvent_molecule)

    # time to minimize the energy!
    # try with current atom names
    """
    try:
        solvent.energy_minimization(steps=500)

    # otherwise rename with just element names:
    except:
        atomnames = [i.name for i in solvent] # get the atomnames
        for atom in solvent: # make the atomnames elements
            atom.name=atom.name[0]

        solvent.energy_minimization(steps=500)

        for i, atom in enumerate(atomnames):
            solvent[i].name = atomnames[i]
    """
    # scale the solvent by 5

    # get a list of individual molecules (so that it separates them into residues)
    solvent = [clone(child) for child in solvent.children]

    for i, solvent_compound in enumerate(solvent):
        solvent[i].name = cg_molecule.name
        solvent[i].translate_to(np.array(solvent[i].pos) * scaling_factor)
    return solvent
コード例 #8
0
def reverse_map_molecule(molecule, target, mapping_moieties):
    print('unique')
    cg_molecule = clone(molecule)  # CG molecule
    aa_template = target[molecule.name]  # full aa Compound for molecule
    aa_moieties = mapping_moieties[
        molecule.name]  # list of lists of indices for each bead
    aa_molecule = Compound()  # this will have the final aa molecule
    cg_to_aa = []  # list of tuples containing (real index, aa atom)

    # now cycle through beads
    for index, bead in enumerate(cg_molecule.particles()):
        aa_atoms = Compound()  # placeholder for molecule atoms
        [
            aa_atoms.add(clone(aa_template.children[i]))
            for i in aa_moieties[index]
        ]
        aa_atoms.translate_to(bead.pos)  # shift to cg_bead position
        cg_to_aa += list(zip(aa_moieties[index], aa_atoms.children))

    # sort atoms in cg_to_aa and add them to the aa_molecule
    cg_to_aa = sorted(cg_to_aa)
    for atom in cg_to_aa:
        aa_molecule.add(clone(atom[1]))

    # add bonds from the template
    aa_template = aa_template.to_trajectory()
    for i, j in aa_template.top.bonds:
        aa_molecule.add_bond([aa_molecule[i.index], aa_molecule[j.index]])

    # equilibrate molecule and shift back to center
    # if the atom names match OpenBabel forcefield naming convention:
    try:
        aa_molecule.energy_minimization(steps=2500)

    # otherwise rename with just element names:
    except:
        atomnames = [i.name for i in aa_molecule]  # get the atomnames
        for atom in aa_molecule:  # make the atomnames elements
            atom.name = atom.name[0]

        aa_molecule.energy_minimization(steps=2500)

        for i, atom in enumerate(atomnames):
            aa_molecule[i].name = atomnames[i]

    #aa_molecule.translate(center)
    aa_molecule.name = molecule.name
    return aa_molecule
コード例 #9
0
ファイル: packing.py プロジェクト: zijiewu3/mbuild
def fill_region(
    compound,
    n_compounds,
    region,
    overlap=0.2,
    seed=12345,
    sidemax=100.0,
    edge=0.2,
    fix_orientation=False,
    temp_file=None,
    update_port_locations=False,
):
    """Fill a region of a box with `mbuild.Compound`(s) using PACKMOL.

    Parameters
    ----------
    compound : mb.Compound or list of mb.Compound
        Compound or list of compounds to fill in region.
    n_compounds : int or list of ints
        Number of compounds to be put in region.
    region : mb.Box or list of mb.Box
        Region to be filled by compounds.
    overlap : float, units nm, default=0.2
        Minimum separation between atoms of different molecules.
    seed : int, default=12345
        Random seed to be passed to PACKMOL.
    sidemax : float, optional, default=100.0
        Needed to build an initial approximation of the molecule distribution in
        PACKMOL. All system coordinates must fit with in +/- sidemax, so
        increase sidemax accordingly to your final box size.
    edge : float, units nm, default=0.2
        Buffer at the edge of the region to not place molecules. This is
        necessary in some systems because PACKMOL does not account for periodic
        boundary conditions in its optimization.
    fix_orientation : bool or list of bools
        Specify that compounds should not be rotated when filling the box,
        default=False.
    temp_file : str, default=None
        File name to write PACKMOL's raw output to.
    update_port_locations : bool, default=False
        After packing, port locations can be updated, but since compounds can be
        rotated, port orientation may be incorrect.

    Returns
    -------
    filled : mb.Compound

    If using mulitple regions and compounds, the nth value in each list are used
    in order.
    For example, if the third compound will be put in the third region using the
    third value in n_compounds.
    """
    # check that the user has the PACKMOL binary on their PATH
    _check_packmol(PACKMOL)

    if not isinstance(compound, (list, set)):
        compound = [compound]
    if not isinstance(n_compounds, (list, set)):
        n_compounds = [n_compounds]
    if not isinstance(fix_orientation, (list, set)):
        fix_orientation = [fix_orientation] * len(compound)

    if compound is not None and n_compounds is not None:
        if len(compound) != len(n_compounds):
            raise ValueError(
                "`compound` and `n_compounds` must be of equal length."
            )
    if compound is not None:
        if len(compound) != len(fix_orientation):
            raise ValueError(
                "`compound`, `n_compounds`, and `fix_orientation` must be of "
                "equal length."
            )

    # See if region is a single region or list
    if isinstance(region, Box):  # Cannot iterate over boxes
        region = [region]
    elif not any(isinstance(reg, (list, set, Box)) for reg in region):
        region = [region]
    region = [_validate_box(reg) for reg in region]

    # In angstroms for packmol.
    overlap *= 10

    # Build the input file and call packmol.
    filled_xyz = _new_xyz_file()

    # List to hold file handles for the temporary compounds
    compound_xyz_list = list()
    try:
        input_text = PACKMOL_HEADER.format(
            overlap, filled_xyz.name, seed, sidemax * 10
        )

        for comp, m_compounds, reg, rotate in zip(
            compound, n_compounds, region, fix_orientation
        ):
            m_compounds = int(m_compounds)

            compound_xyz = _new_xyz_file()
            compound_xyz_list.append(compound_xyz)

            comp.save(compound_xyz.name, overwrite=True)
            reg_mins = reg.mins * 10
            reg_maxs = reg.maxs * 10
            reg_maxs -= edge * 10  # Apply edge buffer
            reg_mins += edge * 10
            input_text += PACKMOL_BOX.format(
                compound_xyz.name,
                m_compounds,
                reg_mins[0],
                reg_mins[1],
                reg_mins[2],
                reg_maxs[0],
                reg_maxs[1],
                reg_maxs[2],
                PACKMOL_CONSTRAIN if rotate else "",
            )

        _run_packmol(input_text, filled_xyz, temp_file)

        # Create the topology and update the coordinates.
        filled = Compound()
        filled = _create_topology(filled, compound, n_compounds)
        filled.update_coordinates(
            filled_xyz.name, update_port_locations=update_port_locations
        )
    finally:
        for file_handle in compound_xyz_list:
            file_handle.close()
            os.unlink(file_handle.name)
        filled_xyz.close()
        os.unlink(filled_xyz.name)
    return filled
コード例 #10
0
ファイル: packing.py プロジェクト: zijiewu3/mbuild
def fill_sphere(
    compound,
    sphere,
    n_compounds=None,
    density=None,
    overlap=0.2,
    seed=12345,
    sidemax=100.0,
    edge=0.2,
    compound_ratio=None,
    fix_orientation=False,
    temp_file=None,
    update_port_locations=False,
):
    """Fill a sphere with a compound using PACKMOL.

    One argument of `n_compounds and density` must be specified.

    If `n_compounds` is not None, the specified number of n_compounds will be
    inserted into a sphere of the specified size.

    If `density` is not None, the corresponding number of compounds will be
    calculated internally.

    Parameters
    ----------
    compound : mb.Compound or list of mb.Compound
        Compound or list of compounds to be put in box.
    sphere : list, units nm
        Sphere coordinates in the form [x_center, y_center, z_center, radius]
    n_compounds : int or list of int
        Number of compounds to be put in box.
    density : float, units kg/m^3, default=None
        Target density for the sphere in macroscale units.
    overlap : float, units nm, default=0.2
        Minimum separation between atoms of different molecules.
    seed : int, default=12345
        Random seed to be passed to PACKMOL.
    sidemax : float, optional, default=100.0
        Needed to build an initial approximation of the molecule distribution in
        PACKMOL. All system coordinates must fit with in +/- sidemax, so
        increase sidemax accordingly to your final sphere size
    edge : float, units nm, default=0.2
        Buffer at the edge of the sphere to not place molecules. This is
        necessary in some systems because PACKMOL does not account for periodic
        boundary conditions in its optimization.
    compound_ratio : list, default=None
        Ratio of number of each compound to be put in sphere. Only used in the
        case of `density` having been specified, `n_compounds` not specified,
        and more than one `compound`.
    fix_orientation : bool or list of bools
        Specify that compounds should not be rotated when filling the sphere,
        default=False.
    temp_file : str, default=None
        File name to write PACKMOL's raw output to.
    update_port_locations : bool, default=False
        After packing, port locations can be updated, but since compounds can be
        rotated, port orientation may be incorrect.

    Returns
    -------
    filled : mb.Compound
    """
    _check_packmol(PACKMOL)

    arg_count = 2 - [n_compounds, density].count(None)
    if arg_count != 1:
        raise ValueError(
            "Exactly 1 of `n_compounds` and `density` must be specified. "
            "{} were given.".format(arg_count)
        )

    if isinstance(sphere, (list, set, tuple)):
        if len(sphere) != 4:
            raise ValueError("`sphere` must be a list of len 4")
    else:
        raise TypeError("`sphere` must be a list")

    if not isinstance(compound, (list, set)):
        compound = [compound]
    if n_compounds is not None and not isinstance(n_compounds, (list, set)):
        n_compounds = [n_compounds]
    if not isinstance(fix_orientation, (list, set)):
        fix_orientation = [fix_orientation] * len(compound)

    if compound is not None and n_compounds is not None:
        if len(compound) != len(n_compounds):
            raise ValueError(
                "`compound` and `n_compounds` must be of equal length."
            )

    if compound is not None:
        if len(compound) != len(fix_orientation):
            raise ValueError(
                "`compound`, `n_compounds`, and `fix_orientation` must be of "
                "equal length."
            )

    for coord in sphere[:3]:
        if coord < sphere[3]:
            raise ValueError(
                "`sphere` center coordinates must be greater than radius."
            )

    # Apply edge buffer
    radius = sphere[3] - edge

    if density is not None:
        if n_compounds is None:
            if len(compound) == 1:
                compound_mass = np.sum(
                    [a.mass for a in compound[0].to_parmed().atoms]
                )
                # Conversion from kg/m^3 / amu * nm^3 to dimensionless units
                n_compounds = [
                    int(
                        density
                        / compound_mass
                        * (4 / 3 * np.pi * radius ** 3)
                        * 0.60224
                    )
                ]
            else:
                if compound_ratio is None:
                    raise ValueError(
                        "Determing `n_compounds` from `density` for systems "
                        "with more than one compound type requires"
                        "`compound_ratio`"
                    )
                if len(compound) != len(compound_ratio):
                    raise ValueError(
                        "Length of `compound_ratio` must equal length of "
                        "`compound`"
                    )
                prototype_mass = 0
                for c, r in zip(compound, compound_ratio):
                    prototype_mass += r * np.sum(
                        [a.mass for a in c.to_parmed().atoms]
                    )
                # Conversion from kg/m^3 / amu * nm^3 to dimensionless units
                n_prototypes = int(
                    density
                    / prototype_mass
                    * (4 / 3 * np.pi * radius ** 3)
                    * 0.60224
                )
                n_compounds = list()
                for c in compound_ratio:
                    n_compounds.append(int(n_prototypes * c))

    # In angstroms for packmol.
    sphere = np.multiply(sphere, 10)
    radius *= 10
    overlap *= 10

    # Build the input file for each compound and call packmol.
    filled_xyz = _new_xyz_file()

    # List to hold file handles for the temporary compounds
    compound_xyz_list = list()
    try:
        input_text = PACKMOL_HEADER.format(
            overlap, filled_xyz.name, seed, sidemax * 10
        )
        for comp, m_compounds, rotate in zip(
            compound, n_compounds, fix_orientation
        ):
            m_compounds = int(m_compounds)

            compound_xyz = _new_xyz_file()
            compound_xyz_list.append(compound_xyz)

            comp.save(compound_xyz.name, overwrite=True)
            input_text += PACKMOL_SPHERE.format(
                compound_xyz.name,
                m_compounds,
                sphere[0],
                sphere[1],
                sphere[2],
                radius,
                PACKMOL_CONSTRAIN if rotate else "",
            )
        _run_packmol(input_text, filled_xyz, temp_file)

        # Create the topology and update the coordinates.
        filled = Compound()
        filled = _create_topology(filled, compound, n_compounds)
        filled.update_coordinates(
            filled_xyz.name, update_port_locations=update_port_locations
        )
    finally:
        for file_handle in compound_xyz_list:
            file_handle.close()
            os.unlink(file_handle.name)
        filled_xyz.close()
        os.unlink(filled_xyz.name)
    return filled
コード例 #11
0
def solvate(solute,
            solvent,
            n_solvent,
            box,
            overlap=0.2,
            seed=12345,
            edge=0.2,
            fix_orientation=False,
            temp_file=None):
    """Solvate a compound in a box of solvent using packmol.

    Parameters
    ----------
    solute : mb.Compound
        Compound to be placed in a box and solvated.
    solvent : mb.Compound
        Compound to solvate the box.
    n_solvent : int
        Number of solvents to be put in box.
    box : mb.Box
        Box to be filled by compounds.
    overlap : float, units nm, default=0.2
        Minimum separation between atoms of different molecules.
    seed : int, default=12345
        Random seed to be passed to PACKMOL.
    edge : float, units nm, default=0.2
        Buffer at the edge of the box to not place molecules. This is necessary
        in some systems because PACKMOL does not account for periodic boundary
        conditions in its optimization.
    fix_orientation : bool
        Specify if solvent should not be rotated when filling box,
        default=False.
    temp_file : str, default=None
        File name to write PACKMOL's raw output to.

    Returns
    -------
    solvated : mb.Compound

    """
    _check_packmol(PACKMOL)

    box = _validate_box(box)
    if not isinstance(solvent, (list, set)):
        solvent = [solvent]
    if not isinstance(n_solvent, (list, set)):
        n_solvent = [n_solvent]
    if not isinstance(fix_orientation, (list, set)):
        fix_orientation = [fix_orientation] * len(solvent)

    if len(solvent) != len(n_solvent):
        msg = ("`n_solvent` and `n_solvent` must be of equal length.")
        raise ValueError(msg)

    # In angstroms for packmol.
    box_mins = box.mins * 10
    box_maxs = box.maxs * 10
    overlap *= 10
    center_solute = (box_maxs + box_mins) / 2

    # Apply edge buffer
    box_maxs -= edge * 10

    # Build the input file for each compound and call packmol.
    solvated_pdb = tempfile.mkstemp(suffix='.pdb')[1]
    solute_pdb = tempfile.mkstemp(suffix='.pdb')[1]
    solute.save(solute_pdb, overwrite=True)
    input_text = (PACKMOL_HEADER.format(overlap, solvated_pdb, seed) +
                  PACKMOL_SOLUTE.format(solute_pdb, *center_solute))

    for solv, m_solvent, rotate in zip(solvent, n_solvent, fix_orientation):
        m_solvent = int(m_solvent)
        solvent_pdb = tempfile.mkstemp(suffix='.pdb')[1]
        solv.save(solvent_pdb, overwrite=True)
        input_text += PACKMOL_BOX.format(solvent_pdb, m_solvent, box_mins[0],
                                         box_mins[1], box_mins[2], box_maxs[0],
                                         box_maxs[1], box_maxs[2],
                                         PACKMOL_CONSTRAIN if rotate else "")
    _run_packmol(input_text, solvated_pdb, temp_file)

    # Create the topology and update the coordinates.
    solvated = Compound()
    solvated.add(solute)
    for solv, m_solvent in zip(solvent, n_solvent):
        for _ in range(m_solvent):
            solvated.add(clone(solv))
    solvated.update_coordinates(solvated_pdb)
    return solvated
コード例 #12
0
def fill_box(compound,
             n_compounds=None,
             box=None,
             density=None,
             overlap=0.2,
             seed=12345,
             edge=0.2,
             compound_ratio=None,
             aspect_ratio=None,
             fix_orientation=False,
             temp_file=None):
    """Fill a box with a compound using packmol.

    Two arguments of `n_compounds, box, and density` must be specified.

    If `n_compounds` and `box` are not None, the specified number of
    n_compounds will be inserted into a box of the specified size.

    If `n_compounds` and `density` are not None, the corresponding box
    size will be calculated internally. In this case, `n_compounds`
    must be an int and not a list of int.

    If `box` and `density` are not None, the corresponding number of
    compounds will be calculated internally.

    For the cases in which `box` is not specified but generated internally,
    the default behavior is to calculate a cubic box. Optionally,
    `aspect_ratio` can be passed to generate a non-cubic box.

    Parameters
    ----------
    compound : mb.Compound or list of mb.Compound
        Compound or list of compounds to be put in box.
    n_compounds : int or list of int
        Number of compounds to be put in box.
    box : mb.Box
        Box to be filled by compounds.
    density : float, units kg/m^3, default=None
        Target density for the system in macroscale units. If not None, one of
        `n_compounds` or `box`, but not both, must be specified.
    overlap : float, units nm, default=0.2
        Minimum separation between atoms of different molecules.
    seed : int, default=12345
        Random seed to be passed to PACKMOL.
    edge : float, units nm, default=0.2
        Buffer at the edge of the box to not place molecules. This is necessary
        in some systems because PACKMOL does not account for periodic boundary
        conditions in its optimization.
    compound_ratio : list, default=None
        Ratio of number of each compound to be put in box. Only used in the
        case of `density` and `box` having been specified, `n_compounds` not
        specified, and more than one `compound`.
    aspect_ratio : list of float
        If a non-cubic box is desired, the ratio of box lengths in the x, y,
        and z directions.
    fix_orientation : bool or list of bools
        Specify that compounds should not be rotated when filling the box,
        default=False.
    temp_file : str, default=None
        File name to write PACKMOL's raw output to.

    Returns
    -------
    filled : mb.Compound

    """
    _check_packmol(PACKMOL)

    arg_count = 3 - [n_compounds, box, density].count(None)
    if arg_count != 2:
        msg = ("Exactly 2 of `n_compounds`, `box`, and `density` "
               "must be specified. {} were given.".format(arg_count))
        raise ValueError(msg)

    if box is not None:
        box = _validate_box(box)
    if not isinstance(compound, (list, set)):
        compound = [compound]
    if n_compounds is not None and not isinstance(n_compounds, (list, set)):
        n_compounds = [n_compounds]
    if not isinstance(fix_orientation, (list, set)):
        fix_orientation = [fix_orientation] * len(compound)

    if compound is not None and n_compounds is not None:
        if len(compound) != len(n_compounds):
            msg = ("`compound` and `n_compounds` must be of equal length.")
            raise ValueError(msg)

    if compound is not None:
        if len(compound) != len(fix_orientation):
            msg = (
                "`compound`, `n_compounds`, and `fix_orientation` must be of equal length."
            )
            raise ValueError(msg)

    if density is not None:
        if box is None and n_compounds is not None:
            total_mass = np.sum([
                n * np.sum([a.mass for a in c.to_parmed().atoms])
                for c, n in zip(compound, n_compounds)
            ])
            # Conversion from (amu/(kg/m^3))**(1/3) to nm
            L = (total_mass / density)**(1 / 3) * 1.1841763
            if aspect_ratio is None:
                box = _validate_box(Box(3 * [L]))
            else:
                L *= np.prod(aspect_ratio)**(-1 / 3)
                box = _validate_box(Box([val * L for val in aspect_ratio]))
        if n_compounds is None and box is not None:
            if len(compound) == 1:
                compound_mass = np.sum(
                    [a.mass for a in compound[0].to_parmed().atoms])
                # Conversion from kg/m^3 / amu * nm^3 to dimensionless units
                n_compounds = [
                    int(density / compound_mass * np.prod(box.lengths) *
                        .60224)
                ]
            else:
                if compound_ratio is None:
                    msg = (
                        "Determing `n_compounds` from `density` and `box` "
                        "for systems with more than one compound type requires"
                        "`compound_ratio`")
                    raise ValueError(msg)
                if len(compound) != len(compound_ratio):
                    msg = ("Length of `compound_ratio` must equal length of "
                           "`compound`")
                    raise ValueError(msg)
                prototype_mass = 0
                for c, r in zip(compound, compound_ratio):
                    prototype_mass += r * np.sum(
                        [a.mass for a in c.to_parmed().atoms])
                # Conversion from kg/m^3 / amu * nm^3 to dimensionless units
                n_prototypes = int(density / prototype_mass *
                                   np.prod(box.lengths) * .60224)
                n_compounds = list()
                for c in compound_ratio:
                    n_compounds.append(int(n_prototypes * c))

    # In angstroms for packmol.
    box_mins = box.mins * 10
    box_maxs = box.maxs * 10
    overlap *= 10

    # Apply edge buffer
    box_maxs -= edge * 10

    # Build the input file for each compound and call packmol.
    filled_pdb = tempfile.mkstemp(suffix='.pdb')[1]
    input_text = PACKMOL_HEADER.format(overlap, filled_pdb, seed)

    for comp, m_compounds, rotate in zip(compound, n_compounds,
                                         fix_orientation):
        m_compounds = int(m_compounds)
        compound_pdb = tempfile.mkstemp(suffix='.pdb')[1]
        comp.save(compound_pdb, overwrite=True)
        input_text += PACKMOL_BOX.format(compound_pdb, m_compounds,
                                         box_mins[0], box_mins[1], box_mins[2],
                                         box_maxs[0], box_maxs[1], box_maxs[2],
                                         PACKMOL_CONSTRAIN if rotate else "")

    _run_packmol(input_text, filled_pdb, temp_file)

    # Create the topology and update the coordinates.
    filled = Compound()
    for comp, m_compounds in zip(compound, n_compounds):
        for _ in range(m_compounds):
            filled.add(clone(comp))
    filled.update_coordinates(filled_pdb)
    filled.periodicity = np.asarray(box.lengths, dtype=np.float32)
    return filled
コード例 #13
0
def specific_ff_to_residue(
    structure,
    forcefield_selection=None,
    residues=None,
    reorder_res_in_pdb_psf=False,
    box=None,
    boxes_for_simulation=1,
):
    """
    Takes the mbuild Compound or mbuild Box structure and applies the selected
    force field to the corresponding residue via foyer.
    Note: a residue is defined as a molecule in this case, so it is not
    designed for applying a force field to a protein.

    Parameters
    ----------
    structure: mbuild Compound object or mbuild Box object;
        The mBuild Compound object or mbuild Box object, which contains the molecules
        (or empty box) that will have the force field applied to them.
    forcefield_selection: str or dictionary, default=None
        Apply a forcefield to the output file by selecting a force field xml file with
        its path or by using the standard force field name provided the `foyer` package.
        Example dict for FF file: {'ETH' : 'oplsaa.xml', 'OCT': 'path_to file/trappe-ua.xml'}
        Example str for FF file: 'path_to file/trappe-ua.xml'
        Example dict for standard FF names : {'ETH' : 'oplsaa', 'OCT': 'trappe-ua'}
        Example str for standard FF names: 'trappe-ua'
        Example of a mixed dict with both : {'ETH' : 'oplsaa', 'OCT': 'path_to file/'trappe-ua.xml'}
    residues: list, [str, ..., str], default=None
        Labels of unique residues in the Compound. Residues are assigned by
        checking against Compound.name.  Only supply residue names as 4 characters
        strings, as the residue names are truncated to 4 characters to fit in the
        psf and pdb file.
    reorder_res_in_pdb_psf: bool, default=False
        This option provides the ability to reorder the residues/molecules from the original
        structure's order.  If True, the residues will be reordered as they appear in the residues
        variable.  If False, the order will be the same as entered in the original structure.
    box: list, default=None
        list of 3 positive float or integer values or the dimensions [x, y ,z]
        for structure in nanometers (nm). This is to add/override or change the structures dimensions.
        Ex: [1,2,3]
    boxes_for_simulation: int [1, 2], default = 1
        Gibbs (GEMC) or grand canonical (GCMC) ensembles are examples of where the boxes_for_simulation would be 2.
        Canonical (NVT) or isothermal–isobaric (NPT) ensembles are example with the boxes_for_simulation equal to 1.
        Note: the only valid options are 1 or 2.

    Returns
    -------
    list, [structure, coulomb14scalar_dict, lj14_scalar_dict, residues_applied_list]
        structure: parmed.Structure
            parmed structure with applied force field
        coulomb14scalar_dict: dict
            a dictionary with the 1,4-colombic scalars for each residue
                (i.e., a different force field could on each residue)
        lj14_scalar_dict: dict
            a dictionary with the 1,4-LJ scalars for each residue
            (i.e., a different force field could on each residue)
        residues_applied_list: list
            list of residues (i.e., list of stings).
            These are all the residues in which the force field actually applied

    Notes
    -----
    To write the NAMD/GOMC force field, pdb, psf, and force field
    (.inp) files, the residues and forcefields must be provided in
    a str or dictionary. If a dictionary is provided all residues must
    be specified to a force field if the boxes_for_simulation is equal to 1.

    Generating an empty box (i.e., pdb and psf files):
    Enter residues = [], but the accompanying structure must be an empty mb.Box.
    However, when doing this, the forcefield_selection must be supplied,
    or it will provide an error (i.e., forcefield_selection can not be equal to None).

    In this current FF/psf/pdb writer, a residue type is essentially a molecule type.
    Therefore, it can only correctly write systems where every bead/atom in the molecule
    has the same residue name, and the residue name is specific to that molecule type.
    For example: a protein molecule with many residue names is not currently supported,
    but is planned to be supported in the future.
    """

    if has_foyer:
        from foyer import Forcefield
        from foyer.forcefields import forcefields
    else:
        print_error_message = (
            "Package foyer is not installed. "
            "Please install it using conda install -c conda-forge foyer")
        raise ImportError(print_error_message)

    if not isinstance(structure, (Compound, mb.Box)):
        print_error_message = ("ERROR: The structure expected to be of type: "
                               "{} or {}, received: {}".format(
                                   type(Compound()),
                                   type(mb.Box(lengths=[1, 1, 1])),
                                   type(structure),
                               ))
        raise TypeError(print_error_message)

    print("forcefield_selection = " + str(forcefield_selection))
    if forcefield_selection is None:
        print_error_message = (
            "Please the force field selection (forcefield_selection) as a dictionary "
            "with all the residues specified to a force field "
            '-> Ex: {"Water" : "oplsaa", "OCT": "path/trappe-ua.xml"}, '
            "Note: the file path must be specified the force field file "
            "or by using the standard force field name provided the `foyer` package."
        )
        raise TypeError(print_error_message)

    elif forcefield_selection is not None and not isinstance(
            forcefield_selection, dict):
        print_error_message = (
            "The force field selection (forcefield_selection) "
            "is not a dictionary. Please enter a dictionary "
            "with all the residues specified to a force field "
            '-> Ex: {"Water" : "oplsaa", "OCT": "path/trappe-ua.xml"}, '
            "Note: the file path must be specified the force field file "
            "or by using the standard force field name provided the `foyer` package."
        )
        raise TypeError(print_error_message)

    if residues is None or not isinstance(residues, list):
        print_error_message = (
            "Please enter the residues in the Specific_FF_to_residue function."
        )
        raise TypeError(print_error_message)

    if not isinstance(reorder_res_in_pdb_psf, bool):
        print_error_message = (
            "Please enter the reorder_res_in_pdb_psf "
            "in the Specific_FF_to_residue function (i.e., True or False).")
        raise TypeError(print_error_message)

    if box is not None:
        box_ang = []
        box_length = len(box)
        if box_length != 3:
            print_error_message = "Please enter all 3 values, and only 3 values for the box dimensions."
            raise ValueError(print_error_message)
        print_error_message_box_positive_values = (
            "Please enter positive ( > 0) integers for the box dimensions.")
        for box_iter in box:
            if not isinstance(box_iter, int) and not isinstance(
                    box_iter, float):
                raise TypeError(print_error_message_box_positive_values)
            if box_iter < 0:
                raise ValueError(print_error_message_box_positive_values)
            # change from nm to Angstroms
            box_ang.append(box_iter * 10)

    print_error_message_for_boxes_for_simulatiion = (
        "ERROR: Please enter boxes_for_simulation equal "
        "the integer 1 or 2.")
    if not isinstance(boxes_for_simulation, int):
        raise TypeError(print_error_message_for_boxes_for_simulatiion)

    elif isinstance(boxes_for_simulation,
                    int) and boxes_for_simulation not in [
                        1,
                        2,
                    ]:
        raise ValueError(print_error_message_for_boxes_for_simulatiion)

    forcefield_keys_list = []
    if forcefield_selection is not None:
        for res in forcefield_selection.keys():
            forcefield_keys_list.append(res)
        ff_data = forcefield_selection

    if forcefield_keys_list == [] and len(residues) != 0:
        print_error_message = "The forcefield_selection variable are not provided, but there are residues provided."
        raise ValueError(print_error_message)

    elif forcefield_keys_list != [] and len(residues) == 0:
        print_error_message = (
            "The residues variable is an empty list but there are "
            "forcefield_selection variables provided.")
        raise ValueError(print_error_message)

    user_entered_ff_with_path_dict = (
        {}
    )  # True means user entered the path, False is a standard foyer FF with no path
    for z in range(0, len(forcefield_keys_list)):
        for res_i in range(0, len(residues)):
            if residues[res_i] == forcefield_keys_list[z]:
                if (os.path.splitext(ff_data[forcefield_keys_list[z]])[1]
                        == ".xml" and len(residues) != 0):
                    user_entered_ff_with_path_dict.update(
                        {residues[res_i]: True})
                elif (os.path.splitext(ff_data[forcefield_keys_list[z]])[1]
                      == "" and len(residues) != 0):
                    user_entered_ff_with_path_dict.update(
                        {residues[res_i]: False})
                else:
                    print_error_message = (
                        r"Please make sure you are entering the correct "
                        "foyer FF name and not a path to a FF file. "
                        "If you are entering a path to a FF file, "
                        "please use the forcefield_files variable with the "
                        "proper XML extension (.xml).")
                    raise ValueError(print_error_message)

    coulomb14scalar_dict = {}
    lj14_scalar_dict = {}
    for j in range(0, len(forcefield_keys_list)):
        residue_iteration = forcefield_keys_list[j]
        if user_entered_ff_with_path_dict[residue_iteration]:
            ff_for_residue_iteration = ff_data[residue_iteration]

            try:
                read_xlm_iteration = minidom.parse(ff_for_residue_iteration)

            except:
                print_error_message = (
                    "Please make sure you are entering the correct foyer FF path, "
                    "including the FF file name.xml "
                    "If you are using the pre-build FF files in foyer, "
                    "please us the forcefield_names variable.")
                raise ValueError(print_error_message)
        elif not user_entered_ff_with_path_dict[residue_iteration]:
            ff_for_residue_iteration = ff_data[residue_iteration]
            ff_names_path_iteration = (forcefields.get_ff_path()[0] + "/xml/" +
                                       ff_for_residue_iteration + ".xml")
            try:
                read_xlm_iteration = minidom.parse(ff_names_path_iteration)
            except:
                print_error_message = (
                    "Please make sure you are entering the correct foyer FF name, or the "
                    "correct file extension (i.e., .xml, if required).")
                raise ValueError(print_error_message)
        lj_coul_1_4_values = read_xlm_iteration.getElementsByTagName(
            "NonbondedForce")

        for Scalar in lj_coul_1_4_values:
            coulomb14scalar_dict.update({
                residue_iteration:
                float(Scalar.getAttribute("coulomb14scale"))
            })
            lj14_scalar_dict.update(
                {residue_iteration: float(Scalar.getAttribute("lj14scale"))})

    # Check to see if it is an empty mbuild.Compound and set intial atoms to 0
    # note empty mbuild.Compound will read 1 atoms but there is really noting there
    if isinstance(structure, Compound):
        if len(structure.children) == 0:
            # there are no real atoms in the Compound so the test fails. User should use mbuild.Box
            print_error_message = (
                "ERROR: If you are not providing an empty box, "
                "you need to specify the atoms/beads as children in the mb.Compound. "
                "If you are providing and empty box, please do so by specifying and "
                "mbuild Box ({})".format(type(mb.Box(lengths=[1, 1, 1]))))
            raise TypeError(print_error_message)
        else:
            initial_no_atoms = len(structure.to_parmed().atoms)

    # calculate the initial number of atoms for later comparison
    if isinstance(structure, mb.Box):
        if structure.lengths is not None:
            mb_box_length_0 = structure.lengths[0]
            mb_box_length_1 = structure.lengths[1]
            mb_box_length_2 = structure.lengths[2]

        elif structure.mins is not None and structure.maxs is not None:
            mb_box_mins_0 = structure.mins[0]
            mb_box_mins_1 = structure.mins[1]
            mb_box_mins_2 = structure.mins[2]

            mb_box_maxs_0 = structure.maxs[0]
            mb_box_maxs_1 = structure.maxs[1]
            mb_box_maxs_2 = structure.maxs[2]

            mb_box_length_0 = mb_box_maxs_0 - mb_box_mins_0
            mb_box_length_1 = mb_box_maxs_1 - mb_box_mins_1
            mb_box_length_2 = mb_box_maxs_2 - mb_box_mins_2

        if (structure.angles[0] != 90 or structure.angles[1] != 90
                or structure.angles[2] != 90):
            print_error_message = (
                "This writer only currently supports orthogonal boxes "
                "(i.e., boxes with all 90 degree angles).")
            raise ValueError(print_error_message)

        # convert the structure to a mbuild.Compound and set the  initial_no_atoms = 0
        if (structure.lengths is None or mb_box_length_0 <= 0
                or mb_box_length_1 <= 0 or mb_box_length_2 <= 0):
            print_error_message = (
                "An empty box was specified, with one or more dimensions <= 0."
            )
            raise ValueError(print_error_message)
        else:
            structure = mb.Compound()
            structure.periodicity[0] = mb_box_length_0
            structure.periodicity[1] = mb_box_length_1
            structure.periodicity[2] = mb_box_length_2
            initial_no_atoms = 0

    # add the FF to the residues
    compound_box_infor = structure.to_parmed(residues=residues)
    new_structure = pmd.Structure()
    new_structure.box = compound_box_infor.box

    # prepare all compound and remove nested compounds
    no_layers_to_check_for_residues = 3

    print_error_message_all_res_not_specified = (
        "ERROR: All the residues are not specified, or "
        "the residues entered does not match the residues that "
        "were found and built for structure.")
    for j in range(0, no_layers_to_check_for_residues):
        new_compound_iter = mb.Compound()
        new_compound_iter.periodicity[0] = structure.periodicity[0]
        new_compound_iter.periodicity[1] = structure.periodicity[1]
        new_compound_iter.periodicity[2] = structure.periodicity[2]
        if structure.name in residues:
            if len(structure.children) == 0:
                warn(
                    "Warning: This residue is the atom, and is a single atom., "
                    + str(structure.name))
                new_compound_iter.add(mb.compound.clone(structure))

            elif len(structure.children) > 0:

                new_compound_iter.add(mb.compound.clone(structure))

        else:
            for child in structure.children:
                if len(child.children) == 0:
                    if child.name not in residues:
                        raise ValueError(
                            print_error_message_all_res_not_specified)

                    else:
                        new_compound_iter.add(mb.compound.clone(child))

                elif len(child.children) > 0:
                    if child.name in residues:
                        new_compound_iter.add(mb.compound.clone(child))
                    else:
                        for sub_child in child.children:
                            if sub_child.name in residues:
                                new_compound_iter.add(
                                    mb.compound.clone(sub_child))

                            else:
                                if len(sub_child.children) == 0 and (
                                        child.name not in residues):

                                    raise ValueError(
                                        print_error_message_all_res_not_specified
                                    )

        structure = new_compound_iter

    residues_applied_list = []
    residue_orig_order_list = []
    for child in structure.children:
        if child.name not in residue_orig_order_list:
            residue_orig_order_list.append(child.name)
    for res_reorder_iter in range(0, len(residues)):
        if residues[res_reorder_iter] not in residue_orig_order_list:
            text_to_print_1 = (
                "All the residues were not used from the forcefield_selection "
                "string or dictionary. There may be residues below other "
                "specified residues in the mbuild.Compound hierarchy. "
                "If so, all the highest listed residues pass down the force "
                "fields through the hierarchy. Alternatively, residues that "
                "are not in the structure may have been specified. ")
            text_to_print_2 = (
                "Note: This warning will appear if you are using the CHARMM pdb and psf writers "
                +
                "2 boxes, and the boxes do not contain all the residues in each box."
            )
            if boxes_for_simulation == 1:
                warn(text_to_print_1)
                raise ValueError(text_to_print_1)
            if boxes_for_simulation == 2:
                warn(text_to_print_1 + text_to_print_2)

    if not reorder_res_in_pdb_psf:
        residues = residue_orig_order_list
    elif reorder_res_in_pdb_psf:
        print(
            "INFO: the output file are being reordered in via the residues list's sequence."
        )

    for i in range(0, len(residues)):
        children_in_iteration = False
        new_compound_iteration = mb.Compound()
        new_compound_iter.periodicity[0] = structure.periodicity[0]
        new_compound_iter.periodicity[1] = structure.periodicity[1]
        new_compound_iter.periodicity[2] = structure.periodicity[2]
        new_structure_iteration = pmd.Structure()
        new_structure_iteration.box = compound_box_infor.box
        for child in structure.children:
            if ff_data.get(child.name) is None:
                print_error_message = "ERROR: All residues are not specified in the force_field dictionary"
                raise ValueError(print_error_message)

            if child.name == residues[i]:
                children_in_iteration = True
                new_compound_iteration.add(mb.compound.clone(child))

        if children_in_iteration:
            if user_entered_ff_with_path_dict[residues[i]]:
                ff_iteration = Forcefield(ff_data[residues[i]])
                residues_applied_list.append(residues[i])
            elif not user_entered_ff_with_path_dict[residues[i]]:
                ff_iteration = Forcefield(name=ff_data[residues[i]])
                residues_applied_list.append(residues[i])

            new_structure_iteration = ff_iteration.apply(
                new_compound_iteration, residues=[residues[i]])
            new_structure = new_structure + new_structure_iteration

    if box is not None:
        new_structure.box[0] = box_ang[0]
        new_structure.box[1] = box_ang[1]
        new_structure.box[2] = box_ang[2]

    structure = new_structure

    # calculate the final number of atoms
    final_no_atoms = len(structure.atoms)

    if final_no_atoms != initial_no_atoms:
        print_error_message = (
            "ERROR: The initial number of atoms sent to the force field analysis is "
            "not the same as the final number of atoms analyzed. "
            "The initial number of atoms was {} and the final number of atoms was {}. "
            "Please ensure that all the residues names that are in the initial "
            "Compound are listed in the residues list "
            "(i.e., the residues variable).".format(initial_no_atoms,
                                                    final_no_atoms))
        raise ValueError(print_error_message)

    return [
        structure,
        coulomb14scalar_dict,
        lj14_scalar_dict,
        residues_applied_list,
    ]
コード例 #14
0
def fill_box(compound, n_compounds, box, overlap=0.2, seed=12345):
    """Fill a box with a compound using packmol.

    Parameters
    ----------
    compound : mb.Compound or list of mb.Compound
    n_compounds : int or list of int
    box : mb.Box
    overlap : float

    Returns
    -------
    filled : mb.Compound

    """
    if not PACKMOL:
        msg = "Packmol not found."
        if sys.platform.startswith("win"):
            msg = (msg +
                   " If packmol is already installed, make sure that the "
                   "packmol.exe is on the path.")
        raise IOError(msg)

    box = _validate_box(box)
    if not isinstance(compound, (list, set)):
        compound = [compound]
    if not isinstance(n_compounds, (list, set)):
        n_compounds = [n_compounds]

    # In angstroms for packmol.
    box_mins = box.mins * 10
    box_maxs = box.maxs * 10
    overlap *= 10

    # Build the input file for each compound and call packmol.
    filled_pdb = tempfile.mkstemp(suffix='.pdb')[1]
    input_text = PACKMOL_HEADER.format(overlap, filled_pdb, seed)

    for comp, m_compounds in zip(compound, n_compounds):
        m_compounds = int(m_compounds)
        compound_pdb = tempfile.mkstemp(suffix='.pdb')[1]
        comp.save(compound_pdb, overwrite=True)
        input_text += PACKMOL_BOX.format(compound_pdb, m_compounds,
                                         box_mins[0], box_mins[1], box_mins[2],
                                         box_maxs[0], box_maxs[1], box_maxs[2])

    proc = Popen(PACKMOL,
                 stdin=PIPE,
                 stdout=PIPE,
                 stderr=PIPE,
                 universal_newlines=True)
    out, err = proc.communicate(input=input_text)
    if err:
        _packmol_error(out, err)

    # Create the topology and update the coordinates.
    filled = Compound()
    for comp, m_compounds in zip(compound, n_compounds):
        for _ in range(m_compounds):
            filled.add(clone(comp))
    filled.update_coordinates(filled_pdb)
    return filled
コード例 #15
0
ファイル: packing.py プロジェクト: iModels/mbuild
def fill_region(compound, n_compounds, region, overlap=0.2,
                seed=12345, edge=0.2, fix_orientation=False, temp_file=None):
    """Fill a region of a box with a compound using packmol.

    Parameters
    ----------
    compound : mb.Compound or list of mb.Compound
        Compound or list of compounds to be put in region.
    n_compounds : int or list of int
        Number of compounds to be put in region.
    region : mb.Box or list of mb.Box
        Region to be filled by compounds.
    overlap : float, units nm, default=0.2
        Minimum separation between atoms of different molecules.
    seed : int, default=12345
        Random seed to be passed to PACKMOL.
    edge : float, units nm, default=0.2
        Buffer at the edge of the region to not place molecules. This is
        necessary in some systems because PACKMOL does not account for
        periodic boundary conditions in its optimization.
    fix_orientation : bool or list of bools
        Specify that compounds should not be rotated when filling the box,
        default=False.
    temp_file : str, default=None
        File name to write PACKMOL's raw output to.

    Returns
    -------
    filled : mb.Compound

    If using mulitple regions and compounds, the nth value in each
    list are used in order.
    For example, if the third compound will be put in the third
    region using the third value in n_compounds.
    """
    _check_packmol(PACKMOL)

    if not isinstance(compound, (list, set)):
        compound = [compound]
    if not isinstance(n_compounds, (list, set)):
        n_compounds = [n_compounds]
    if not isinstance(fix_orientation, (list, set)):
        fix_orientation = [fix_orientation]*len(compound)

    if compound is not None and n_compounds is not None:
        if len(compound) != len(n_compounds):
            msg = ("`compound` and `n_compounds` must be of equal length.")
            raise ValueError(msg)
    if compound is not None:
        if len(compound) != len(fix_orientation):
            msg = ("`compound`, `n_compounds`, and `fix_orientation` must be of equal length.")
            raise ValueError(msg)

    # See if region is a single region or list
    if isinstance(region, Box):  # Cannot iterate over boxes
        region = [region]
    elif not any(isinstance(reg, (list, set, Box)) for reg in region):
        region = [region]
    region = [_validate_box(reg) for reg in region]

    # In angstroms for packmol.
    overlap *= 10

    # Build the input file and call packmol.
    filled_xyz = _new_xyz_file()

    # List to hold file handles for the temporary compounds
    compound_xyz_list = list()
    try:
        input_text = PACKMOL_HEADER.format(overlap, filled_xyz.name, seed)

        for comp, m_compounds, reg, rotate in zip(compound, n_compounds, region, fix_orientation):
            m_compounds = int(m_compounds)

            compound_xyz = _new_xyz_file()
            compound_xyz_list.append(compound_xyz)

            comp.save(compound_xyz.name, overwrite=True)
            reg_mins = reg.mins * 10
            reg_maxs = reg.maxs * 10
            reg_maxs -= edge * 10  # Apply edge buffer
            input_text += PACKMOL_BOX.format(compound_xyz.name, m_compounds,
                                             reg_mins[0], reg_mins[1],
                                             reg_mins[2], reg_maxs[0],
                                             reg_maxs[1], reg_maxs[2],
                                            PACKMOL_CONSTRAIN if rotate else "")

        _run_packmol(input_text, filled_xyz, temp_file)

        # Create the topology and update the coordinates.
        filled = Compound()
        filled = _create_topology(filled, compound, n_compounds)
        filled.update_coordinates(filled_xyz.name)
    finally:
        for file_handle in compound_xyz_list:
            file_handle.close()
            os.unlink(file_handle.name)
        filled_xyz.close()
        os.unlink(filled_xyz.name)
    return filled
コード例 #16
0
ファイル: packing.py プロジェクト: iModels/mbuild
def solvate(solute, solvent, n_solvent, box, overlap=0.2,
            seed=12345, edge=0.2, fix_orientation=False, temp_file=None):
    """Solvate a compound in a box of solvent using packmol.

    Parameters
    ----------
    solute : mb.Compound
        Compound to be placed in a box and solvated.
    solvent : mb.Compound
        Compound to solvate the box.
    n_solvent : int
        Number of solvents to be put in box.
    box : mb.Box
        Box to be filled by compounds.
    overlap : float, units nm, default=0.2
        Minimum separation between atoms of different molecules.
    seed : int, default=12345
        Random seed to be passed to PACKMOL.
    edge : float, units nm, default=0.2
        Buffer at the edge of the box to not place molecules. This is necessary
        in some systems because PACKMOL does not account for periodic boundary
        conditions in its optimization.
    fix_orientation : bool
        Specify if solvent should not be rotated when filling box,
        default=False.
    temp_file : str, default=None
        File name to write PACKMOL's raw output to.

    Returns
    -------
    solvated : mb.Compound

    """
    _check_packmol(PACKMOL)

    box = _validate_box(box)
    if not isinstance(solvent, (list, set)):
        solvent = [solvent]
    if not isinstance(n_solvent, (list, set)):
        n_solvent = [n_solvent]
    if not isinstance(fix_orientation, (list, set)):
        fix_orientation = [fix_orientation] * len(solvent)

    if len(solvent) != len(n_solvent):
        msg = ("`n_solvent` and `n_solvent` must be of equal length.")
        raise ValueError(msg)

    # In angstroms for packmol.
    box_mins = box.mins * 10
    box_maxs = box.maxs * 10
    overlap *= 10
    center_solute = (box_maxs + box_mins) / 2

    # Apply edge buffer
    box_maxs -= edge * 10

    # Build the input file for each compound and call packmol.
    solvated_xyz = _new_xyz_file()
    solute_xyz = _new_xyz_file()

    # generate list of temp files for the solvents
    solvent_xyz_list = list()
    try:
        solute.save(solute_xyz.name, overwrite=True)
        input_text = (PACKMOL_HEADER.format(overlap, solvated_xyz.name, seed) +
                      PACKMOL_SOLUTE.format(solute_xyz.name, *center_solute))

        for solv, m_solvent, rotate in zip(solvent, n_solvent, fix_orientation):
            m_solvent = int(m_solvent)

            solvent_xyz = _new_xyz_file()
            solvent_xyz_list.append(solvent_xyz)

            solv.save(solvent_xyz.name, overwrite=True)
            input_text += PACKMOL_BOX.format(solvent_xyz.name, m_solvent,
                                             box_mins[0], box_mins[1],
                                             box_mins[2], box_maxs[0],
                                             box_maxs[1], box_maxs[2],
                                             PACKMOL_CONSTRAIN if rotate else "")
        _run_packmol(input_text, solvated_xyz, temp_file)

        # Create the topology and update the coordinates.
        solvated = Compound()
        solvated.add(solute)
        solvated = _create_topology(solvated, solvent, n_solvent)
        solvated.update_coordinates(solvated_xyz.name)

    finally:
        for file_handle in solvent_xyz_list:
            file_handle.close()
            os.unlink(file_handle.name)
        solvated_xyz.close()
        solute_xyz.close()
        os.unlink(solvated_xyz.name)
        os.unlink(solute_xyz.name)
    return solvated
コード例 #17
0
def fill_region(
    compound,
    n_compounds,
    region,
    overlap=0.2,
    bounds=None,
    seed=12345,
    sidemax=100.0,
    edge=0.2,
    fix_orientation=False,
    temp_file=None,
    update_port_locations=False,
):
    """Fill a region of a box with `mbuild.Compound` (s) using PACKMOL.

    Parameters
    ----------
    compound : mb.Compound or list of mb.Compound
        Compound or list of compounds to fill in region.
    n_compounds : int or list of ints
        Number of compounds to be put in region.
    region : mb.Box or list of mb.Box
        Region to be filled by compounds.
    overlap : float, units nm, default=0.2
        Minimum separation between atoms of different molecules.
    seed : int, default=12345
        Random seed to be passed to PACKMOL.
    sidemax : float, optional, default=100.0
        Needed to build an initial approximation of the molecule distribution in
        PACKMOL. All system coordinates must fit with in +/- sidemax, so
        increase sidemax accordingly to your final box size.
    edge : float, units nm, default=0.2
        Buffer at the edge of the region to not place molecules. This is
        necessary in some systems because PACKMOL does not account for periodic
        boundary conditions in its optimization.
    fix_orientation : bool or list of bools
        Specify that compounds should not be rotated when filling the box,
        default=False.
    bounds : list-like of floats [minx, miny, minz, maxx, maxy, maxz], units nm, default=None
        Bounding within box to pack compounds, if you want to pack within a bounding
        area that is not the full extent of the region, bounds are required.
    temp_file : str, default=None
        File name to write PACKMOL raw output to.
    update_port_locations : bool, default=False
        After packing, port locations can be updated, but since compounds can be
        rotated, port orientation may be incorrect.

    Returns
    -------
    filled : mb.Compound

    If using mulitple regions and compounds, the nth value in each list are used
    in order.
    For example, the third compound will be put in the third region using the
    third value in n_compounds.
    """
    # check that the user has the PACKMOL binary on their PATH
    _check_packmol(PACKMOL)
    if not isinstance(compound, (list, set)):
        compound = [compound]
    if not isinstance(n_compounds, (list, set)):
        n_compounds = [n_compounds]
    if not isinstance(fix_orientation, (list, set)):
        fix_orientation = [fix_orientation] * len(compound)

    if compound is not None and n_compounds is not None:
        if len(compound) != len(n_compounds):
            raise ValueError(
                "`compound` and `n_compounds` must be of equal length.")
    if compound is not None:
        if len(compound) != len(fix_orientation):
            raise ValueError(
                "`compound`, `n_compounds`, and `fix_orientation` must be of "
                "equal length.")

    # See if region is a single region or list
    my_regions = []
    if isinstance(region, Box):  # Cannot iterate over boxes
        my_regions.append(region)
    # if region is a list of boxes or a list of lists of floats append to my_regions, otherwise the list is expected to be a list of floats
    elif isinstance(region, list):
        for reg in region:
            if isinstance(reg, (list, Box)):
                my_regions.append(reg)
            else:
                raise ValueError(
                    f"list contents expected to be mbuild.Box or list of floats, provided: {type(reg)}"
                )
    else:
        raise ValueError(
            f"expected a list of type: list or mbuild.Box, was provided {region} of type: {type(region)}"
        )
    container = []
    if not bounds:
        bounds = []
    for bound, reg in zip_longest(bounds, my_regions, fillvalue=None):
        if bound is None:
            container.append(reg)
        else:
            container.append(bound)
    container = [_validate_box(bounding) for bounding in container]

    # In angstroms for packmol.
    overlap *= 10

    # Build the input file and call packmol.
    filled_xyz = _new_xyz_file()

    # List to hold file handles for the temporary compounds
    compound_xyz_list = list()
    try:
        input_text = PACKMOL_HEADER.format(overlap, filled_xyz.name, seed,
                                           sidemax * 10)
        for comp, m_compounds, rotate, items_n in zip(compound, n_compounds,
                                                      fix_orientation,
                                                      container):
            m_compounds = int(m_compounds)

            compound_xyz = _new_xyz_file()
            compound_xyz_list.append(compound_xyz)

            comp.save(compound_xyz.name, overwrite=True)
            # TODO how to handle these mins and maxs of this system
            # box should not have any idea of mins and maxs
            my_min = items_n[1]
            my_max = items_n[2]
            reg_mins = np.asarray(my_min) * 10.0
            reg_maxs = np.asarray(my_max) * 10.0

            reg_maxs -= edge * 10  # Apply edge buffer
            input_text += PACKMOL_BOX.format(
                compound_xyz.name,
                m_compounds,
                reg_mins[0],
                reg_mins[1],
                reg_mins[2],
                reg_maxs[0],
                reg_maxs[1],
                reg_maxs[2],
                PACKMOL_CONSTRAIN if rotate else "",
            )

        _run_packmol(input_text, filled_xyz, temp_file)

        # Create the topology and update the coordinates.
        filled = Compound()
        filled = _create_topology(filled, compound, n_compounds)
        filled.update_coordinates(filled_xyz.name,
                                  update_port_locations=update_port_locations)
    finally:
        for file_handle in compound_xyz_list:
            file_handle.close()
            os.unlink(file_handle.name)
        filled_xyz.close()
        os.unlink(filled_xyz.name)
    return filled
コード例 #18
0
def reverse_map(coarse_grained,
                mapping_moieties,
                target=None,
                solvent_name=None,
                sol_per_bead=4,
                sol_cutoff=2,
                scaling_factor=5,
                parallel=True):
    """ Reverse map an mb.Compound

    Parameters
    ---------
    coarse_grained : mb.Compound
        original structure. Generated from a MDTraj trajectory
    mapping_moieties : dictionary
        Relate CG molecule names to a list of finer-detailed mbuild
        Compounds. Care must be taken that bead indices match with
        list indices.
    target_structure : dictionary
        mb.Compound, optional, default=False
        A target atomistic structure which can be used to reconstruct
        bonding.
        Bond network in the reverse-mapped structure will be completely
        overridden by the bond network from the target atomistic structure
        Care must be taken that atom indices match perfectly

    """

    aa_system = Compound()

    not_solvent = [
        mol for mol in coarse_grained.children if mol.name != solvent_name
    ]
    is_solvent = [
        mol for mol in coarse_grained.children if mol.name == solvent_name
    ]

    print(
        "There are {} non-solvent molecules and {} solvent molecules.".format(
            len(not_solvent), len(is_solvent)))

    # For each bead, replace it with the appropriate mb compound
    # Iterate through each molecule (set of particles that are bonded together)
    if parallel:
        pool = mp.Pool(processes=mp.cpu_count())

        # get the solvent molecules mapped in parallel
        inp = zip(is_solvent, [target[solvent_name]] * len(is_solvent),
                  [sol_per_bead] * len(is_solvent),
                  [sol_cutoff] * len(is_solvent))
        chunksize = int(len(is_solvent) / mp.cpu_count()) + 1
        solvent_list = pool.starmap(reverse_map_solvent, inp, chunksize)
        # name the solvents

        # get the non_solvent molecules mapped in parallel
        inp = zip(not_solvent, [target] * len(not_solvent),
                  [mapping_moieties] * len(not_solvent))
        chunksize = int(len(not_solvent) / mp.cpu_count()) + 1
        molecule_list = pool.starmap(reverse_map_molecule, inp, chunksize)

        # put put solvents in one list
        solvent_molecule_list = []
        for i in solvent_list:
            solvent_molecule_list += i

        # put lipids in a box and get the box size
        for molecule in molecule_list:
            aa_system.add(molecule)

        print(aa_system.boundingbox)

        # put everything in a box
        for molecule in solvent_molecule_list:
            aa_system.add(molecule)

    else:
        [
            aa_system.add(
                reverse_map_molecule(molecule, target, mapping_moieties))
            for molecule in not_solvent
        ]
        solvent_compound = reverse_map_solvent(is_solvent,
                                               target[solvent_name],
                                               sol_per_bead, sol_cutoff)
        [aa_system.add(molecule) for molecule in solvent_compound.children]

    return aa_system
コード例 #19
0
def fill_region(compound, n_compounds, region, overlap=0.2, seed=12345):
    """Fill a region of a box with a compound using packmol.

    Parameters
    ----------
    compound : mb.Compound or list of mb.Compound
    n_compounds : int or list of int
    region : mb.Box or list of mb.Box
    overlap : float

    Returns
    -------
    filled : mb.Compound

    If using mulitple regions and compounds, the nth value in each list are used in order.
    For example, if the third compound will be put in the third region using the third value in n_compounds.
    """
    if not PACKMOL:
        msg = "Packmol not found."
        if sys.platform.startswith("win"):
            msg = (msg +
                   " If packmol is already installed, make sure that the "
                   "packmol.exe is on the path.")
        raise IOError(msg)

    if not isinstance(compound, (list, set)):
        compound = [compound]
    if not isinstance(n_compounds, (list, set)):
        n_compounds = [n_compounds]
    # See if region is a single region or list
    if isinstance(region, Box):  # Cannot iterate over boxes
        region = [region]
    elif not any(isinstance(reg, (list, set, Box)) for reg in region):
        region = [region]
    region = [_validate_box(reg) for reg in region]

    # In angstroms for packmol.
    overlap *= 10

    # Build the input file and call packmol.
    filled_pdb = tempfile.mkstemp(suffix='.pdb')[1]
    input_text = PACKMOL_HEADER.format(overlap, filled_pdb, seed)

    for comp, m_compounds, reg in zip(compound, n_compounds, region):
        m_compounds = int(m_compounds)
        compound_pdb = tempfile.mkstemp(suffix='.pdb')[1]
        comp.save(compound_pdb, overwrite=True)
        reg_mins = reg.mins * 10
        reg_maxs = reg.maxs * 10
        input_text += PACKMOL_BOX.format(compound_pdb, m_compounds,
                                         reg_mins[0], reg_mins[1], reg_mins[2],
                                         reg_maxs[0], reg_maxs[1], reg_maxs[2])

    proc = Popen(PACKMOL,
                 stdin=PIPE,
                 stdout=PIPE,
                 stderr=PIPE,
                 universal_newlines=True)
    out, err = proc.communicate(input=input_text)
    if err:
        _packmol_error(out, err)

    # Create the topology and update the coordinates.
    filled = Compound()
    for comp, m_compounds in zip(compound, n_compounds):
        for _ in range(m_compounds):
            filled.add(clone(comp))
    filled.update_coordinates(filled_pdb)
    return filled
コード例 #20
0
ファイル: packing.py プロジェクト: ctk3b/mbuild
def fill_region(compound, n_compounds, region, overlap=0.2, seed=12345):
    """Fill a region of a box with a compound using packmol.

    Parameters
    ----------
    compound : mb.Compound or list of mb.Compound
    n_compounds : int or list of int
    region : mb.Box or list of mb.Box
    overlap : float

    Returns
    -------
    filled : mb.Compound

    If using mulitple regions and compounds, the nth value in each list are used in order.
    For example, if the third compound will be put in the third region using the third value in n_compounds.
    """
    if not PACKMOL:
        msg = "Packmol not found."
        if sys.platform.startswith("win"):
            msg = (msg + " If packmol is already installed, make sure that the "
                         "packmol.exe is on the path.")
        raise IOError(msg)

    if not isinstance(compound, (list, set)):
        compound = [compound]
    if not isinstance(n_compounds, (list, set)):
        n_compounds = [n_compounds]
    # See if region is a single region or list
    if isinstance(region, Box): # Cannot iterate over boxes
        region = [region]
    elif not any(isinstance(reg, (list, set, Box)) for reg in region):
        region = [region]
    region = [_validate_box(reg) for reg in region]

    # In angstroms for packmol.
    overlap *= 10

    # Build the input file and call packmol.
    filled_pdb = tempfile.mkstemp(suffix='.pdb')[1]
    input_text = PACKMOL_HEADER.format(overlap, filled_pdb, seed)

    for comp, m_compounds, reg in zip(compound, n_compounds, region):
        m_compounds = int(m_compounds)
        compound_pdb = tempfile.mkstemp(suffix='.pdb')[1]
        comp.save(compound_pdb, overwrite=True)
        reg_mins = reg.mins * 10
        reg_maxs = reg.maxs * 10
        input_text += PACKMOL_BOX.format(compound_pdb, m_compounds,
                                        reg_mins[0], reg_mins[1], reg_mins[2],
                                        reg_maxs[0], reg_maxs[1], reg_maxs[2])

    proc = Popen(PACKMOL, stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True)
    out, err = proc.communicate(input=input_text)
    if err:
        _packmol_error(out, err)

    # Create the topology and update the coordinates.
    filled = Compound()
    for comp, m_compounds in zip(compound, n_compounds):
        for _ in range(m_compounds):
            filled.add(clone(comp))
    filled.update_coordinates(filled_pdb)
    return filled
コード例 #21
0
def fill_region(compound,
                n_compounds,
                region,
                overlap=0.2,
                seed=12345,
                edge=0.2,
                fix_orientation=False,
                temp_file=None):
    """Fill a region of a box with a compound using packmol.

    Parameters
    ----------
    compound : mb.Compound or list of mb.Compound
        Compound or list of compounds to be put in region.
    n_compounds : int or list of int
        Number of compounds to be put in region.
    region : mb.Box or list of mb.Box
        Region to be filled by compounds.
    overlap : float, units nm, default=0.2
        Minimum separation between atoms of different molecules.
    seed : int, default=12345
        Random seed to be passed to PACKMOL.
    edge : float, units nm, default=0.2
        Buffer at the edge of the region to not place molecules. This is
        necessary in some systems because PACKMOL does not account for
        periodic boundary conditions in its optimization.
    fix_orientation : bool or list of bools
        Specify that compounds should not be rotated when filling the box,
        default=False.
    temp_file : str, default=None
        File name to write PACKMOL's raw output to.

    Returns
    -------
    filled : mb.Compound

    If using mulitple regions and compounds, the nth value in each list are used in order.
    For example, if the third compound will be put in the third region using the third value in n_compounds.
    """
    _check_packmol(PACKMOL)

    if not isinstance(compound, (list, set)):
        compound = [compound]
    if not isinstance(n_compounds, (list, set)):
        n_compounds = [n_compounds]
    if not isinstance(fix_orientation, (list, set)):
        fix_orientation = [fix_orientation] * len(compound)

    if compound is not None and n_compounds is not None:
        if len(compound) != len(n_compounds):
            msg = ("`compound` and `n_compounds` must be of equal length.")
            raise ValueError(msg)
    if compound is not None:
        if len(compound) != len(fix_orientation):
            msg = (
                "`compound`, `n_compounds`, and `fix_orientation` must be of equal length."
            )
            raise ValueError(msg)

    # See if region is a single region or list
    if isinstance(region, Box):  # Cannot iterate over boxes
        region = [region]
    elif not any(isinstance(reg, (list, set, Box)) for reg in region):
        region = [region]
    region = [_validate_box(reg) for reg in region]

    # In angstroms for packmol.
    overlap *= 10

    # Build the input file and call packmol.
    filled_pdb = tempfile.mkstemp(suffix='.pdb')[1]
    input_text = PACKMOL_HEADER.format(overlap, filled_pdb, seed)

    for comp, m_compounds, reg, rotate in zip(compound, n_compounds, region,
                                              fix_orientation):
        m_compounds = int(m_compounds)
        compound_pdb = tempfile.mkstemp(suffix='.pdb')[1]
        comp.save(compound_pdb, overwrite=True)
        reg_mins = reg.mins * 10
        reg_maxs = reg.maxs * 10
        reg_maxs -= edge * 10  # Apply edge buffer
        input_text += PACKMOL_BOX.format(compound_pdb, m_compounds,
                                         reg_mins[0], reg_mins[1], reg_mins[2],
                                         reg_maxs[0], reg_maxs[1], reg_maxs[2],
                                         PACKMOL_CONSTRAIN if rotate else "")

    _run_packmol(input_text, filled_pdb, temp_file)

    # Create the topology and update the coordinates.
    filled = Compound()
    for comp, m_compounds in zip(compound, n_compounds):
        for _ in range(m_compounds):
            filled.add(clone(comp))
    filled.update_coordinates(filled_pdb)
    return filled
コード例 #22
0
def fill_box(
    compound,
    n_compounds=None,
    box=None,
    density=None,
    overlap=0.2,
    seed=12345,
    sidemax=100.0,
    edge=0.2,
    compound_ratio=None,
    aspect_ratio=None,
    fix_orientation=False,
    temp_file=None,
    update_port_locations=False,
):
    """Fill a box with an `mbuild.compound` or `Compound` s using PACKMOL.

    `fill_box` takes a single `Compound` or a list of `Compound` s and
    returns a `Compound` that has been filled to specification by PACKMOL.

    When filling a system, two arguments of `n_compounds` , `box` , and
    `density` must be specified.

    If `n_compounds` and `box` are not None, the specified number of
    compounds will be inserted into a box of the specified size.

    If `n_compounds` and `density` are not None, the corresponding box size
    will be calculated internally. In this case, `n_compounds` must be an int
    and not a list of int.

    If `box` and `density` are not None, the corresponding number of
    compounds will be calculated internally.

    For the cases in which `box` is not specified but generated internally,
    the default behavior is to calculate a cubic box. Optionally,
    `aspect_ratio` can be passed to generate a non-cubic box.

    Parameters
    ----------
    compound : mb.Compound or list of mb.Compound
        Compound or list of compounds to fill in box.
    n_compounds : int or list of int
        Number of compounds to be filled in box.
    box : mb.Box
        Box to be filled by compounds.
    density : float, units :math:`kg/m^3` , default=None
        Target density for the system in macroscale units. If not None, one of
        `n_compounds` or `box` , but not both, must be specified.
    overlap : float, units nm, default=0.2
        Minimum separation between atoms of different molecules.
    seed : int, default=12345
        Random seed to be passed to PACKMOL.
    sidemax : float, optional, default=100.0
        Needed to build an initial approximation of the molecule distribution in
        PACKMOL. All system coordinates must fit with in +/- sidemax, so
        increase sidemax accordingly to your final box size.
    edge : float, units nm, default=0.2
        Buffer at the edge of the box to not place molecules. This is necessary
        in some systems because PACKMOL does not account for periodic boundary
        conditions in its optimization.
    compound_ratio : list, default=None
        Ratio of number of each compound to be put in box. Only used in the case
        of `density` and `box` having been specified, `n_compounds` not
        specified, and more than one `compound` .
    aspect_ratio : list of float
        If a non-cubic box is desired, the ratio of box lengths in the x, y, and
        z directions.
    fix_orientation : bool or list of bools
        Specify that compounds should not be rotated when filling the box,
        default=False.
    temp_file : str, default=None
        File name to write PACKMOL raw output to.
    update_port_locations : bool, default=False
        After packing, port locations can be updated, but since compounds can be
        rotated, port orientation may be incorrect.

    Returns
    -------
    filled : mb.Compound
    """
    # check that the user has the PACKMOL binary on their PATH
    _check_packmol(PACKMOL)

    arg_count = 3 - [n_compounds, box, density].count(None)
    if arg_count != 2:
        raise ValueError(
            "Exactly 2 of `n_compounds`, `box`, and `density` must be "
            "specified. {} were given.".format(arg_count))

    if box is not None:
        (box, my_mins, my_maxs) = _validate_box(box)
    if not isinstance(compound, (list, set)):
        compound = [compound]
    if n_compounds is not None and not isinstance(n_compounds, (list, set)):
        n_compounds = [n_compounds]
    if not isinstance(fix_orientation, (list, set)):
        fix_orientation = [fix_orientation] * len(compound)

    if compound is not None and n_compounds is not None:
        if len(compound) != len(n_compounds):
            raise ValueError(
                "`compound` and `n_compounds` must be of equal length.")

    if compound is not None:
        if len(compound) != len(fix_orientation):
            raise ValueError(
                "`compound`, `n_compounds`, and `fix_orientation` must be of "
                "equal length.")

    if density is not None:
        if box is None and n_compounds is not None:
            total_mass = np.sum([
                n * np.sum([a.mass for a in c.to_parmed().atoms])
                for c, n in zip(compound, n_compounds)
            ])
            # Conversion from (amu/(kg/m^3))**(1/3) to nm
            L = (total_mass / density)**(1 / 3) * 1.1841763
            if aspect_ratio is None:
                (box, my_mins, my_maxs) = _validate_box(
                    Box(lengths=[L, L, L], angles=[90.0, 90.0, 90.0]))
            else:
                L *= np.prod(aspect_ratio)**(-1 / 3)
                (box, my_mins,
                 my_maxs) = _validate_box([val * L for val in aspect_ratio])
        if n_compounds is None and box is not None:
            if len(compound) == 1:
                compound_mass = np.sum(
                    [a.mass for a in compound[0].to_parmed().atoms])
                # Conversion from kg/m^3 / amu * nm^3 to dimensionless units
                n_compounds = [
                    int(density / compound_mass *
                        np.prod(np.asarray(box.lengths)) * 0.60224)
                ]
            else:
                if compound_ratio is None:
                    raise ValueError(
                        "Determing `n_compounds` from `density` and `box` for "
                        "systems with more than one compound type requires "
                        "`compound_ratio`")
                if len(compound) != len(compound_ratio):
                    raise ValueError(
                        "Length of `compound_ratio` must equal length of "
                        "`compound`")
                prototype_mass = 0
                for c, r in zip(compound, compound_ratio):
                    prototype_mass += r * np.sum(
                        [a.mass for a in c.to_parmed().atoms])
                # Conversion from kg/m^3 / amu * nm^3 to dimensionless units
                n_prototypes = int(density / prototype_mass *
                                   np.prod(np.asarray(box.lengths)) * 0.60224)
                n_compounds = list()
                for c in compound_ratio:
                    n_compounds.append(int(n_prototypes * c))

    # Convert nm to angstroms for PACKMOL.
    # TODO how to handle box starts and ends
    # not really a thing a box would know?
    box_mins = np.asarray(my_mins) * 10
    box_maxs = np.asarray(my_maxs) * 10
    overlap *= 10

    # Apply 0.2nm edge buffer
    box_maxs = [a_max - (edge * 10) for a_max in box_maxs]
    box_mins = [a_min + (edge * 10) for a_min in box_mins]

    # Build the input file for each compound and call packmol.
    filled_xyz = _new_xyz_file()

    # create a list to contain the file handles for the compound temp files
    compound_xyz_list = list()
    try:
        input_text = PACKMOL_HEADER.format(overlap, filled_xyz.name, seed,
                                           sidemax * 10)
        for comp, m_compounds, rotate in zip(compound, n_compounds,
                                             fix_orientation):
            m_compounds = int(m_compounds)

            compound_xyz = _new_xyz_file()
            compound_xyz_list.append(compound_xyz)

            comp.save(compound_xyz.name, overwrite=True)
            input_text += PACKMOL_BOX.format(
                compound_xyz.name,
                m_compounds,
                box_mins[0],
                box_mins[1],
                box_mins[2],
                box_maxs[0],
                box_maxs[1],
                box_maxs[2],
                PACKMOL_CONSTRAIN if rotate else "",
            )
        _run_packmol(input_text, filled_xyz, temp_file)
        # Create the topology and update the coordinates.
        filled = Compound()
        filled = _create_topology(filled, compound, n_compounds)
        filled.update_coordinates(filled_xyz.name,
                                  update_port_locations=update_port_locations)
        filled.box = box

    # ensure that the temporary files are removed from the machine after filling
    finally:
        for file_handle in compound_xyz_list:
            file_handle.close()
            os.unlink(file_handle.name)
        filled_xyz.close()
        os.unlink(filled_xyz.name)
    return filled
コード例 #23
0
def solvate(solute, solvent, n_solvent, box, overlap=0.2, seed=12345):
    """Solvate a compound in a box of solvent using packmol.

    Parameters
    ----------
    solute : mb.Compound
    solvent : mb.Compound
    n_solvent : int
    box : mb.Box
    overlap : float

    Returns
    -------
    solvated : mb.Compound

    """
    if not PACKMOL:
        raise IOError("Packmol not found")

    box = _validate_box(box)
    if not isinstance(solvent, (list, set)):
        solvent = [solvent]
    if not isinstance(n_solvent, (list, set)):
        n_solvent = [n_solvent]

    # In angstroms for packmol.
    box_mins = box.mins * 10
    box_maxs = box.maxs * 10
    overlap *= 10
    center_solute = (box_maxs + box_mins) / 2

    # Build the input file for each compound and call packmol.
    solvated_pdb = tempfile.mkstemp(suffix='.pdb')[1]
    solute_pdb = tempfile.mkstemp(suffix='.pdb')[1]
    solute.save(solute_pdb, overwrite=True)
    input_text = (PACKMOL_HEADER.format(overlap, solvated_pdb, seed) +
                  PACKMOL_SOLUTE.format(solute_pdb, *center_solute))

    for solv, m_solvent in zip(solvent, n_solvent):
        m_solvent = int(m_solvent)
        solvent_pdb = tempfile.mkstemp(suffix='.pdb')[1]
        solv.save(solvent_pdb, overwrite=True)
        input_text += PACKMOL_BOX.format(solvent_pdb, m_solvent, box_mins[0],
                                         box_mins[1], box_mins[2], box_maxs[0],
                                         box_maxs[1], box_maxs[2])

    proc = Popen(PACKMOL,
                 stdin=PIPE,
                 stdout=PIPE,
                 stderr=PIPE,
                 universal_newlines=True)
    out, err = proc.communicate(input=input_text)
    if err:
        _packmol_error(out, err)

    # Create the topology and update the coordinates.
    solvated = Compound()
    solvated.add(solute)
    for solv, m_solvent in zip(solvent, n_solvent):
        for _ in range(m_solvent):
            solvated.add(clone(solv))
    solvated.update_coordinates(solvated_pdb)
    return solvated
コード例 #24
0
ファイル: packing.py プロジェクト: iModels/mbuild
def fill_sphere(compound, sphere, n_compounds=None, density=None, overlap=0.2,
                seed=12345, edge=0.2, compound_ratio=None,
                fix_orientation=False, temp_file=None):
    """Fill a sphere with a compound using packmol.

    One argument of `n_compounds and density` must be specified.

    If `n_compounds` is not None, the specified number of
    n_compounds will be inserted into a sphere of the specified size.

    If `density` is not None, the corresponding number of
    compounds will be calculated internally.

    Parameters
    ----------
    compound : mb.Compound or list of mb.Compound
        Compound or list of compounds to be put in box.
    sphere : list, units nm
        Sphere coordinates in the form [x_center, y_center, z_center, radius]
    n_compounds : int or list of int
        Number of compounds to be put in box.
    density : float, units kg/m^3, default=None
        Target density for the sphere in macroscale units.
    overlap : float, units nm, default=0.2
        Minimum separation between atoms of different molecules.
    seed : int, default=12345
        Random seed to be passed to PACKMOL.
    edge : float, units nm, default=0.2
        Buffer at the edge of the sphere to not place molecules. This is necessary
        in some systems because PACKMOL does not account for periodic boundary
        conditions in its optimization.
    compound_ratio : list, default=None
        Ratio of number of each compound to be put in sphere. Only used in the
        case of `density` having been specified, `n_compounds` not specified, 
        and more than one `compound`.
    fix_orientation : bool or list of bools
        Specify that compounds should not be rotated when filling the sphere,
        default=False.
    temp_file : str, default=None
        File name to write PACKMOL's raw output to.

    Returns
    -------
    filled : mb.Compound

    """
    _check_packmol(PACKMOL)

    arg_count = 2 - [n_compounds, density].count(None)
    if arg_count != 1:
        msg = ("Exactly 1 of `n_compounds` and `density` "
               "must be specified. {} were given.".format(arg_count))
        raise ValueError(msg)

    if isinstance(sphere, (list, set, tuple)):
        if len(sphere) != 4:
            msg = ("`sphere` must be a list of len 4")
    else:
        msg = ("`sphere` must be a list")
        raise ValueError(msg)

    if not isinstance(compound, (list, set)):
        compound = [compound]
    if n_compounds is not None and not isinstance(n_compounds, (list, set)):
        n_compounds = [n_compounds]
    if not isinstance(fix_orientation, (list, set)):
        fix_orientation = [fix_orientation]*len(compound)

    if compound is not None and n_compounds is not None:
        if len(compound) != len(n_compounds):
            msg = ("`compound` and `n_compounds` must be of equal length.")
            raise ValueError(msg)

    if compound is not None:
        if len(compound) != len(fix_orientation):
            msg = ("`compound`, `n_compounds`, and `fix_orientation` must be of equal length.")
            raise ValueError(msg)

    for coord in sphere[:3]:
        if coord < sphere[3]:
            msg = ("`sphere` center coordinates must be greater than radius.")
            raise ValueError(msg)

    # Apply edge buffer
    radius = sphere[3] - edge

    if density is not None:
        if n_compounds is None:
            if len(compound) == 1:
                compound_mass = np.sum([a.mass for a in compound[0].to_parmed().atoms])
                # Conversion from kg/m^3 / amu * nm^3 to dimensionless units
                n_compounds = [int(density/compound_mass*(4/3*np.pi*radius**3)*.60224)]
            else:
                if compound_ratio is None:
                    msg = ("Determing `n_compounds` from `density` "
                           "for systems with more than one compound type requires"
                           "`compound_ratio`")
                    raise ValueError(msg)
                if len(compound) != len(compound_ratio):
                    msg = ("Length of `compound_ratio` must equal length of "
                           "`compound`")
                    raise ValueError(msg)
                prototype_mass = 0
                for c, r in zip(compound, compound_ratio):
                    prototype_mass += r * np.sum([a.mass for a in c.to_parmed().atoms])
                # Conversion from kg/m^3 / amu * nm^3 to dimensionless units
                n_prototypes = int(density/prototype_mass*(4/3*np.pi*radius**3)*.60224)
                n_compounds = list()
                for c in compound_ratio:
                    n_compounds.append(int(n_prototypes * c))

    # In angstroms for packmol.
    sphere = np.multiply(sphere, 10)
    radius *= 10
    overlap *= 10

    # Build the input file for each compound and call packmol.
    filled_xyz = _new_xyz_file()

    # List to hold file handles for the temporary compounds
    compound_xyz_list = list()
    try:
        input_text = PACKMOL_HEADER.format(overlap, filled_xyz.name, seed)
        for comp, m_compounds, rotate in zip(compound, n_compounds, fix_orientation):
            m_compounds = int(m_compounds)

            compound_xyz = _new_xyz_file()
            compound_xyz_list.append(compound_xyz)

            comp.save(compound_xyz.name, overwrite=True)
            input_text += PACKMOL_SPHERE.format(compound_xyz.name, m_compounds,
                                                sphere[0], sphere[1],
                                                sphere[2], radius,
                                                PACKMOL_CONSTRAIN if rotate else "")
        print(input_text)
        _run_packmol(input_text, filled_xyz, temp_file)

        # Create the topology and update the coordinates.
        filled = Compound()
        filled = _create_topology(filled, compound, n_compounds)
        filled.update_coordinates(filled_xyz.name)
    finally:
        for file_handle in compound_xyz_list:
            file_handle.close()
            os.unlink(file_handle.name)
        filled_xyz.close()
        os.unlink(filled_xyz.name)
    return filled
コード例 #25
0
def solvate(
    solute,
    solvent,
    n_solvent,
    box,
    overlap=0.2,
    seed=12345,
    sidemax=100.0,
    edge=0.2,
    fix_orientation=False,
    temp_file=None,
    update_port_locations=False,
):
    """Solvate a compound in a box of solvent using PACKMOL.

    Parameters
    ----------
    solute : mb.Compound
        Compound to be placed in a box and solvated.
    solvent : mb.Compound
        Compound to solvate the box.
    n_solvent : int
        Number of solvents to be put in box.
    box : mb.Box
        Box to be filled by compounds.
    overlap : float, units nm, default=0.2
        Minimum separation between atoms of different molecules.
    seed : int, default=12345
        Random seed to be passed to PACKMOL.
    sidemax : float, optional, default=100.0
        Needed to build an initial approximation of the molecule distribution in
        PACKMOL. All system coordinates must fit with in +/- sidemax, so
        increase sidemax accordingly to your final box size
    edge : float, units nm, default=0.2
        Buffer at the edge of the box to not place molecules. This is necessary
        in some systems because PACKMOL does not account for periodic boundary
        conditions in its optimization.
    fix_orientation : bool
        Specify if solvent should not be rotated when filling box,
        default=False.
    temp_file : str, default=None
        File name to write PACKMOL raw output to.
    update_port_locations : bool, default=False
        After packing, port locations can be updated, but since compounds can be
        rotated, port orientation may be incorrect.

    Returns
    -------
    solvated : mb.Compound
    """
    # check that the user has the PACKMOL binary on their PATH
    _check_packmol(PACKMOL)

    (box, min_tmp, max_tmp) = _validate_box(box)
    if not isinstance(solvent, (list, set)):
        solvent = [solvent]
    if not isinstance(n_solvent, (list, set)):
        n_solvent = [n_solvent]
    if not isinstance(fix_orientation, (list, set)):
        fix_orientation = [fix_orientation] * len(solvent)

    if len(solvent) != len(n_solvent):
        raise ValueError(
            "`n_solvent` and `n_solvent` must be of equal length.")

    # In angstroms for packmol.
    box_mins = np.asarray(min_tmp) * 10
    box_maxs = np.asarray(max_tmp) * 10
    overlap *= 10
    center_solute = (box_maxs + box_mins) / 2

    # Apply edge buffer
    box_maxs = np.subtract(box_maxs, edge * 10)
    box_mins = np.add(box_mins, edge * 10)
    # Build the input file for each compound and call packmol.
    solvated_xyz = _new_xyz_file()
    solute_xyz = _new_xyz_file()

    # generate list of temp files for the solvents
    solvent_xyz_list = list()
    try:
        solute.save(solute_xyz.name, overwrite=True)
        input_text = PACKMOL_HEADER.format(
            overlap, solvated_xyz.name, seed,
            sidemax * 10) + PACKMOL_SOLUTE.format(solute_xyz.name,
                                                  *center_solute.tolist())

        for (solv, m_solvent, rotate) in zip(solvent, n_solvent,
                                             fix_orientation):
            m_solvent = int(m_solvent)
            solvent_xyz = _new_xyz_file()
            solvent_xyz_list.append(solvent_xyz)

            solv.save(solvent_xyz.name, overwrite=True)
            input_text += PACKMOL_BOX.format(
                solvent_xyz.name,
                m_solvent,
                box_mins[0],
                box_mins[1],
                box_mins[2],
                box_maxs[0],
                box_maxs[1],
                box_maxs[2],
                PACKMOL_CONSTRAIN if rotate else "",
            )
        _run_packmol(input_text, solvated_xyz, temp_file)

        # Create the topology and update the coordinates.
        solvated = Compound()
        solvated.add(solute)
        solvated = _create_topology(solvated, solvent, n_solvent)
        solvated.update_coordinates(
            solvated_xyz.name, update_port_locations=update_port_locations)

    finally:
        for file_handle in solvent_xyz_list:
            file_handle.close()
            os.unlink(file_handle.name)
        solvated_xyz.close()
        solute_xyz.close()
        os.unlink(solvated_xyz.name)
        os.unlink(solute_xyz.name)
    return solvated
コード例 #26
0
ファイル: packing.py プロジェクト: iModels/mbuild
def fill_box(compound, n_compounds=None, box=None, density=None, overlap=0.2,
             seed=12345, edge=0.2, compound_ratio=None,
             aspect_ratio=None, fix_orientation=False, temp_file=None):
    """Fill a box with a compound using packmol.

    Two arguments of `n_compounds, box, and density` must be specified.

    If `n_compounds` and `box` are not None, the specified number of
    n_compounds will be inserted into a box of the specified size.

    If `n_compounds` and `density` are not None, the corresponding box
    size will be calculated internally. In this case, `n_compounds`
    must be an int and not a list of int.

    If `box` and `density` are not None, the corresponding number of
    compounds will be calculated internally.

    For the cases in which `box` is not specified but generated internally,
    the default behavior is to calculate a cubic box. Optionally,
    `aspect_ratio` can be passed to generate a non-cubic box.

    Parameters
    ----------
    compound : mb.Compound or list of mb.Compound
        Compound or list of compounds to be put in box.
    n_compounds : int or list of int
        Number of compounds to be put in box.
    box : mb.Box
        Box to be filled by compounds.
    density : float, units kg/m^3, default=None
        Target density for the system in macroscale units. If not None, one of
        `n_compounds` or `box`, but not both, must be specified.
    overlap : float, units nm, default=0.2
        Minimum separation between atoms of different molecules.
    seed : int, default=12345
        Random seed to be passed to PACKMOL.
    edge : float, units nm, default=0.2
        Buffer at the edge of the box to not place molecules. This is necessary
        in some systems because PACKMOL does not account for periodic boundary
        conditions in its optimization.
    compound_ratio : list, default=None
        Ratio of number of each compound to be put in box. Only used in the
        case of `density` and `box` having been specified, `n_compounds` not
        specified, and more than one `compound`.
    aspect_ratio : list of float
        If a non-cubic box is desired, the ratio of box lengths in the x, y,
        and z directions.
    fix_orientation : bool or list of bools
        Specify that compounds should not be rotated when filling the box,
        default=False.
    temp_file : str, default=None
        File name to write PACKMOL's raw output to.

    Returns
    -------
    filled : mb.Compound

    """
    _check_packmol(PACKMOL)

    arg_count = 3 - [n_compounds, box, density].count(None)
    if arg_count != 2:
        msg = ("Exactly 2 of `n_compounds`, `box`, and `density` "
               "must be specified. {} were given.".format(arg_count))
        raise ValueError(msg)

    if box is not None:
        box = _validate_box(box)
    if not isinstance(compound, (list, set)):
        compound = [compound]
    if n_compounds is not None and not isinstance(n_compounds, (list, set)):
        n_compounds = [n_compounds]
    if not isinstance(fix_orientation, (list, set)):
        fix_orientation = [fix_orientation]*len(compound)

    if compound is not None and n_compounds is not None:
        if len(compound) != len(n_compounds):
            msg = ("`compound` and `n_compounds` must be of equal length.")
            raise ValueError(msg)

    if compound is not None:
        if len(compound) != len(fix_orientation):
            msg = ("`compound`, `n_compounds`, and `fix_orientation` must be of equal length.")
            raise ValueError(msg)

    if density is not None:
        if box is None and n_compounds is not None:
            total_mass = np.sum([n*np.sum([a.mass for a in c.to_parmed().atoms])
                                for c, n in zip(compound, n_compounds)])
            # Conversion from (amu/(kg/m^3))**(1/3) to nm
            L = (total_mass/density)**(1/3)*1.1841763
            if aspect_ratio is None:
                box = _validate_box(Box(3*[L]))
            else:
                L *= np.prod(aspect_ratio) ** (-1/3)
                box = _validate_box(Box([val*L for val in aspect_ratio]))
        if n_compounds is None and box is not None:
            if len(compound) == 1:
                compound_mass = np.sum([a.mass for a in compound[0].to_parmed().atoms])
                # Conversion from kg/m^3 / amu * nm^3 to dimensionless units
                n_compounds = [int(density/compound_mass*np.prod(box.lengths)*.60224)]
            else:
                if compound_ratio is None:
                    msg = ("Determing `n_compounds` from `density` and `box` "
                           "for systems with more than one compound type requires"
                           "`compound_ratio`")
                    raise ValueError(msg)
                if len(compound) != len(compound_ratio):
                    msg = ("Length of `compound_ratio` must equal length of "
                           "`compound`")
                    raise ValueError(msg)
                prototype_mass = 0
                for c, r in zip(compound, compound_ratio):
                    prototype_mass += r * np.sum([a.mass for a in c.to_parmed().atoms])
                # Conversion from kg/m^3 / amu * nm^3 to dimensionless units
                n_prototypes = int(density/prototype_mass*np.prod(box.lengths)*.60224)
                n_compounds = list()
                for c in compound_ratio:
                    n_compounds.append(int(n_prototypes * c))

    # In angstroms for packmol.
    box_mins = box.mins * 10
    box_maxs = box.maxs * 10
    overlap *= 10

    # Apply edge buffer
    box_maxs -= edge * 10

    # Build the input file for each compound and call packmol.
    filled_xyz = _new_xyz_file()

    # create a list to contain the file handles for the compound temp files
    compound_xyz_list = list()
    try:
        input_text = PACKMOL_HEADER.format(overlap, filled_xyz.name, seed)
        for comp, m_compounds, rotate in zip(compound, n_compounds, fix_orientation):
            m_compounds = int(m_compounds)

            compound_xyz = _new_xyz_file()
            compound_xyz_list.append(compound_xyz)

            comp.save(compound_xyz.name, overwrite=True)
            input_text += PACKMOL_BOX.format(compound_xyz.name, m_compounds,
                                             box_mins[0], box_mins[1],
                                             box_mins[2], box_maxs[0],
                                             box_maxs[1], box_maxs[2],
                                             PACKMOL_CONSTRAIN if rotate else "")

        _run_packmol(input_text, filled_xyz, temp_file)
        # Create the topology and update the coordinates.
        filled = Compound()
        filled = _create_topology(filled, compound, n_compounds)
        filled.update_coordinates(filled_xyz.name)
        filled.periodicity = np.asarray(box.lengths, dtype=np.float32)

    finally:
        for file_handle in compound_xyz_list:
            file_handle.close()
            os.unlink(file_handle.name)
        filled_xyz.close()
        os.unlink(filled_xyz.name)
    return filled
コード例 #27
0
def fill_box(compound,
             n_compounds=None,
             box=None,
             density=None,
             overlap=0.2,
             seed=12345):
    """Fill a box with a compound using packmol.

    Two arguments of `n_compounds, box, and density` must be specified.

    If `n_compounds` and `box` are not None, the specified number of
    n_compounds will be inserted into a box of the specified size.

    If `n_compounds` and `density` are not None, the corresponding box
    size will be calculated internally. In this case, `n_compounds`
    must be an int and not a list of int.

    If `box` and `density` are not None, the corresponding number of
    compounds will be calculated internally.

    Parameters
    ----------
    compound : mb.Compound or list of mb.Compound
    n_compounds : int or list of int
    box : mb.Box
    overlap : float, units nm
    density : float, units kg/m^3

    Returns
    -------
    filled : mb.Compound
    
    TODO : Support aspect ratios in generated boxes
    TODO : Support ratios of n_compounds
    """
    if not PACKMOL:
        msg = "Packmol not found."
        if sys.platform.startswith("win"):
            msg = (msg +
                   " If packmol is already installed, make sure that the "
                   "packmol.exe is on the path.")
        raise IOError(msg)

    arg_count = 3 - [n_compounds, box, density].count(None)
    if arg_count != 2:
        msg = ("Exactly 2 of `n_compounds`, `box`, and `density` "
               "must be specified. {} were given.".format(arg_count))
        raise ValueError(msg)

    if box is not None:
        box = _validate_box(box)
    if not isinstance(compound, (list, set)):
        compound = [compound]
    if n_compounds is not None and not isinstance(n_compounds, (list, set)):
        n_compounds = [n_compounds]

    if density is not None:
        if box is None and n_compounds is not None:
            total_mass = np.sum([
                n * np.sum([a.mass for a in c.to_parmed().atoms])
                for c, n in zip(compound, n_compounds)
            ])
            # Conversion from (amu/(kg/m^3))**(1/3) to nm
            L = (total_mass / density)**(1 / 3) * 1.1841763
            box = _validate_box(Box(3 * [L]))
        if n_compounds is None and box is not None:
            if len(compound) > 1:
                msg = ("Determing `n_compounds` from `density` and `box` "
                       "currently only supported for systems with one "
                       "compound type.")
                raise ValueError(msg)
            else:
                compound_mass = np.sum(
                    [a.mass for a in compound[0].to_parmed().atoms])
                # Conversion from kg/m^3 / amu * nm^3 to dimensionless units
                n_compounds = [
                    int(density / compound_mass * np.prod(box.lengths) *
                        .60224)
                ]

    # In angstroms for packmol.
    box_mins = box.mins * 10
    box_maxs = box.maxs * 10
    overlap *= 10

    # Build the input file for each compound and call packmol.
    filled_pdb = tempfile.mkstemp(suffix='.pdb')[1]
    input_text = PACKMOL_HEADER.format(overlap, filled_pdb, seed)

    for comp, m_compounds in zip(compound, n_compounds):
        m_compounds = int(m_compounds)
        compound_pdb = tempfile.mkstemp(suffix='.pdb')[1]
        comp.save(compound_pdb, overwrite=True)
        input_text += PACKMOL_BOX.format(compound_pdb, m_compounds,
                                         box_mins[0], box_mins[1], box_mins[2],
                                         box_maxs[0], box_maxs[1], box_maxs[2])

    proc = Popen(PACKMOL,
                 stdin=PIPE,
                 stdout=PIPE,
                 stderr=PIPE,
                 universal_newlines=True)
    out, err = proc.communicate(input=input_text)
    if err:
        _packmol_error(out, err)

    # Create the topology and update the coordinates.
    filled = Compound()
    for comp, m_compounds in zip(compound, n_compounds):
        for _ in range(m_compounds):
            filled.add(clone(comp))
    filled.update_coordinates(filled_pdb)
    filled.periodicity = np.asarray(box.lengths, dtype=np.float32)
    return filled