def test_convert_kwarg_units_helper(self):
     kwargs = {
         "example_cutoff": 5.5 * u.angstrom,
         "example_cutoff_list": [5.5 * u.angstrom, 6.0 * u.angstrom],
         "example_cutoff_array": [5.5, 6.0] * u.angstrom,
     }
     _convert_kwarg_units_helper(kwargs, "example_cutoff", "nm")
     assert kwargs["example_cutoff"].units == u.nm
     assert allclose_units(kwargs["example_cutoff"], 0.55 * u.nm)
     with pytest.raises(TypeError, match="went wrong"):
         _convert_kwarg_units_helper(kwargs, "example_cutoff_list", "nm")
     _convert_kwarg_units_helper(kwargs, "example_cutoff_array", "nm")
     assert kwargs["example_cutoff_array"].units == u.nm
     assert allclose_units(kwargs["example_cutoff_array"],
                           [0.55, 0.60] * u.nm)
예제 #2
0
    def __eq__(self, other):
        """Compare two boxes for equivalence."""
        if self is other:
            return True

        if not isinstance(other, Box):
            return False

        if not allclose_units(
                self.lengths, other.lengths, rtol=1e-5, atol=1e-8):
            return False

        if not allclose_units(self.angles, other.angles, rtol=1e-5, atol=1e-8):
            return False

        return True
예제 #3
0
파일: testing.py 프로젝트: djr5/unyt-1
def assert_allclose_units(actual, desired, rtol=1e-7, atol=0, **kwargs):
    """Raise an error if two objects are not equal up to desired tolerance

    This is a wrapper for :func:`numpy.testing.assert_allclose` that also
    verifies unit consistency

    Parameters
    ----------
    actual : array-like
        Array obtained (possibly with attached units)
    desired : array-like
        Array to compare with (possibly with attached units)
    rtol : float, optional
        Relative tolerance, defaults to 1e-7
    atol : float or quantity, optional
        Absolute tolerance. If units are attached, they must be consistent
        with the units of ``actual`` and ``desired``. If no units are attached,
        assumes the same units as ``desired``. Defaults to zero.

    See Also
    --------
    :func:`unyt.array.allclose_units`

    Notes
    -----
    Also accepts additional keyword arguments accepted by
    :func:`numpy.testing.assert_allclose`, see the documentation of that
    function for details.

    Examples
    --------
    >>> import unyt as u
    >>> actual = [1e-5, 1e-3, 1e-1]*u.m
    >>> desired = actual.to("cm")
    >>> assert_allclose_units(actual, desired)
    """
    if not allclose_units(actual, desired, rtol, atol, **kwargs):
        raise AssertionError
