def test_spce_xml(self): spce = ForceField(get_path('spce.xml')) assert len(spce.atom_types) == 2 assert len(spce.bond_types) == 1 assert len(spce.angle_types) == 1 assert len(spce.dihedral_types) == 0 # Store expected expressions in list ref_exprs = [sympy.sympify(expr) for expr in [ "4*epsilon*((sigma/r)**12 - (sigma/r)**6)", "0.5 * k * (r-r_eq)**2", "0.5 * k * (theta-theta_eq)**2", ] ] assert allclose(spce.atom_types['opls_116'].charge, -0.8476 * u.elementary_charge) assert allclose(spce.atom_types['opls_117'].charge, 0.4238 * u.elementary_charge) assert sympy.simplify(spce.atom_types['opls_116'].expression - ref_exprs[0]) == 0 assert sympy.simplify(spce.atom_types['opls_117'].expression - ref_exprs[0]) == 0 assert sympy.simplify(spce.bond_types['opls_116~opls_117'].expression - ref_exprs[1]) == 0 assert sympy.simplify(spce.angle_types['opls_117~opls_116~opls_117'].expression - ref_exprs[2]) == 0 assert allclose(spce.atom_types['opls_116'].parameters['sigma'], 0.316557 * u.nm) assert allclose(spce.atom_types['opls_116'].parameters['epsilon'], 0.650194 * u.Unit('kJ/mol')) assert allclose(spce.atom_types['opls_117'].parameters['sigma'], 0.1 * u.nm) assert allclose(spce.atom_types['opls_117'].parameters['epsilon'], 0.0 * u.Unit('kJ/mol')) assert allclose(spce.bond_types['opls_116~opls_117'].parameters['r_eq'], 0.1 * u.nm) assert allclose(spce.bond_types['opls_116~opls_117'].parameters['k'], 345000.0 * u.Unit('kJ/(mol*nm**2)')) assert allclose(spce.angle_types['opls_117~opls_116~opls_117'].parameters['theta_eq'], 109.47 * u.degree) assert allclose(spce.angle_types['opls_117~opls_116~opls_117'].parameters['k'], 383.0 * u.Unit('kJ/mol/rad**2'))
def test_new_atom_type(self, charge, mass): new_type = AtomType(name='mytype', charge=charge, mass=mass, parameters={'sigma': 1 * u.nm, 'epsilon': 10 * u.Unit('kcal / mol')}, independent_variables={'r'}) assert new_type.name == 'mytype' assert allclose(new_type.charge, charge) assert allclose(new_type.parameters['sigma'], 1 * u.nm) assert allclose(new_type.parameters['epsilon'], 10 * u.Unit('kcal / mol')) assert allclose(new_type.mass, mass)
def test_read_gro(self): top = read_gro(get_fn('acn.gro')) assert top.name == 'ACN' assert top.n_sites == 6 assert allclose(top.box.lengths, 4 * np.ones(3) * u.nm) top = read_gro(get_fn('350-waters.gro')) assert top.name == 'Generic title' assert top.n_sites == 1050 assert allclose(top.box.lengths, 2.20866 * np.ones(3) * u.nm)
def test_new_potential(self): new_potential = Potential(name='mypotential', expression='a*x+b', parameters={ 'a': 1.0 * u.g, 'b': 1.0 * u.m }, independent_variables={'x'}) assert new_potential.name == 'mypotential' assert new_potential.expression == sympy.sympify('a*x+b') assert allclose(new_potential.parameters['a'], 1.0 * u.g) assert allclose(new_potential.parameters['b'], 1.0 * u.m) assert new_potential.independent_variables == {sympy.symbols('x')}
def test_from_parmed_basic(self, angles): struc = pmd.load_file(get_fn('ethane.mol2'), structure=True) top = from_parmed(struc, refer_type=False) for site in top.sites: assert site.atom_type is None for connection in top.connections: assert connection.connection_type is None assert top.n_sites == 8 assert top.n_bonds == 7 assert top.box is not None lengths = u.nm * [0.714, 0.7938, 0.6646] assert allclose(top.box.lengths, lengths) assert allclose(top.box.angles, angles)
def test_tip3p_force_field(self): water = ForceField(get_path('tip3p.xml')) assert len(water.atom_types) == 2 assert len(water.bond_types) == 1 assert len(water.angle_types) == 1 assert len(water.dihedral_types) == 0 # Store expected expressions in list ref_exprs = [sympy.sympify(expr) for expr in [ "4*epsilon*((sigma/r)**12 - (sigma/r)**6)", "0.5 * k * (r-r_eq)**2", "0.5 * k * (theta-theta_eq)**2", ] ] assert water.atom_types['opls_111'].charge.value == -0.834 assert water.atom_types['opls_112'].charge.value == 0.417 assert sympy.simplify(water.atom_types['opls_111'].expression - ref_exprs[0]) == 0 assert sympy.simplify(water.atom_types['opls_112'].expression - ref_exprs[0]) == 0 assert sympy.simplify(water.bond_types['opls_111~opls_112'].expression - ref_exprs[1]) == 0 assert sympy.simplify(water.angle_types['opls_112~opls_111~opls_112'].expression - ref_exprs[2]) == 0 assert allclose(water.atom_types['opls_111'].parameters['sigma'], 0.315061 * u.nm) assert allclose(water.atom_types['opls_111'].parameters['epsilon'], 0.636386 * u.Unit('kJ/mol')) assert allclose(water.atom_types['opls_112'].parameters['sigma'], 1.0 * u.nm) assert allclose(water.atom_types['opls_112'].parameters['epsilon'], 0.0 * u.Unit('kJ/mol')) assert allclose(water.bond_types['opls_111~opls_112'].parameters['r_eq'], 0.09572 * u.nm) assert allclose(water.bond_types['opls_111~opls_112'].parameters['k'], 502416.0 * u.Unit('kJ/(mol*nm**2)')) assert allclose(water.angle_types['opls_112~opls_111~opls_112'].parameters['theta_eq'], 1.824218134 * u.radian) assert allclose(water.angle_types['opls_112~opls_111~opls_112'].parameters['k'], 682.02 * u.Unit('kJ/(mol*rad**2)'))
def test_setters(self, charge, mass): new_type = AtomType(self) new_type.name = "SettingName" new_type.charge = -1.0 * charge new_type.mass = 1 * mass new_type.independent_variables = 'r' new_type.parameters = {'sigma': 1 * u.nm, 'epsilon': 10 * u.Unit('kcal / mol')} new_type.expression = 'r * sigma * epsilon' assert new_type.name == "SettingName" assert allclose(new_type.charge, -1.0 * charge) assert allclose(new_type.mass, 1 * mass) assert new_type.independent_variables == {sympy.symbols('r')} assert new_type.parameters == {'sigma': 1 * u.nm, 'epsilon': 10 * u.Unit('kcal / mol')} assert new_type.expression == sympy.sympify('r * sigma * epsilon')
def test_add_box(self): top = Topology() box = Box(2 * u.nm * np.ones(3)) assert top.box is None top.box = box assert top.box is not None assert allclose(top.box.lengths, u.nm * 2 * np.ones(3))
def test_carbon_force_field(self): carbon = ForceField(get_path('carbon.xml')) assert len(carbon.atom_types) == 1 assert len(carbon.bond_types) == 1 assert len(carbon.angle_types) == 1 assert len(carbon.dihedral_types) == 1 # Store expected expressions in list ref_exprs = [ sympy.sympify(expr) for expr in [ "4*epsilon*((sigma/r)**12 - (sigma/r)**6)", "0.5 * k * (r-r_eq)**2", "0.5 * k * (theta-theta_eq)**2", "k * (1 + cos(n * theta - theta_0))", ] ] assert carbon.atom_types['C'].charge.value == 0 assert sympy.simplify(carbon.atom_types['C'].expression - ref_exprs[0]) == 0 assert sympy.simplify(carbon.bond_types['C~C'].expression - ref_exprs[1]) == 0 assert sympy.simplify(carbon.angle_types['C~C~C'].expression - ref_exprs[2]) == 0 assert sympy.simplify(carbon.dihedral_types['*~C~C~*'].expression - ref_exprs[3]) == 0 assert allclose(carbon.atom_types['C'].parameters['sigma'], 0.339966950842 * u.nm) assert allclose(carbon.atom_types['C'].parameters['epsilon'], 0.359824 * u.Unit('kJ/mol')) assert allclose(carbon.bond_types['C~C'].parameters['r_eq'], 0.1324 * u.nm) assert allclose(carbon.bond_types['C~C'].parameters['k'], 493460.96 * u.Unit('kJ/(mol*nm**2)')) assert allclose(carbon.angle_types['C~C~C'].parameters['theta_eq'], 2.12598556185 * u.radian) assert allclose(carbon.angle_types['C~C~C'].parameters['k'], 584.42112 * u.Unit('kJ/(mol*rad**2)')) assert allclose(carbon.dihedral_types['*~C~C~*'].parameters['k'], 27.8236 * u.Unit('kJ/mol')) assert allclose(carbon.dihedral_types['*~C~C~*'].parameters['n'], 2 * u.dimensionless) assert allclose(carbon.dihedral_types['*~C~C~*'].parameters['theta_0'], np.pi * u.radian)
def test_scaled_vectors(self): box = Box(lengths=u.unyt_array((2, 2, 2), u.nm), angles=u.degree * [40.0, 50.0, 60.0]) vectors = box.get_vectors() test_vectors = np.array([[1, 0, 0], [0.5, 0.86603, 0], [0.64278, 0.51344, 0.56852]]) test_vectors = (test_vectors.T * box.lengths).T assert allclose(vectors, test_vectors, atol=u.nm * 1e-3) assert vectors.units == u.nm
def test_full_io(self): original_top = read_xyz(get_fn('ethane.xyz')) write_xyz(original_top, 'full_conversion.xyz') new_top = read_xyz('full_conversion.xyz') assert original_top.n_sites == new_top.n_sites assert original_top.n_connections == new_top.n_connections assert allclose(original_top.positions, new_top.positions)
def test_scaling_unit_vectors(self): box = Box(lengths=u.unyt_array((2, 2, 2), u.nm), angles=u.degree * [40.0, 50.0, 60.0]) u_vectors = box.get_unit_vectors() scaled_u_vectors = (u_vectors.T * box.lengths).T scaled_vectors = box.get_vectors() assert allclose(scaled_vectors, scaled_u_vectors, atol=u.nm * 1e-3) assert scaled_u_vectors.units == u.nm
def test_from_parmed_parametrized_structure(self, angles): struc = pmd.load_file(get_fn('ethane.top'), xyz=get_fn('ethane.gro')) top = from_parmed(struc) assert top.n_sites == 8 assert top.n_bonds == 7 assert top.n_angles == 12 assert top.n_connections == 19 for site in top.sites: assert site.atom_type is not None assert site.charge is not None for connection in top.connections: assert connection.connection_type is not None assert top.box is not None lengths = u.nm * [0.714, 0.7938, 0.6646] assert allclose(top.box.lengths, lengths) assert allclose(top.box.angles, angles)
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(box.angles, u.unyt_array([90, 90, 90], 'degree')): 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') for idx, dihedral_type in enumerate(topology.dihedral_types): if dihedral_type.name == 'RyckaertBellemansTorsionPotential': 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'] / 2, dihedral_type.parameters['k2'] / 2, dihedral_type.parameters['k3'] / 2, dihedral_type.parameters['k4'] / 2)) # 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))
def test_pass_box_bounding(self, ethane): ethane.periodicity = [0, 0, 0] top = from_mbuild(ethane) assert allclose(top.box.lengths, (ethane.boundingbox.lengths + [0.5, 0.5, 0.5]) * u.nm)
def test_pass_box_periodicity(self, ethane): ethane.periodicity = [2, 2, 2] top = from_mbuild(ethane) assert allclose(top.box.lengths, [2, 2, 2] * u.nm)
def test_pass_box(self, ethane): mb_box = Box(lengths=[3, 3, 3]) top = from_mbuild(ethane, box=mb_box) assert allclose(top.box.lengths, [3, 3, 3] * u.nm)
def test_ethylene_forcefield(self): ethylene = ForceField(get_path('ethylene.xml')) assert len(ethylene.atom_types) == 2 assert len(ethylene.bond_types) == 2 assert len(ethylene.angle_types) == 2 assert len(ethylene.dihedral_types) == 1 # Store expected expressions in list ref_exprs = [ sympy.sympify(expr) for expr in [ "4*epsilon*((sigma/r)**12 - (sigma/r)**6)", "0.5 * k * (r-r_eq)**2", "0.5 * k * (theta-theta_eq)**2", "c_0 + c_1 * cos(psi) + c_2 * cos(psi)**2 + c_3 * cos(psi)**3 + c_4 * cos(psi)**4 + c_5 * cos(psi)**5" ] ] assert allclose(ethylene.atom_types['opls_143'].charge, -0.23 * u.elementary_charge) assert allclose(ethylene.atom_types['opls_144'].charge, 0.115 * u.elementary_charge) assert sympy.simplify(ethylene.atom_types['opls_143'].expression - ref_exprs[0]) == 0 assert sympy.simplify(ethylene.atom_types['opls_144'].expression - ref_exprs[0]) == 0 assert sympy.simplify( ethylene.bond_types['opls_143~opls_144'].expression - ref_exprs[1]) == 0 assert sympy.simplify( ethylene.angle_types['opls_144~opls_143~opls_144'].expression - ref_exprs[2]) == 0 assert sympy.simplify( ethylene.dihedral_types['opls_144~opls_143~opls_143~opls_144']. expression - ref_exprs[3]) == 0 assert allclose(ethylene.atom_types['opls_143'].parameters['sigma'], 0.317984 * u.nm) assert allclose(ethylene.atom_types['opls_143'].parameters['epsilon'], 0.355 * u.Unit('kJ/mol')) assert allclose(ethylene.atom_types['opls_144'].parameters['sigma'], 0.12552 * u.nm) assert allclose(ethylene.atom_types['opls_144'].parameters['epsilon'], 0.242 * u.Unit('kJ/mol')) assert allclose( ethylene.bond_types['opls_143~opls_143'].parameters['r_eq'], 0.134 * u.nm) assert allclose( ethylene.bond_types['opls_143~opls_144'].parameters['k'], 284512.0 * u.Unit('kJ/(mol*nm**2)')) assert allclose( ethylene.angle_types['opls_143~opls_143~opls_144']. parameters['theta_eq'], 2.0943951 * u.rad) assert allclose( ethylene.angle_types['opls_144~opls_143~opls_144'].parameters['k'], 292.88 * u.Unit('kJ/mol/rad**2')) assert allclose( ethylene.dihedral_types['opls_144~opls_143~opls_143~opls_144']. parameters['c_0'], 58.576 * u.Unit('kJ/mol')) assert allclose( ethylene.dihedral_types['opls_144~opls_143~opls_143~opls_144']. parameters['c_2'], -58.576 * u.Unit('kJ/mol')) assert allclose( ethylene.dihedral_types['opls_144~opls_143~opls_143~opls_144']. parameters['c_5'], 0.0 * u.Unit('kJ/mol'))
def test_noble_mie_xml(self): ff = ForceField(get_path('noble_mie.xml')) templates = PotentialTemplateLibrary() ref_expr = templates['MiePotential'].expression assert len(ff.atom_types) == 4 assert len(ff.bond_types) == 0 assert len(ff.angle_types) == 0 assert len(ff.dihedral_types) == 0 for (name, atom_type) in ff.atom_types.items(): assert sympy.simplify(atom_type.expression - ref_expr) == 0 assert allclose(ff.atom_types['Ne'].parameters['epsilon'], 0.26855713 * u.Unit('kJ/mol')) assert allclose(ff.atom_types['Ne'].parameters['sigma'], 2.794 * u.Angstrom) assert allclose(ff.atom_types['Ne'].parameters['n'], 11 * u.dimensionless) assert allclose(ff.atom_types['Ne'].parameters['m'], 6 * u.dimensionless) assert ff.atom_types['Ne'].charge.value == 0 assert allclose(ff.atom_types['Ar'].parameters['epsilon'], 1.01519583 * u.Unit('kJ/mol')) assert allclose(ff.atom_types['Ar'].parameters['sigma'], 3.405 * u.Angstrom) assert allclose(ff.atom_types['Ar'].parameters['n'], 13 * u.dimensionless) assert allclose(ff.atom_types['Ar'].parameters['m'], 6 * u.dimensionless) assert ff.atom_types['Ar'].charge.value == 0 assert allclose(ff.atom_types['Kr'].parameters['epsilon'], 1.46417678 * u.Unit('kJ/mol')) assert allclose(ff.atom_types['Kr'].parameters['sigma'], 3.645 * u.Angstrom) assert allclose(ff.atom_types['Kr'].parameters['n'], 14 * u.dimensionless) assert allclose(ff.atom_types['Kr'].parameters['m'], 6 * u.dimensionless) assert ff.atom_types['Kr'].charge.value == 0 assert allclose(ff.atom_types['Xe'].parameters['epsilon'], 2.02706587 * u.Unit('kJ/mol')) assert allclose(ff.atom_types['Xe'].parameters['sigma'], 3.964 * u.Angstrom) assert allclose(ff.atom_types['Xe'].parameters['n'], 14 * u.dimensionless) assert allclose(ff.atom_types['Xe'].parameters['m'], 6 * u.dimensionless) assert ff.atom_types['Xe'].charge.value == 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)) xyz = list() types = list() for site in topology.sites: xyz.append([site.position[0], site.position[1], site.position[2]]) types.append(site.atom_type.name) forcefield = True if topology.sites[0].atom_type.name in ['', None]: forcefield = False box = topology.box unique_types = list(set(types)) unique_types.sort(key=natural_sort) # TODO: charges # TODO: bonds # TODO: Angles # TODO: Dihedrals # TODO: Figure out handling bond, angle, and dihedral indices # placeholder; change later bonds = 0 angles = 0 dihedrals = 0 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(len(topology.sites))) if atom_style in ['full', 'molecular']: if bonds != 0: data.write('{:} bonds\n'.format(len(bonds))) else: data.write('0 bonds\n') if angles != 0: data.write('{:} angles\n'.format(len(angles))) else: data.write('0 angles\n') if dihedrals != 0: data.write('{:} dihedrals\n\n'.format(len(dihedrals))) else: data.write('0 dihedrals\n\n') data.write('{:d} atom types\n'.format(len(set(types)))) # TODO: Write out bonds, angles, and dihedrals data.write('\n') # Box data if allclose(box.angles, u.unyt_array([90, 90, 90], 'degree')): 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)) # Mass data masses = [site.atom_type.mass for site in topology.sites] mass_dict = dict([(unique_types.index(atom_type) + 1, mass) for atom_type, mass in zip(types, masses)]) data.write('\nMasses\n\n') for atom_type, mass in mass_dict.items(): data.write('{:d}\t{:.6f}\t# {}\n'.format( atom_type, mass.in_units(u.g / u.mol).value, unique_types[atom_type - 1])) if forcefield: sigmas = [ site.atom_type.parameters['sigma'] for site in topology.sites ] epsilons = [ site.atom_type.parameters['epsilon'] for site in topology.sites ] sigma_dict = dict([(unique_types.index(atom_type) + 1, sigma) for atom_type, sigma in zip(types, sigmas)]) epsilon_dict = dict([ (unique_types.index(atom_type) + 1, epsilon) for atom_type, epsilon in zip(types, epsilons) ]) # TODO: Modified cross-interactions # Pair coefficients data.write('\nPair Coeffs # lj\n\n') for idx, epsilon in epsilon_dict.items(): data.write('{}\t{:.5f}\t{:.5f}\n'.format( idx, epsilon.in_units(u.Unit('kcal/mol')).value, sigma_dict[idx].in_units(u.angstrom).value)) # TODO: Write out bond coefficients # TODO: Write out angle coefficients # TODO: Write out dihedral coefficients # 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' # TODO: Add back in correct 'type_index' and 'charge' #for i,coords in enumerate(xyz): # data.write(atom_line.format( # index=i+1,type_index=unique_types.index(types[i])+1, # zero=0,charge=charges[i], # x=coords[0],y=coords[1],z=coords[2])) for i, coords in enumerate(xyz): data.write( atom_line.format( index=i + 1, type_index=unique_types.index(types[i]) + 1, zero=0, charge=0, # TODO: handle charges from atomtype and/or site x=coords[0].in_units(u.angstrom).value, y=coords[1].in_units(u.angstrom).value, z=coords[2].in_units(u.angstrom).value))
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(top.box.angles, u.degree * [90, 90, 90], 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), ))