diff --git a/doc/source/tutorials.rst b/doc/source/tutorials.rst index 6ebe58b..e31586a 100644 --- a/doc/source/tutorials.rst +++ b/doc/source/tutorials.rst @@ -6,6 +6,7 @@ Tutorials ./tutorials/dimer_lattice_tutorial ./tutorials/analysis_tutorial/analysis_tutorial + ./tutorials/ethanol-water-tutorial/ethanol_water_mixture .. grid:: 2 @@ -22,3 +23,9 @@ Tutorials Read and analyze a LAMMPS dump file to study the Lennard-Jones fluid using the freud Python package. + + .. grid-item-card:: Initializing an Ethanol-Water Mixture + :link: ./tutorials/ethanol-water-tutorial/ethanol_water_mixture + :link-type: doc + + Use Snapshot to create a LAMMPS data file for an ethanol-water mixture. diff --git a/doc/source/tutorials/ethanol-water-tutorial/ethanol.json b/doc/source/tutorials/ethanol-water-tutorial/ethanol.json new file mode 100644 index 0000000..5300d1e --- /dev/null +++ b/doc/source/tutorials/ethanol-water-tutorial/ethanol.json @@ -0,0 +1,392 @@ +{ + "application": "LAMMPS", + "format": "molecule", + "revision": 1, + "title": "Ethanol molecule.", + "schema": "https://download.lammps.org/json/molecule-schema.json", + "units": "real", + "coords": { + "format": [ + "atom-id", + "x", + "y", + "z" + ], + "data": [ + [ + 1, + 1.218342, + -0.217632, + -0.000001 + ], + [ + 2, + 2.059319, + 0.468960, + -0.000001 + ], + [ + 3, + 1.286739, + -0.849265, + 0.878584 + ], + [ + 4, + 1.286738, + -0.849265, + -0.878586 + ], + [ + 5, + -0.092539, + 0.543081, + 0.000001 + ], + [ + 6, + -0.152259, + 1.184139, + 0.877729 + ], + [ + 7, + -0.152260, + 1.184140, + -0.877727 + ], + [ + 8, + -1.139029, + -0.393689, + 0.000001 + ], + [ + 9, + -1.970861, + 0.058109, + -0.000010 + ] + ] + }, + "types": { + "format": [ + "atom-id", + "type" + ], + "data": [ + [ + 1, + "CT" + ], + [ + 2, + "HC" + ], + [ + 3, + "HC" + ], + [ + 4, + "HC" + ], + [ + 5, + "CT" + ], + [ + 6, + "HC" + ], + [ + 7, + "HC" + ], + [ + 8, + "OA" + ], + [ + 9, + "HO" + ] + ] + }, + "masses": { + "format": [ + "atom-id", + "mass" + ], + "data": [ + [ + 1, + 12.011 + ], + [ + 2, + 1.008 + ], + [ + 3, + 1.008 + ], + [ + 4, + 1.008 + ], + [ + 5, + 12.011 + ], + [ + 6, + 1.008 + ], + [ + 7, + 1.008 + ], + [ + 8, + 15.999 + ], + [ + 9, + 1.008 + ] + ] + }, + "bonds": { + "format": [ + "bond-type", + "atom1", + "atom2" + ], + "data": [ + [ + "CT-HC", + 1, + 2 + ], + [ + "CT-HC", + 1, + 3 + ], + [ + "CT-HC", + 1, + 4 + ], + [ + "CT-CT", + 1, + 5 + ], + [ + "CT-HC", + 5, + 6 + ], + [ + "CT-HC", + 5, + 7 + ], + [ + "CT-OA", + 5, + 8 + ], + [ + "OA-HO", + 8, + 9 + ] + ] + }, + "angles": { + "format": [ + "angle-type", + "atom1", + "atom2", + "atom3" + ], + "data": [ + [ + "CT-CT-HC", + 1, + 5, + 6 + ], + [ + "HC-CT-HC", + 1, + 5, + 7 + ], + [ + "CT-CT-OA", + 1, + 5, + 8 + ], + [ + "HC-CT-HC", + 2, + 1, + 3 + ], + [ + "HC-CT-HC", + 2, + 1, + 4 + ], + [ + "HC-CT-CT", + 2, + 1, + 5 + ], + [ + "HC-CT-HC", + 3, + 1, + 4 + ], + [ + "HC-CT-CT", + 3, + 1, + 5 + ], + [ + "HC-CT-CT", + 4, + 1, + 5 + ], + [ + "CT-OA-HO", + 5, + 8, + 9 + ], + [ + "HC-CT-HC", + 6, + 5, + 7 + ], + [ + "HC-CT-OA", + 6, + 5, + 8 + ], + [ + "HC-CT-OA", + 7, + 5, + 8 + ] + ] + }, + "dihedrals": { + "format": [ + "dihedral-type", + "atom1", + "atom2", + "atom3", + "atom4" + ], + "data": [ + [ + "CT-CT-OA-HO", + 1, + 5, + 8, + 9 + ], + [ + "HC-CT-CT-HC", + 2, + 1, + 5, + 6 + ], + [ + "HC-CT-CT-HC", + 2, + 1, + 5, + 7 + ], + [ + "HC-CT-CT-OA", + 2, + 1, + 5, + 8 + ], + [ + "HC-CT-CT-HC", + 3, + 1, + 5, + 6 + ], + [ + "HC-CT-CT-HC", + 3, + 1, + 5, + 7 + ], + [ + "HC-CT-CT-OA", + 3, + 1, + 5, + 8 + ], + [ + "HC-CT-CT-HC", + 4, + 1, + 5, + 6 + ], + [ + "HC-CT-CT-HC", + 4, + 1, + 5, + 7 + ], + [ + "HC-CT-CT-OA", + 4, + 1, + 5, + 8 + ], + [ + "HC-CT-OA-HO", + 6, + 5, + 8, + 9 + ], + [ + "HC-CT-OA-HO", + 7, + 5, + 8, + 9 + ] + ] + } +} diff --git a/doc/source/tutorials/ethanol-water-tutorial/ethanol_water_mixture.ipynb b/doc/source/tutorials/ethanol-water-tutorial/ethanol_water_mixture.ipynb new file mode 100644 index 0000000..a6876d5 --- /dev/null +++ b/doc/source/tutorials/ethanol-water-tutorial/ethanol_water_mixture.ipynb @@ -0,0 +1,550 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a7dd39d8", + "metadata": {}, + "source": [ + "# Creating an Ethanol-Water Mixture\n", + "\n", + "In this tutorial, we'll walk through setting up an atomistic system containing water and ethanol molecules using `lammpsio`. This tutorial demonstrates how to work with molecular systems that have bonds, angles, and dihedral angles across multiple species. At the end, we will create a data file ready to be used by LAMMPS.\n", + "\n", + "First, we import the necessary packages." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "84c9875c", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "import subprocess\n", + "\n", + "import lammpsio\n", + "import numpy" + ] + }, + { + "cell_type": "markdown", + "id": "53a14ac0", + "metadata": {}, + "source": [ + "## System parameters\n", + "\n", + "We define the composition of our mixture and the simulation box size. Our system will contain 125 water molecules and 125 ethanol molecules, creating an equimolar mixture in a cubic box with a side length of 25 Å." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "9e27e511", + "metadata": {}, + "outputs": [], + "source": [ + "N_water = 125\n", + "N_ethanol = 125\n", + "L_box = 25" + ] + }, + { + "cell_type": "markdown", + "id": "55d39215", + "metadata": {}, + "source": [ + "## Generating the molecular configuration\n", + "\n", + "Before we can build our LAMMPS data file, we need to create a molecular configuration. We'll use [PACKMOL](http://leandro.iqm.unicamp.br/m3g/packmol/home.shtml), a powerful tool for creating initial configurations, to set up a dense, nonoverlapping arrangement of molecules.\n", + "\n", + "For this tutorial, we've provided the necessary input files for you to follow along:\n", + "\n", + "- `water.json`: LAMMPS JSON molecule file for water\n", + "- `ethanol.json`: LAMMPS JSON molecule file for ethanol\n", + "\n", + "We start by reading the molecular structures from the LAMMPS JSON molecule files. These files contain the atomic coordinates, masses, types, and topological information (bonds, angles, and dihedrals) for each molecule type. The provided files use OPLS-aa types for ethanol and TIP3P types for water. We assume that all sections containing atom IDs are sorted." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "f457221f", + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"water.json\", \"r\") as f:\n", + " water_data = json.load(f)\n", + "\n", + "with open(\"ethanol.json\", \"r\") as f:\n", + " ethanol_data = json.load(f)" + ] + }, + { + "cell_type": "markdown", + "id": "4b29e227", + "metadata": {}, + "source": [ + "## Preparing and running PACKMOL\n", + "\n", + "Now we create the necessary input files for PACKMOL. We write XYZ coordinate files for each molecule type and generate a PACKMOL input file that specifies how to pack the molecules in our simulation box." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "9c9898bd", + "metadata": {}, + "outputs": [], + "source": [ + "# write water xyz file\n", + "with open(\"water.xyz\", \"w\") as f:\n", + " f.write(f\"{len(water_data[\"coords\"][\"data\"])}\\n\")\n", + " f.write(\"Angstrom\\n\")\n", + " for cords, types in zip(water_data[\"coords\"][\"data\"], water_data[\"types\"][\"data\"]):\n", + " id, x, y, z = cords\n", + " id, _type = types\n", + " f.write(f\"{_type} {x} {y} {z} \\n\")\n", + "\n", + "# write ethanol xyz file\n", + "with open(\"ethanol.xyz\", \"w\") as f:\n", + " f.write(f\"{len(ethanol_data[\"coords\"][\"data\"])}\\n\")\n", + " f.write(\"Angstrom\\n\")\n", + " for cords, types in zip(ethanol_data[\"coords\"][\"data\"], ethanol_data[\"types\"][\"data\"]):\n", + " id, x, y, z = cords\n", + " id, _type = types\n", + " f.write(f\"{_type} {x} {y} {z} \\n\")\n", + "\n", + "# create PACKMOL input file\n", + "with open(\"pack.inp\", \"w\") as f:\n", + " lines = [\n", + " \"tolerance 2.0\\n\",\n", + " \"output mixture.xyz\\n\", \n", + " \"filetype xyz\\n\",\n", + " f\"pbc 0 0 0 {L_box} {L_box} {L_box}\\n\",\n", + " \"structure water.xyz\\n\",\n", + " f\" number {N_water}\\n\",\n", + " f\" inside box 0 0 0 {L_box} {L_box} {L_box}\\n\",\n", + " \"end structure\\n\",\n", + " \"structure ethanol.xyz\\n\", \n", + " f\" number {N_ethanol}\\n\",\n", + " f\" inside box 0 0 0 {L_box} {L_box} {L_box}\\n\",\n", + " \"end structure\\n\"\n", + " ]\n", + " f.writelines(lines)" + ] + }, + { + "cell_type": "markdown", + "id": "b43af5d7", + "metadata": {}, + "source": [ + "With our input files prepared, we can now execute PACKMOL to generate the mixed molecular configuration. This will create a single file `mixture.xyz` containing all molecules positioned within our simulation box." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "2ca37fcf", + "metadata": {}, + "outputs": [], + "source": [ + "subprocess.run(\"packmol < pack.inp\", shell=True, capture_output=True);" + ] + }, + { + "cell_type": "markdown", + "id": "f0d4bd9c", + "metadata": {}, + "source": [ + "## Reading packed coordinates\n", + "\n", + "Now we read the molecular coordinates from the `mixture.xyz` file generated by PACKMOL and separate them into water and ethanol positions. PACKMOL writes coordinates in the order specified in the input, so water molecules appear first followed by ethanol molecules. We also store the types of the packed particles and number of atoms in each molecule, as this will be needed several times throughout the tutorial." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "a365897c", + "metadata": {}, + "outputs": [], + "source": [ + "data = numpy.genfromtxt('mixture.xyz', skip_header=2, dtype=[('type', 'U8'), ('x', float), ('y', float), ('z', float)])\n", + "positions = numpy.column_stack((data['x'], data['y'], data['z']))\n", + "types = data['type']\n", + "\n", + "atoms_per_water = len(water_data[\"coords\"][\"data\"])\n", + "atoms_per_ethanol = len(ethanol_data[\"coords\"][\"data\"])" + ] + }, + { + "cell_type": "markdown", + "id": "15058f13", + "metadata": {}, + "source": [ + "Now we create a mapping from the atom type names to consecutive integer IDs that LAMMPS requires. We use a `set` to get the unique atom types from our packed system and assign each a numeric identifier." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "25c38ff0", + "metadata": {}, + "outputs": [], + "source": [ + "unique_types = set(types)\n", + "combined_type_map = {type: i + 1 for i, type in enumerate(unique_types)}" + ] + }, + { + "cell_type": "markdown", + "id": "599d90ef", + "metadata": {}, + "source": [ + "## Creating the snapshot\n", + "\n", + "Now we create a `Snapshot` to hold all our molecular data. We calculate the total number of atoms and define a simulation box that contains our system. We use the same box dimensions that were specified in our PACKMOL input file: a cubic box extending from -12.5 to 12.5 Å in each direction." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "551604e8", + "metadata": {}, + "outputs": [], + "source": [ + "N_total = N_water * atoms_per_water + N_ethanol * atoms_per_ethanol\n", + "box = lammpsio.Box([0, 0, 0], [L_box, L_box, L_box])\n", + "\n", + "snap = lammpsio.Snapshot(N_total, box, step=0)" + ] + }, + { + "cell_type": "markdown", + "id": "2331e903", + "metadata": {}, + "source": [ + "## Assigning atomic properties\n", + "\n", + "Now we assign positions, masses, and type IDs to each atom. The packed coordinates are assigned to each atom directly from the PACKMOL output." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "d66437e5", + "metadata": {}, + "outputs": [], + "source": [ + "snap.position = positions" + ] + }, + { + "cell_type": "markdown", + "id": "2ecfa03e", + "metadata": {}, + "source": [ + "We extract the atomic type and mass information from each molecule's JSON and replicate them by the number of each molecule. We concatenate these together, remembering that water's coordinates were generated first by PACKMOL, and assign them to the `Snapshot` object.\n", + "\n", + "Finally, we create a `LabelMap` to link integer type IDs to their corresponding atom type, which could be useful for later force field assignment." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "dac75368", + "metadata": {}, + "outputs": [], + "source": [ + "# assign type IDs\n", + "snap.typeid = [combined_type_map[t] for t in types]\n", + "snap.type_label = lammpsio.LabelMap({id: type for type, id in combined_type_map.items()})\n", + "\n", + "# assign masses\n", + "water_mass = [mass for _, mass in water_data[\"masses\"][\"data\"]]\n", + "ethanol_mass = [mass for _, mass in ethanol_data[\"masses\"][\"data\"]]\n", + "\n", + "snap.mass = water_mass * N_water + ethanol_mass * N_ethanol" + ] + }, + { + "cell_type": "markdown", + "id": "f9a10503", + "metadata": {}, + "source": [ + "## Parsing topology\n", + "\n", + "To prevent code duplication, we write a helper function that will parse our topology objects and handle the replication across multiple molecules." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "e930acf3", + "metadata": {}, + "outputs": [], + "source": [ + "def get_topology(topology_data, num_repeats, atoms_per_molecule):\n", + " \"\"\"Get topology for a mixture of molecules.\n", + " \n", + " Args:\n", + " topology_data: List of topology data for each molecule type from JSON \n", + " molecule files\n", + " num_repeats: List of number of molecules for each molecule type\n", + " atoms_per_molecule: List of atoms per molecule for each molecule type\n", + " \n", + " Returns:\n", + " members_list: List of member atom indices for each topology item\n", + " types_list: List of topology type labels for each topology item\n", + " \"\"\"\n", + " members_list = []\n", + " types_list = []\n", + "\n", + " if len(topology_data) != len(num_repeats) or len(topology_data) != len(atoms_per_molecule):\n", + " raise ValueError(\"Length of topology_data, num_repeats, and atoms_per_molecule must be the same.\")\n", + " \n", + " offset = 0\n", + " for topology, repeats, atoms_per_mol in zip(topology_data, num_repeats, atoms_per_molecule):\n", + " for _ in range(repeats):\n", + " for topology_item in topology:\n", + " topology_type = topology_item[0]\n", + " member_indices = topology_item[1:]\n", + "\n", + " types_list.append(topology_type)\n", + " members_list.append([offset + idx for idx in member_indices])\n", + " offset += atoms_per_mol\n", + " \n", + " return members_list, types_list" + ] + }, + { + "cell_type": "markdown", + "id": "ab6756b0", + "metadata": {}, + "source": [ + "## Creating bonds\n", + "\n", + "We extract the bond topology from the molecular data files we loaded earlier. Each molecule type has its own set of bonds defined in the JSON files. We start by reading the bond information from both molecule types and creating a mapping from the original bond type names to consecutive integer bond IDs that LAMMPS requires." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "8dee7275", + "metadata": {}, + "outputs": [], + "source": [ + "water_bond = water_data[\"bonds\"][\"data\"]\n", + "ethanol_bond = ethanol_data[\"bonds\"][\"data\"]" + ] + }, + { + "cell_type": "markdown", + "id": "71b84028", + "metadata": {}, + "source": [ + "Now we calculate the total number of bonds, use our helper function to get the bond topology for all molecules, and create a mapping from bond type names to integer IDs that LAMMPS requires." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "47dd8cc5", + "metadata": {}, + "outputs": [], + "source": [ + "# calculate total number of bonds\n", + "N_bonds = N_water * len(water_bond) + N_ethanol * len(ethanol_bond)\n", + "\n", + "# get bond topology for all molecules using our helper function\n", + "bond_members, bond_types = get_topology(\n", + " topology_data=[water_data[\"bonds\"][\"data\"], ethanol_data[\"bonds\"][\"data\"]],\n", + " num_repeats=[N_water, N_ethanol], \n", + " atoms_per_molecule=[atoms_per_water, atoms_per_ethanol],\n", + ")\n", + "\n", + "# create mapping from bond type names to integer IDs\n", + "unique_bonds = set(bond_types)\n", + "bond_type_map = {bond_type:idx+1 for idx, bond_type in enumerate(unique_bonds)}" + ] + }, + { + "cell_type": "markdown", + "id": "e6ab5f74", + "metadata": {}, + "source": [ + "Finally, we create and assign the `Bonds` object with all the bond connectivity, numeric type-id information for our system and `LabelMap`." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "d206560f", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "snap.bonds = lammpsio.Bonds(N=N_bonds, num_types=len(unique_bonds))\n", + "snap.bonds.members = bond_members\n", + "snap.bonds.typeid = [bond_type_map[bond_type] for bond_type in bond_types]\n", + "snap.bonds.type_label = lammpsio.LabelMap({id: bond_type for bond_type, id in bond_type_map.items()})" + ] + }, + { + "cell_type": "markdown", + "id": "01102754", + "metadata": {}, + "source": [ + "## Creating angles\n", + "\n", + "We follow the same approach for angles as we did for bonds: extract the angle topology from the molecule JSON files, create a mapping from the original angle type names to angle IDs, replicate the per-molecule angles to all molecules in the system, and finally create the `Angles` object to store all the angle information for the system." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "d3bb8f1e", + "metadata": {}, + "outputs": [], + "source": [ + "# get angle topology from molecule JSON data\n", + "water_angles = water_data[\"angles\"][\"data\"]\n", + "ethanol_angles = ethanol_data[\"angles\"][\"data\"]\n", + "\n", + "# calculate total number of angles\n", + "N_angles = N_water * len(water_angles) + N_ethanol * len(ethanol_angles)\n", + "\n", + "# get angle members and types for all molecules using our helper function\n", + "angle_members, angle_types = get_topology(\n", + " topology_data=[water_data[\"angles\"][\"data\"], ethanol_data[\"angles\"][\"data\"]],\n", + " num_repeats=[N_water, N_ethanol], \n", + " atoms_per_molecule=[atoms_per_water, atoms_per_ethanol],\n", + ")\n", + "\n", + "# create mapping from angle type names to integer IDs\n", + "unique_angles = set(angle_types)\n", + "angle_type_map = {angle_type:idx+1 for idx, angle_type in enumerate(unique_angles)}\n", + "\n", + "# create and assign the Angles object\n", + "snap.angles = lammpsio.Angles(N=N_angles, num_types=len(unique_angles))\n", + "snap.angles.members = angle_members\n", + "snap.angles.typeid = [angle_type_map[angle_type] for angle_type in angle_types]\n", + "snap.angles.type_label = lammpsio.LabelMap({id: angle_type for angle_type, id in angle_type_map.items()})" + ] + }, + { + "cell_type": "markdown", + "id": "16b18a77", + "metadata": {}, + "source": [ + "## Creating dihedral angles\n", + "\n", + "We also need dihedral angles (4-atom torsional angles) to properly define the molecular geometry of ethanol. Water molecules don't have dihedral angles since they only have 3 atoms.\n", + "\n", + "We follow the same protocol as above, but we only process ethanol molecules since water doesn't have dihedrals." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "280c9e4e", + "metadata": {}, + "outputs": [], + "source": [ + "# get dihedral topology from ethanol JSON data\n", + "ethanol_dihedrals = ethanol_data[\"dihedrals\"][\"data\"]\n", + "\n", + "# calculate total number of dihedrals in the system\n", + "N_dihedrals = N_ethanol * len(ethanol_dihedrals)\n", + "\n", + "# get dihedral members and types for all ethanol molecules using our helper function\n", + "dihedral_members, dihedral_types = get_topology(\n", + " topology_data=[ethanol_data[\"dihedrals\"][\"data\"]],\n", + " num_repeats=[N_ethanol], \n", + " atoms_per_molecule=[atoms_per_ethanol],\n", + ")\n", + "\n", + "# create mapping from dihedral type names to integer IDs\n", + "unique_dihedrals = set(dihedral_types)\n", + "dihedral_type_map = {dihedral_type:idx+1 for idx, dihedral_type in enumerate(unique_dihedrals)}\n", + "\n", + "# create and assign the Dihedrals object\n", + "snap.dihedrals = lammpsio.Dihedrals(N=N_dihedrals, num_types=len(unique_dihedrals))\n", + "snap.dihedrals.members = dihedral_members\n", + "snap.dihedrals.typeid = [dihedral_type_map[dihedral_type] for dihedral_type in dihedral_types]\n", + "snap.dihedrals.type_label = lammpsio.LabelMap({id: dihedral_type for dihedral_type, id in dihedral_type_map.items()})" + ] + }, + { + "cell_type": "markdown", + "id": "1e57854b", + "metadata": {}, + "source": [ + "## Save to LAMMPS data file\n", + "\n", + "Finally, we save the complete molecular system to a LAMMPS data file." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "3ee14596", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Write to LAMMPS data file\n", + "lammpsio.DataFile.create(\n", + " filename=\"ethanol_water_mixture.data\", snapshot=snap\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "c1d4333d", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "In this tutorial, we created an ethanol-water mixture using `lammpsio`. We demonstrated how to work with atomistic systems that require bonds, angles, and dihedral angles. The key steps were:\n", + "\n", + "1. **Read molecular data** from LAMMPS JSON files containing coordinates, masses, and topological information\n", + "2. **Generate coordinates** using PACKMOL to create a realistic configuration of molecules\n", + "3. **Build molecular topology** by extracting and processing bonds, angles, and dihedrals from the JSON molecule files\n", + "4. **Create the data file** ready to be used in LAMMPS\n", + "\n", + "This approach shows how `lammpsio` can interface with LAMMPS molecule JSON files and PACKMOL to handle molecular systems with multiple species!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "lammpsio-dev", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/doc/source/tutorials/ethanol-water-tutorial/water.json b/doc/source/tutorials/ethanol-water-tutorial/water.json new file mode 100644 index 0000000..32e0b8c --- /dev/null +++ b/doc/source/tutorials/ethanol-water-tutorial/water.json @@ -0,0 +1,111 @@ +{ + "application": "LAMMPS", + "format": "molecule", + "revision": 1, + "title": "Water molecule. TIP3P geometry", + "schema": "https://download.lammps.org/json/molecule-schema.json", + "units": "real", + "coords": { + "format": [ + "atom-id", + "x", + "y", + "z" + ], + "data": [ + [ + 1, + 0.00000, + -0.06556, + 0.00000 + ], + [ + 2, + 0.75695, + 0.52032, + 0.00000 + ], + [ + 3, + -0.75695, + 0.52032, + 0.00000 + ] + ] + }, + "types": { + "format": [ + "atom-id", + "type" + ], + "data": [ + [ + 1, + "OW" + ], + [ + 2, + "HO1" + ], + [ + 3, + "HO1" + ] + ] + }, + "masses": { + "format": [ + "atom-id", + "mass" + ], + "data": [ + [ + 1, + 15.999 + ], + [ + 2, + 1.008 + ], + [ + 3, + 1.008 + ] + ] + }, + "bonds": { + "format": [ + "bond-type", + "atom1", + "atom2" + ], + "data": [ + [ + "OW-HO1", + 1, + 2 + ], + [ + "OW-HO1", + 1, + 3 + ] + ] + }, + "angles": { + "format": [ + "angle-type", + "atom1", + "atom2", + "atom3" + ], + "data": [ + [ + "HO1-OW-HO1", + 2, + 1, + 3 + ] + ] + } +}