예제 #4
0
파일: gro.py 프로젝트: zijiewu3/gmso
def write_gro(top, filename):
    """Write a topology to a gro file.

    The Gromos87 (gro) format is a common plain text structure file used
    commonly with the GROMACS simulation engine.  This file contains the
    simulation box parameters, number of atoms, the residue and atom number for
    each atom, as well as their positions and velocities (velocity is
    optional).  This method takes a topology object and a filepath string or
    file object and saves a Gromos87 (gro) to disk.

    Parameters
    ----------
    top : gmso.core.topology
        The `topology` to write out to the gro file.
    filename : str or file object
        The location and name of file to save to disk.


    Notes
    -----
    Multiple residue assignment has not been added, each `site` will belong to
    the same resid of 1 currently.

    """

    top = _prepare_topology_to_gro(top)

    with open(filename, 'w') as out_file:
        out_file.write('{} written by topology at {}\n'.format(
            top.name if top.name is not None else '',
            str(datetime.datetime.now())))
        out_file.write('{:d}\n'.format(top.n_sites))
        for idx, site in enumerate(top.sites):
            warnings.warn(
                'Residue information is not currently '
                'stored or written to GRO files.', NotYetImplementedWarning)
            # TODO: assign residues
            res_id = 1
            res_name = 'X'
            atom_name = site.name
            atom_id = idx + 1
            out_file.write(
                '{0:5d}{1:5s}{2:5s}{3:5d}{4:8.3f}{5:8.3f}{6:8.3f}\n'.format(
                    res_id,
                    res_name,
                    atom_name,
                    atom_id,
                    site.position[0].in_units(u.nm).value,
                    site.position[1].in_units(u.nm).value,
                    site.position[2].in_units(u.nm).value,
                ))

        if allclose_units(top.box.angles,
                          u.degree * [90, 90, 90],
                          rtol=1e-5,
                          atol=0.1 * u.degree):
            out_file.write(' {:0.5f} {:0.5f} {:0.5f} \n'.format(
                top.box.lengths[0].in_units(u.nm).value.round(6),
                top.box.lengths[1].in_units(u.nm).value.round(6),
                top.box.lengths[2].in_units(u.nm).value.round(6),
            ))
        else:
            # TODO: Work around GROMACS's triclinic limitations #30
            vectors = top.box.get_vectors()
            out_file.write(
                ' {:0.5f} {:0.5f} {:0.5f} {:0.5f} {:0.5f} {:0.5f} {:0.5f} {:0.5f} {:0.5f} \n'
                .format(
                    vectors[0, 0].in_units(u.nm).value.round(6),
                    vectors[1, 1].in_units(u.nm).value.round(6),
                    vectors[2, 2].in_units(u.nm).value.round(6),
                    vectors[0, 1].in_units(u.nm).value.round(6),
                    vectors[0, 2].in_units(u.nm).value.round(6),
                    vectors[1, 0].in_units(u.nm).value.round(6),
                    vectors[1, 2].in_units(u.nm).value.round(6),
                    vectors[2, 0].in_units(u.nm).value.round(6),
                    vectors[2, 1].in_units(u.nm).value.round(6),
                ))
