Tutorial: Contact Angle Analysis (Binning Method)

This tutorial demonstrates how to compute the contact angle using the binning method in wetting_angle_kit. The method divides the simulation box into spatial bins to calculate the liquid–solid interface and the corresponding contact angle, for a group of frames.


1. Overview

The binning method works by:

  1. Collecting the positions of water molecules (typically oxygen atoms).

  2. Dividing the region of interest into bins in the x–z plane.

  3. Computing density profiles and fitting the interface shape.

  4. Deriving the contact angle from the interface curvature.


2. Prerequisites

Your trajectory file (e.g., a LAMMPS dump file) should contain:

  • Atom IDs, types, and positions

  • Liquid particles (in this case, water molecules: O and H atoms)

Example trajectory:

tests/trajectories/traj_10_3_330w_nve_4k_reajust.lammpstrj

3. Example Script

# Import necessary modules
from wetting_angle_kit.parsers import LammpsDumpParser, LammpsDumpWaterFinder
from wetting_angle_kit.analysis import BinningTrajectoryAnalyzer

# --- Step 1: Define the trajectory file ---
filename = "../../tests/trajectories/traj_10_3_330w_nve_4k_reajust.lammpstrj"

# --- Step 2: Initialize the water molecule finder ---
# This identifies O and H atoms in water molecules
wat_find = LammpsDumpWaterFinder(
    filename,
    particle_type_wall={3},  # Wall atom types
    oxygen_type=1,  # Oxygen atom type
    hydrogen_type=2,  # Hydrogen atom type
)

# --- Step 3: Get oxygen atom indices for the first frame ---
oxygen_indices = wat_find.get_water_oxygen_ids(frame_index=0)
print("Number of water molecules:", len(oxygen_indices))

# --- Step 4: Define binning parameters ---
binning_params = {
    "xi_0": 0.0,  # Minimum x-coordinate
    "xi_f": 100.0,  # Maximum x-coordinate
    "nbins_xi": 50,  # Number of bins along x
    "zi_0": 0.0,  # Minimum z-coordinate
    "zi_f": 100.0,  # Maximum z-coordinate
    "nbins_zi": 25,  # Number of bins along z
}

# --- Step 5: Initialize the parser ---
parser = LammpsDumpParser(filename)

# --- Step 6: Create the contact angle analyzer ---
analyzer = BinningTrajectoryAnalyzer(
    parser=parser,
    atom_indices=oxygen_indices,
    droplet_geometry="cylinder_y",  # Interface fitting model
    binning_params=binning_params,
)

# --- Step 7: Run analysis for a frame range ---
results = analyzer.analyze([1])  # Analyze frame 1
print("Mean contact angle (°):", results.mean_angle)

4. Output

Running this example will:

  • Parse the trajectory.

  • Compute the interface shape and local contact angle for each batch.

  • Return a BinningResults dataclass holding angles, density fields and fitted isolines for every batch (no files written).

Example printed output:

Number of water molecules: 4000
Mean contact angle (°): 94.58987060394456

The returned results object exposes mean_angle, std_angle, angles_per_batch and a batches list whose entries carry the density field (xi_cc, zi_cc, rho_cc) and the fitted droplet / wall isoline coordinates. Feed it directly to BinningTrajectoryPlotter to draw the interactive density contour with the fitted semi-circle:

from wetting_angle_kit.visualization import BinningTrajectoryPlotter

plotter = BinningTrajectoryPlotter(results)
fig = plotter.plot_density_contour(batch_index=0)
fig.show()

5. Tips

  • Adjust xi_f, zi_f, and the bin counts (nbins_xi, nbins_zi) according to your simulation box dimensions.

  • If the wall surface is not flat or the system is tilted, pre-align it before analysis.

  • Multi-batch analysis: analyzer.analyze(range(0, 100), split_factor=10) splits the frame range into batches of ten frames each.