Visualisation Tutorial — Per-Frame Droplet Snapshot
This tutorial uses DropletSlicePlotter to draw a single-frame
snapshot of a droplet, overlaying the recovered interface contour,
the fitted Taubin circle, the tangent at the contact point, and the
wall atom positions. The plotter takes raw arrays
(atom positions, surface points, fit parameters), so it works on the
output of the slicing pipeline once you pull the corresponding fields
off the result object.
1. Overview
The workflow:
Identify water molecules and read their positions for the frame.
Read the wall atom positions for the same frame.
Run the slicing pipeline (
TrajectoryAnalyzerwithSurfaceFitter.slicing()) on that frame; pull theslice_surfaces/slice_popts/per_slice_anglesarrays off the resultingSlicingBatchResult.Pick one slice index and pass the corresponding entries to
DropletSlicePlotter.
2. Import Required Modules
import numpy as np
from wetting_angle_kit.analysis import (
DensityEstimator,
InterfaceExtractor,
SpaceSampling,
SurfaceFitter,
TrajectoryAnalyzer,
WallDetector,
)
from wetting_angle_kit.analysis.temporal import TemporalAggregator
from wetting_angle_kit.parsers import (
LammpsDumpParser,
LammpsDumpWallParser,
LammpsDumpWaterFinder,
)
from wetting_angle_kit.visualization import DropletSlicePlotter
3. Define the Input Trajectory
filename = "../../tests/trajectories/traj_10_3_330w_nve_4k_reajust.lammpstrj"
frame_index = 10
4. Identify Water Molecules
wat_find = LammpsDumpWaterFinder(filename, oxygen_type=1, hydrogen_type=2)
oxygen_indices = wat_find.get_water_oxygen_indices(frame_index=0)
print("Number of water molecules detected:", len(oxygen_indices))
5. Read Atom and Wall Positions
parser = LammpsDumpParser(filepath=filename)
oxygen_position = parser.parse(frame_index=frame_index, indices=oxygen_indices)
# Wall parser: ``liquid_particle_types`` lists what to EXCLUDE
# (the liquid), leaving the wall atoms.
wall_parser = LammpsDumpWallParser(filename, liquid_particle_types=[1, 2])
wall_coords = wall_parser.parse(frame_index=frame_index)
6. Run the Slicing Pipeline
analyzer = TrajectoryAnalyzer(
parser=LammpsDumpParser(filename),
atom_indices=oxygen_indices,
droplet_geometry="cylinder_y",
interface_extractor=InterfaceExtractor(
sampling=SpaceSampling.rays(delta_cylinder=5.0, delta_polar=8.0),
density=DensityEstimator.gaussian(),
),
surface_fitter=SurfaceFitter.slicing(surface_filter_offset=2.0),
wall_detector=WallDetector.min_plus_offset(offset=0.0),
temporal_aggregator=TemporalAggregator(batch_size=1),
)
batch = analyzer.analyze([frame_index]).batches[0]
print("Per-slice contact angles (°):", batch.per_slice_angles)
7. Visualise the Droplet
The plotter takes a single slice’s data; pick a slice index and pull the corresponding entries off the batch:
plotter = DropletSlicePlotter(center=True)
slice_idx = 0 # pick a slice — all three arrays are parallel
fig = plotter.plot_surface_points(
oxygen_position=oxygen_position,
surface_data=[batch.slice_surfaces[slice_idx]],
popt=batch.slice_popts[slice_idx],
wall_coords=wall_coords,
alpha=float(batch.per_slice_angles[slice_idx]),
)
# Interactive view in a notebook
fig.show()
# Or save a standalone HTML page
fig.write_html("droplet_plot.html")
The figure overlays four layers: the raw water-oxygen positions, the
recovered interface contour for the chosen slice, the fitted Taubin
circle, and the wall atoms. DropletSlicePlotter accepts
center=True (default) to centre the plot on the droplet COM.