예제 #5
0
def write_lammpsdata(topology, filename, atom_style="full"):
    """Output a LAMMPS data file.

    Outputs a LAMMPS data file in the 'full' atom style format.
    Assumes use of 'real' units.
    See http://lammps.sandia.gov/doc/atom_style.html for more information on atom styles.

    Parameters
    ----------
    Topology : `Topology`
        A Topology Object
    filename : str
        Path of the output file
    atom_style : str, optional, default='full'
        Defines the style of atoms to be saved in a LAMMPS data file.
        The following atom styles are currently supported: 'full', 'atomic', 'charge', 'molecular'
        see http://lammps.sandia.gov/doc/atom_style.html for more information on atom styles.

    Notes
    -----
    See http://lammps.sandia.gov/doc/2001/data_format.html for a full description of the LAMMPS data format.
    This is a work in progress, as only atoms, masses, and atom_type information can be written out.

    Some of this function has been adopted from `mdtraj`'s support of the LAMMPSTRJ trajectory format.
    See https://github.com/mdtraj/mdtraj/blob/master/mdtraj/formats/lammpstrj.py for details.

    """
    if atom_style not in ["atomic", "charge", "molecular", "full"]:
        raise ValueError(
            'Atom style "{}" is invalid or is not currently supported'.format(
                atom_style))

    # TODO: Support various unit styles

    box = topology.box

    with open(filename, "w") as data:
        data.write("{} written by topology at {}\n\n".format(
            topology.name if topology.name is not None else "",
            str(datetime.datetime.now()),
        ))
        data.write("{:d} atoms\n".format(topology.n_sites))
        if atom_style in ["full", "molecular"]:
            if topology.n_bonds != 0:
                data.write("{:d} bonds\n".format(topology.n_bonds))
            else:
                data.write("0 bonds\n")
            if topology.n_angles != 0:
                data.write("{:d} angles\n".format(topology.n_angles))
            else:
                data.write("0 angles\n")
            if topology.n_dihedrals != 0:
                data.write("{:d} dihedrals\n\n".format(topology.n_dihedrals))
            else:
                data.write("0 dihedrals\n\n")

        data.write("\n{:d} atom types\n".format(len(topology.atom_types)))
        data.write("{:d} bond types\n".format(len(topology.bond_types)))
        data.write("{:d} angle types\n".format(len(topology.angle_types)))
        data.write("{:d} dihedral types\n".format(len(
            topology.dihedral_types)))

        data.write("\n")

        # Box data
        if allclose_units(
                box.angles,
                u.unyt_array([90, 90, 90], "degree"),
                rtol=1e-5,
                atol=1e-8,
        ):
            warnings.warn("Orthorhombic box detected")
            box.lengths.convert_to_units(u.angstrom)
            for i, dim in enumerate(["x", "y", "z"]):
                data.write("{0:.6f} {1:.6f} {2}lo {2}hi\n".format(
                    0, box.lengths.value[i], dim))
        else:
            warnings.warn("Non-orthorhombic box detected")
            box.lengths.convert_to_units(u.angstrom)
            box.angles.convert_to_units(u.radian)
            vectors = box.get_vectors()
            a, b, c = box.lengths
            alpha, beta, gamma = box.angles

            lx = a
            xy = b * np.cos(gamma)
            xz = c * np.cos(beta)
            ly = np.sqrt(b**2 - xy**2)
            yz = (b * c * np.cos(alpha) - xy * xz) / ly
            lz = np.sqrt(c**2 - xz**2 - yz**2)

            xhi = vectors[0][0]
            yhi = vectors[1][1]
            zhi = vectors[2][2]
            xy = vectors[1][0]
            xz = vectors[2][0]
            yz = vectors[2][1]
            xlo = u.unyt_array(0, xy.units)
            ylo = u.unyt_array(0, xy.units)
            zlo = u.unyt_array(0, xy.units)

            xlo_bound = xlo + u.unyt_array(np.min([0.0, xy, xz, xy + xz]),
                                           xy.units)
            xhi_bound = xhi + u.unyt_array(np.max([0.0, xy, xz, xy + xz]),
                                           xy.units)
            ylo_bound = ylo + u.unyt_array(np.min([0.0, yz]), xy.units)
            yhi_bound = yhi + u.unyt_array(np.max([0.0, yz]), xy.units)
            zlo_bound = zlo
            zhi_bound = zhi

            data.write("{0:.6f} {1:.6f} xlo xhi\n".format(
                xlo_bound.value, xhi_bound.value))
            data.write("{0:.6f} {1:.6f} ylo yhi\n".format(
                ylo_bound.value, yhi_bound.value))
            data.write("{0:.6f} {1:.6f} zlo zhi\n".format(
                zlo_bound.value, zhi_bound.value))
            data.write("{0:.6f} {1:.6f} {2:.6f} xy xz yz\n".format(
                xy.value, xz.value, yz.value))

        # TODO: Get a dictionary of indices and atom types
        if topology.is_typed():
            # Write out mass data
            data.write("\nMasses\n\n")
            for atom_type in topology.atom_types:
                data.write("{:d}\t{:.6f}\t# {}\n".format(
                    topology.atom_types.index(atom_type) + 1,
                    atom_type.mass.in_units(u.g / u.mol).value,
                    atom_type.name,
                ))

            # TODO: Modified cross-interactions
            # Pair coefficients
            data.write("\nPair Coeffs # lj\n\n")
            for idx, param in enumerate(topology.atom_types):
                data.write("{}\t{:.5f}\t{:.5f}\n".format(
                    idx + 1,
                    param.parameters["epsilon"].in_units(
                        u.Unit("kcal/mol")).value,
                    param.parameters["sigma"].in_units(u.angstrom).value,
                ))

            if topology.bonds:
                data.write("\nBond Coeffs\n\n")
                for idx, bond_type in enumerate(topology.bond_types):
                    data.write("{}\t{:.5f}\t{:.5f}\n".format(
                        idx + 1,
                        bond_type.parameters["k"].in_units(
                            u.Unit("kcal/mol/angstrom**2")).value / 2,
                        bond_type.parameters["r_eq"].in_units(
                            u.Unit("angstrom")).value,
                    ))

            if topology.angles:
                data.write("\nAngle Coeffs\n\n")
                for idx, angle_type in enumerate(topology.angle_types):
                    data.write("{}\t{:.5f}\t{:.5f}\n".format(
                        idx + 1,
                        angle_type.parameters["k"].in_units(
                            u.Unit("kcal/mol/radian**2")).value / 2,
                        angle_type.parameters["theta_eq"].in_units(
                            u.Unit("degree")).value,
                    ))

            # TODO: Write out multiple dihedral styles
            if topology.dihedrals:
                data.write("\nDihedral Coeffs\n\n")
                for idx, dihedral_type in enumerate(topology.dihedral_types):
                    rbtorsion = PotentialTemplateLibrary(
                    )["RyckaertBellemansTorsionPotential"]
                    if (dihedral_type.expression == sympify(
                            rbtorsion.expression)
                            or dihedral_type.name == rbtorsion.name):
                        dihedral_type = convert_ryckaert_to_opls(dihedral_type)
                    data.write("{}\t{:.5f}\t{:5f}\t{:5f}\t{:.5f}\n".format(
                        idx + 1,
                        dihedral_type.parameters["k1"].in_units(
                            u.Unit("kcal/mol")).value,
                        dihedral_type.parameters["k2"].in_units(
                            u.Unit("kcal/mol")).value,
                        dihedral_type.parameters["k3"].in_units(
                            u.Unit("kcal/mol")).value,
                        dihedral_type.parameters["k4"].in_units(
                            u.Unit("kcal/mol")).value,
                    ))

        # Atom data
        data.write("\nAtoms\n\n")
        if atom_style == "atomic":
            atom_line = "{index:d}\t{type_index:d}\t{x:.6f}\t{y:.6f}\t{z:.6f}\n"
        elif atom_style == "charge":
            atom_line = "{index:d}\t{type_index:d}\t{charge:.6f}\t{x:.6f}\t{y:.6f}\t{z:.6f}\n"
        elif atom_style == "molecular":
            atom_line = "{index:d}\t{zero:d}\t{type_index:d}\t{x:.6f}\t{y:.6f}\t{z:.6f}\n"
        elif atom_style == "full":
            atom_line = "{index:d}\t{zero:d}\t{type_index:d}\t{charge:.6f}\t{x:.6f}\t{y:.6f}\t{z:.6f}\n"

        for i, site in enumerate(topology.sites):
            data.write(
                atom_line.format(
                    index=topology.sites.index(site) + 1,
                    type_index=topology.atom_types.index(site.atom_type) + 1,
                    zero=0,
                    charge=site.charge.to(u.elementary_charge).value,
                    x=site.position[0].in_units(u.angstrom).value,
                    y=site.position[1].in_units(u.angstrom).value,
                    z=site.position[2].in_units(u.angstrom).value,
                ))

        if topology.bonds:
            data.write("\nBonds\n\n")
            for i, bond in enumerate(topology.bonds):
                data.write("{:d}\t{:d}\t{:d}\t{:d}\n".format(
                    i + 1,
                    topology.bond_types.index(bond.connection_type) + 1,
                    topology.sites.index(bond.connection_members[0]) + 1,
                    topology.sites.index(bond.connection_members[1]) + 1,
                ))

        if topology.angles:
            data.write("\nAngles\n\n")
            for i, angle in enumerate(topology.angles):
                data.write("{:d}\t{:d}\t{:d}\t{:d}\t{:d}\n".format(
                    i + 1,
                    topology.angle_types.index(angle.connection_type) + 1,
                    topology.sites.index(angle.connection_members[0]) + 1,
                    topology.sites.index(angle.connection_members[1]) + 1,
                    topology.sites.index(angle.connection_members[2]) + 1,
                ))

        if topology.dihedrals:
            data.write("\nDihedrals\n\n")
            for i, dihedral in enumerate(topology.dihedrals):
                data.write("{:d}\t{:d}\t{:d}\t{:d}\t{:d}\t{:d}\n".format(
                    i + 1,
                    topology.dihedral_types.index(dihedral.connection_type) +
                    1,
                    topology.sites.index(dihedral.connection_members[0]) + 1,
                    topology.sites.index(dihedral.connection_members[1]) + 1,
                    topology.sites.index(dihedral.connection_members[2]) + 1,
                    topology.sites.index(dihedral.connection_members[3]) + 1,
                ))
