@@ -735,7 +735,12 @@ def from_ff_and_topologies(cls, box, ff, topologies, atom_style="full"):
735735 atom_style (str): Output atom_style. Default to "full".
736736
737737 """
738- atom_types = set .union (* (t .species for t in topologies ))
738+ atom_types = set .union (* [t .species for t in topologies ])
739+ print (f"atom_types:\n { atom_types } \n " )
740+ print (f"ff object:\n { ff } \n " )
741+ print (f"ff.maps:\n { ff .maps } \n " )
742+ print (f"ff.maps['Atoms']:\n { ff .maps ['Atoms' ]} \n " )
743+ print (f"ff.maps['Atoms'].keys():\n { ff .maps ['Atoms' ].keys ()} \n " )
739744 assert atom_types .issubset (ff .maps ["Atoms" ].keys ()), "Unknown atom type found in topologies"
740745
741746 items = dict (box = box , atom_style = atom_style , masses = ff .masses , force_field = ff .force_field )
@@ -1758,29 +1763,41 @@ def __init__(
17581763 length_increase = 0.5 ,
17591764 check_ff_duplicates = True ,
17601765 box_data_type = "cubic" ,
1766+ system_molecule = None ,
1767+ system_molecule_type = None ,
1768+ sorted_mol_names = None ,
17611769 ):
17621770 """
1763- Low level constructor designed to work with lists of dictionaries that should
1764- be able to be obtained from databases. Works for cubic boxes only using
1765- real coordinates.
1766- :param system_force_fields: [dict] Contains force field information using
1767- the following format:
1771+ Low level constructor designed to work with lists of
1772+ dictionaries that should be able to be obtained from databases.
1773+ Works for cubic boxes only using real coordinates.
1774+ :param system_force_fields: [dict] Contains force field
1775+ information using the following format:
17681776 { unique_molecule_name: {
17691777 'Molecule': pymatgen.Molecule,
17701778 'Labels': [atom_a, ...]
17711779 'Masses': [OrderedDict({species_1: mass_1, ...})],
17721780 'Nonbond': [[...], ...],
1773- 'Bonds': [{'coeffs': [...], 'types': [(i, j), ...]}, ...],
1774- 'Angles': [{'coeffs': [...], 'types': [(i, j, k), ...]}, ...],
1775- 'Dihedrals': [{'coeffs': [...], 'types': [(i, j, k, l), ...]}, ...],
1776- 'Impropers': [{'coeffs': [...], 'types': [(i, j, k, l), ...]}, ...],
1781+ 'Bonds': [
1782+ {'coeffs': [...], 'types': [(i, j), ...]}, ...
1783+ ],
1784+ 'Angles': [
1785+ {'coeffs': [...], 'types': [(i, j, k), ...]}, ...
1786+ ],
1787+ 'Dihedrals': [
1788+ {'coeffs': [...], 'types': [(i, j, k, l), ...]}, ...
1789+ ],
1790+ 'Impropers': [
1791+ {'coeffs': [...], 'types': [(i, j, k, l), ...]}, ...
1792+ ],
17771793 'Improper Topologies': [[a, b, c, d],...]
17781794 'Charges': [atom_a, ...]
17791795 }, ...}
1780- :param system_mixture_data: [dict] Format depends on mixture_data_type input.
1781- For mixture_data_type = 'concentration', this parameter contains molarity,
1782- density, and molar weights of solutes and solvents using the
1783- following format:
1796+ :param system_mixture_data: [dict] Format depends on
1797+ mixture_data_type input.
1798+ For mixture_data_type = 'concentration', this parameter
1799+ contains molarity, density, and molar weights of solutes and
1800+ solvents using the following format:
17841801 {
17851802 'Solutes': {unique_molecule_name: {
17861803 'Initial Molarity': molarity_1i,
@@ -1795,22 +1812,87 @@ def __init__(
17951812 'Molar Weight': molar_weight_1
17961813 }, ...}
17971814 }
1798- For mixture_data_type = 'number of molecules', this parameter contains
1799- the number of molecules for each species in the system in the following
1800- format:
1815+ For mixture_data_type = 'number of molecules', this
1816+ parameter contains the number of molecules for each species
1817+ in the system in the following format:
18011818 {
18021819 unique_molecule_name: n_mols,
18031820 ...
18041821 }
1805- :param mixture_data_type: [str] controls the format of the system_mixture_data
1806- parameter. Currently supports values of 'concentration' and
1807- 'number of molecules'. Defaults to "concentration".
1808- :param cube_length: [float] length of system box in angstroms.
1809- :param origin: [list] Optional. Change if the minimum xyz coordinates for
1810- desired box are not [0,0,0].
1822+ :param box_data: [float, 3x2 array_like, LammpsBox] This
1823+ parameter controls the dimensions of the system box. The
1824+ format depends on the value of box_data_type.
1825+ For box_data_type = 'cubic', this parameter is a float and
1826+ sets the length of the cubic box.
1827+ For box_data_type = 'rectangular', this parameter is a 3x2
1828+ array_like and sets the bounds of the rectangular box.
1829+ For box_data_type = 'LammpsBox', this parameter is a pmg
1830+ LammpsBox object and sets the bounds of the box.
1831+ :param mixture_data_type: [str] controls the format of the
1832+ system_mixture_data parameter. Currently supports values of
1833+ 'concentration', 'number of molecules', 'pmg molecule', and
1834+ 'xyz'. Defaults to "concentration".
1835+ :param origin: [1x3 array_like] Optional. Change if the minimum
1836+ xyz coordinates for desired box are not [0,0,0].
18111837 :param seed: [int] Optional. Sets the seed for running packmol.
1812- :param packmolrunner_inputs: [dict] Optional. Parameters for PackmolRunner
1813- in pymatgen.io.lammps.utils
1838+ This parameter will update the 'seed' key in the
1839+ packmolrunner_inputs parameter. Defaults to 150.
1840+ :param packmolrunner_inputs: [dict] Optional. Parameters for
1841+ PackmolRunner in pymatgen.io.lammps.utils
1842+ :param length_increase: [int, float, or 1x3 array_like] Optional.
1843+ Because packmol will not always strictly adhere to the box
1844+ dimensions, this parameter sets the amount to increase the
1845+ box dimensions by n order to ensure that all molecules are
1846+ within the box. This is also relevant for interfacial
1847+ systems where there is a fixed boundary since LAMMPS does
1848+ not work if the atom is exactly on the boundary. This param
1849+ will increase the length of the box by this amount in the x,
1850+ y, and z directions. In effect, it will decrease the min box
1851+ value by half of its value and increase the max box value by
1852+ half of its value Defaults to 0.5. This means that the min
1853+ values of x, y, and z will be decreased by 0.25 and the max
1854+ values of x, y, and z will be increased by 0.25 by default.
1855+ If using the default value for liquid-only systems, it might
1856+ be advisable to set the origin to [0.25, 0.25, 0.25] to
1857+ ensure that the true box origin is at [0,0,0].
1858+ :param check_ff_duplicates: [bool] Optional. If True, the angle,
1859+ dihedral, and improper bond types will be checked for
1860+ duplicates (eg, for angles, the following two types are
1861+ duplicates: ("C", "O", "H") and ("H", "O", "C")). If
1862+ duplicates are found, a warning will be issued. Defaults to
1863+ True.
1864+ :param box_data_type: [str] Optional. Controls the format of the
1865+ box_data parameter. Currently supports values of 'cubic',
1866+ 'rectangular', and 'LammpsBox'. Defaults to "cubic".
1867+ :param system_molecule: [pmg Molecule, str] Optional. If
1868+ mixture_data_type is 'pmg molecule', this parameter should
1869+ contain a pmg Molecule object containing all of the
1870+ molecules in the system. The order of the molecules in this
1871+ object should be similar to that created by the
1872+ PackmolRunner object. That is, all sites belonging to the
1873+ first molecule should be listed first, then all sites
1874+ belonging to the second molecule, and so on. Also, the sites
1875+ belonging to each molecular species should be sorted such
1876+ that all the sites belonging to the first molecular species
1877+ occur first, then all the sites belonging to the second
1878+ molecular species, and so on.
1879+ If mixture_data_type is 'xyz', this parameter should contain
1880+ a path to an xyz file containing the coordinates of all the
1881+ molecules in the system. The order of the molecules in this
1882+ file should be similar to the xyz file created by the
1883+ PackmolRunner object, and by extension, the 'pmg molecule'
1884+ version of this parameter.
1885+ If this parameter is used, then the system_mixture_data
1886+ should be the number of molecules in the system.
1887+ If None, the system molecule will be created from packmol
1888+ based on the system_mixture_data. Defaults to None.
1889+ :param system_molecule_type: [str] Optional. controls the format
1890+ of the system_molecule parameter. Currently supports values
1891+ of 'pmg molecule' and 'xyz'. Defaults to None.
1892+ :param sorted_mol_names: [list] Optional. Contains the unique
1893+ molecule names in the system. If None, the molecule names
1894+ will be sorted from most to least number of atoms. Defaults
1895+ to None.
18141896 """
18151897 self ._ff_list = system_force_fields
18161898 self ._concentration_data = False
@@ -1830,40 +1912,79 @@ def __init__(
18301912 elif mixture_data_type == "number of molecules" :
18311913 self ._number_of_molecules_data = True
18321914 self ._n_mol_dict = system_mixture_data
1833-
1915+
1916+ self ._origin = origin
18341917 if box_data_type == "cubic" :
18351918 self .length = box_data
1836- self ._initial_lammps_box = LammpsBox ([[0.0 , box_data ],
1837- [0.0 , box_data ],
1838- [0.0 , box_data ]])
1919+ self ._initial_lammps_box = LammpsBox ([[self . _origin [ 0 ] , box_data ],
1920+ [self . _origin [ 1 ] , box_data ],
1921+ [self . _origin [ 2 ] , box_data ]])
18391922 elif box_data_type == "rectangular" :
18401923 self ._initial_lammps_box = LammpsBox (box_data )
18411924 elif box_data_type == "LammpsBox" :
18421925 self ._initial_lammps_box = box_data
1843- self . _origin = origin
1926+
18441927
18451928 packmolrunner_inputs ["control_params" ]["seed" ] = seed
18461929 self ._packmolrunner_inputs = packmolrunner_inputs
18471930
18481931 self .xyz_high = [bound [1 ] for bound in self ._initial_lammps_box .as_dict ()["bounds" ]]
18491932 self .xyz_low = [bound [0 ] for bound in self ._initial_lammps_box .as_dict ()["bounds" ]]
18501933
1934+ if type (length_increase ) in [int , float ]:
1935+ self ._length_increase_vector = [length_increase ] * 3
1936+ elif len (length_increase ) == 3 :
1937+ self ._length_increase_vector = length_increase
1938+ else :
1939+ raise ValueError (
1940+ "The length_increase parameter is not valid. It should be a \
1941+ float or int or a list of 3 floats or ints."
1942+ )
18511943 self ._length_increase = length_increase
18521944 self ._check_ff_duplicates = check_ff_duplicates
18531945
1946+ if system_molecule_type is None :
1947+ self ._packmol_run_status = True
1948+ elif system_molecule_type in ["pmg molecule" , "xyz" ]:
1949+ self ._packmol_run_status = False
1950+ if system_molecule_type == "xyz" :
1951+ self ._sys_molecule = Molecule .from_file (system_molecule )
1952+ else :
1953+ self ._sys_molecule = system_molecule
1954+ if mixture_data_type != "number of molecules" :
1955+ raise ValueError (
1956+ "The system_molecule_type parameter is not valid. \
1957+ Currently, the only valid value for this parameter is \
1958+ 'number of molecules' if also using a system_molecule \
1959+ parameter."
1960+ )
1961+ else :
1962+ raise ValueError (
1963+ "The system_molecule_type parameter is not valid. Currently, \
1964+ the only valid values are 'pmg molecule' and 'xyz'."
1965+ )
1966+
1967+ self ._sorted_mol_names = sorted_mol_names
1968+
1969+
18541970 @property
18551971 def sorted_mol_names (self ):
18561972 """
1857- Sorts molecules from most to least number of atoms
1858- :return molecule_name_list: [list] Contains the unique_molecule_names
1973+ Sorts molecules from most to least number of atoms. Only used if
1974+ sorted_mol_names is None.
1975+ :return molecule_name_list: [list] Contains the
1976+ unique_molecule_names
18591977 """
1860- molecule_name_list = list (self ._ff_list .keys ())
1861- molecule_natoms_list = [len (self ._ff_list [name ]["Molecule" ]) for
1862- name in molecule_name_list ]
1863- molecule_name_list .sort (key = dict (zip (molecule_name_list ,
1864- molecule_natoms_list )).get ,
1865- reverse = True )
1866- return molecule_name_list
1978+ if self ._sorted_mol_names is not None :
1979+ return self ._sorted_mol_names
1980+ else :
1981+ molecule_name_list = list (self ._ff_list .keys ())
1982+ molecule_natoms_list = [len (self ._ff_list [name ]["Molecule" ]) for
1983+ name in molecule_name_list ]
1984+ molecule_name_list .sort (key = dict (zip (molecule_name_list ,
1985+ molecule_natoms_list )).get ,
1986+ reverse = True )
1987+ return molecule_name_list
18671988
18681989 @property
18691990 def nmol_dict (self ):
@@ -2097,15 +2218,18 @@ def _get_lammps_box(self, system_molecule):
20972218 :param system_molecule: [Molecule] Output from _run_packmol()
20982219 :return Mix_lmpbox: [pmg.LammpsBox] Object representing the simulation box
20992220 """
2100- final_xyz_low = np .subtract (self .xyz_low , np .ones ( 3 ) * self ._length_increase * 0.5 )
2101- final_xyz_high = np .add (self .xyz_high , np .ones ( 3 ) * self ._length_increase * 0.5 )
2221+ final_xyz_low = np .subtract (self .xyz_low , np .multiply ( self ._length_increase_vector , 0.5 ) )
2222+ final_xyz_high = np .add (self .xyz_high , np .multiply ( self ._length_increase_vector , 0.5 ) )
21022223 final_bounds = np .asarray ([final_xyz_low , final_xyz_high ]).transpose ()
21032224
21042225 mix_lmpbox = LammpsBox (final_bounds , self ._initial_lammps_box .as_dict ()["tilt" ])
21052226 return mix_lmpbox
21062227
21072228 def build_lammps_data (self , atom_style = "full" ):
2108- system_molecule = self ._run_packmol ()
2229+ if self ._packmol_run_status :
2230+ system_molecule = self ._run_packmol ()
2231+ else :
2232+ system_molecule = self ._sys_molecule
21092233 system_topologies = self ._get_topologies (system_molecule )
21102234 system_lammps_box = self ._get_lammps_box (system_molecule )
21112235 system_lammps_data = LammpsData .from_ff_and_topologies (
0 commit comments