Exemplo n.º 1
0
def from_snapshot(snapshot, scale=1.0):
    """Convert a Snapshot to a Compound.

    Snapshot can be a hoomd.data.Snapshot or a gsd.hoomd.Snapshot.

    Parameters
    ----------
    snapshot : hoomd._hoomd.SnapshotSystemData_float or gsd.hoomd.Snapshot
        Snapshot from which to build the mbuild Compound.
    scale : float, optional, default 1.0
        Value by which to scale the length values

    Returns
    -------
    comp : Compound

    Note
    ----
    GSD and HOOMD snapshots center their boxes on the origin (0,0,0), so the
    compound is shifted by half the box lengths
    """
    comp = Compound()
    bond_array = snapshot.bonds.group
    n_atoms = snapshot.particles.N

    if "SnapshotSystemData_float" in dir(hoomd._hoomd) and isinstance(
        snapshot, hoomd._hoomd.SnapshotSystemData_float
    ):
        # hoomd v2
        box = snapshot.box
        comp.box = Box.from_lengths_tilt_factors(
            lengths=np.array([box.Lx, box.Ly, box.Lz]) * scale,
            tilt_factors=np.array([box.xy, box.xz, box.yz]),
        )
    else:
        # gsd / hoomd v3
        box = snapshot.configuration.box
        comp.box = Box.from_lengths_tilt_factors(
            lengths=box[:3] * scale, tilt_factors=box[3:]
        )

    # GSD and HOOMD snapshots center their boxes on the origin (0,0,0)
    shift = np.array(comp.box.lengths) / 2
    # Add particles
    for i in range(n_atoms):
        name = snapshot.particles.types[snapshot.particles.typeid[i]]
        xyz = snapshot.particles.position[i] * scale + shift
        charge = snapshot.particles.charge[i]

        atom = Particle(name=name, pos=xyz, charge=charge)
        comp.add(atom, label=str(i))

    # Add bonds
    particle_dict = {idx: p for idx, p in enumerate(comp.particles())}
    for i in range(bond_array.shape[0]):
        atom1 = int(bond_array[i][0])
        atom2 = int(bond_array[i][1])
        comp.add_bond([particle_dict[atom1], particle_dict[atom2]])
    return comp
Exemplo n.º 2
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
Exemplo n.º 3
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)

    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_mins = box.mins * 10
    box_maxs = box.maxs * 10
    overlap *= 10
    center_solute = (box_maxs + box_mins) / 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_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 _ in range(n_solvent):
        solvated.add(clone(solvent))
    solvated.update_coordinates(solvated_pdb)
    return solvated
Exemplo n.º 4
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
Exemplo n.º 5
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
    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)

    box = _validate_box(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_mins = box.mins * 10
    box_maxs = box.maxs * 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_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 _ in range(n_compounds):
        filled.add(clone(compound))
    filled.update_coordinates(filled_pdb)
    return filled
Exemplo n.º 6
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")

    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
Exemplo n.º 7
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
    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
Exemplo n.º 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
Exemplo n.º 9
0
    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))
Exemplo n.º 10
0
Arquivo: port.py Projeto: 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))
Exemplo n.º 11
0
    def __init__(self, anchor=None, orientation=None, separation=0):
        super(Port, self).__init__(name="Port", port_particle=True)
        self.anchor = anchor

        default_direction = np.array([0, 1, 0])
        if orientation is None:
            orientation = [0, 1, 0]
        orientation = np.asarray(orientation)

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

        down = clone(up)
        self.add(up, "up")
        self.add(down, "down")
        self.used = False

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

        if anchor:
            self.translate_to(anchor.pos)

        self.translate(separation * unit_vector(orientation))
Exemplo n.º 12
0
    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')
Exemplo n.º 13
0
    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')
        self.used = False

        if anchor:
            translate_to(self, anchor.pos)
Exemplo n.º 14
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'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
    -------
    solvated : mb.Compound

    """
    # check that the user has the PACKMOL binary on their PATH
    _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, sidemax *
            10) + 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, 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
Exemplo n.º 15
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
Exemplo n.º 16
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_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
Exemplo n.º 17
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
Exemplo n.º 18
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
Exemplo n.º 19
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
Exemplo n.º 20
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
Exemplo n.º 21
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
Exemplo n.º 22
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
Exemplo n.º 23
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
Exemplo n.º 24
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
Exemplo n.º 25
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