예제 #6
0
def write_gsd(
    top,
    filename,
    ref_distance=1.0 * u.nm,
    ref_mass=1.0 * u.Unit("g/mol"),
    ref_energy=1.0 * u.Unit("kcal/mol"),
    rigid_bodies=None,
    shift_coords=True,
    write_special_pairs=True,
):
    """Output a GSD file (HOOMD v2 default data format).

    The `GSD` binary file format is the native format of HOOMD-Blue. This file
    can be used as a starting point for a HOOMD-Blue simulation, for analysis,
    and for visualization in various tools.

    Parameters
    ----------
    top : gmso.Topology
        gmso.Topology object
    filename : str
        Path of the output file.
    ref_distance : float, optional, default=1.0
        Reference distance for conversion to reduced units
    ref_mass : float, optional, default=1.0
        Reference mass for conversion to reduced units
    ref_energy : float, optional, default=1.0
        Reference energy for conversion to reduced units
    rigid_bodies : list of int, optional, default=None
        List of rigid body information. An integer value is required for each
        atom corresponding to the index of the rigid body the particle is to be
        associated with. A value of None indicates the atom is not part of a
        rigid body.
    shift_coords : bool, optional, default=True
        Shift coordinates from (0, L) to (-L/2, L/2) if necessary.
    write_special_pairs : bool, optional, default=True
        Writes out special pair information necessary to correctly use the OPLS
        fudged 1,4 interactions in HOOMD.

    Notes
    -----
    Force field parameters are not written to the GSD file and must be included
    manually in a HOOMD input script. Work on a HOOMD plugin is underway to
    read force field parameters from a Foyer XML file.

    """
    xyz = u.unyt_array([site.position for site in top.sites])
    if shift_coords:
        warnings.warn("Shifting coordinates to [-L/2, L/2]")
        xyz = coord_shift(xyz, top.box)

    gsd_snapshot = gsd.hoomd.Snapshot()

    gsd_snapshot.configuration.step = 0
    gsd_snapshot.configuration.dimensions = 3

    # Write box information
    if allclose_units(top.box.angles,
                      np.array([90, 90, 90]) * u.degree,
                      rtol=1e-5,
                      atol=1e-8):
        warnings.warn("Orthorhombic box detected")
        gsd_snapshot.configuration.box = np.hstack(
            (top.box.lengths / ref_distance, np.zeros(3)))
    else:
        warnings.warn("Non-orthorhombic box detected")
        u_vectors = top.box.get_unit_vectors()
        lx, ly, lz = top.box.lengths / ref_distance
        xy = u_vectors[1][0]
        xz = u_vectors[2][0]
        yz = u_vectors[2][1]
        gsd_snapshot.configuration.box = np.array([lx, ly, lz, xy, xz, yz])

    warnings.warn(
        "Only writing particle and bond information."
        " Angle and dihedral is not currently written to GSD files",
        NotYetImplementedWarning,
    )
    _write_particle_information(gsd_snapshot, top, xyz, ref_distance, ref_mass,
                                ref_energy, rigid_bodies)
    # if write_special_pairs:
    #    _write_pair_information(gsd_snapshot, top)
    if top.n_bonds > 0:
        _write_bond_information(gsd_snapshot, top)
    # if structure.angles:
    #    _write_angle_information(gsd_snapshot, top)
    # if structure.rb_torsions:
    #    _write_dihedral_information(gsd_snapshot, top)

    with gsd.hoomd.open(filename, mode="wb") as gsd_file:
        gsd_file.append(gsd_snapshot)