Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
bad3f73
Phase P0: bug fixes.
gbrunin May 12, 2026
8fada31
Phase P1: design issues.
gbrunin May 12, 2026
4b996d1
Phase P3: documentation.
gbrunin May 12, 2026
9f45fe1
Phase 4: packaging / CI
gbrunin May 12, 2026
e34bfb2
CI
gbrunin May 12, 2026
99fc0b6
CI missed a file in commit.
gbrunin May 12, 2026
36eeb46
Replaced relative imports by full imports.
gbrunin May 12, 2026
729d095
pyproject.toml update
gbrunin May 13, 2026
455c35d
Removed output_repo instead of deprecation warnings.
gbrunin May 13, 2026
dfc24f6
Update testing and ruff after bump to python 3.10
gbrunin May 13, 2026
cbf53ad
Moved to src/ layout.
gbrunin May 13, 2026
a0a60d4
Update ovito version.
gbrunin May 13, 2026
84d4ed9
Ruff fix.
gbrunin May 13, 2026
6d209c3
Cosmetic changes.
gbrunin May 13, 2026
547b7c1
Reviewed docstring.
gbrunin May 18, 2026
95f4a0f
Docstring update.
gbrunin May 20, 2026
7ddf233
Cosmetic changes to parsers.
gbrunin May 20, 2026
e9ccb9f
Force orthogonal cells. More robust lengths computations.
gbrunin May 20, 2026
7c03f9c
refcatorBaseParser.get_profile_coordinates and move project_to_profil…
May 20, 2026
ccd3716
remove test test_base_parser_empty_frame_list_does_not_crash
May 20, 2026
eaea3ca
update pre-commit
May 20, 2026
932e69c
fix pre-commit error
May 20, 2026
16b902d
Pre-commit fixing.
gbrunin May 20, 2026
422acfe
Code cleanup.
gbrunin May 20, 2026
521b516
Update main init to force using namespaces.
gbrunin May 20, 2026
91db16b
re-reading docstr and comments
May 21, 2026
7b5cac5
Update ruff version from old one.
gbrunin May 22, 2026
551016d
Review of the paper.
gbrunin May 22, 2026
f1c8e62
Split long lines in md file in shorter ones for easier review.
gbrunin May 22, 2026
4a6a701
Adding ORCID and updating python version in README. Updating licence …
gbrunin May 22, 2026
a220310
Removed docs/build from tracked files, update pre-commit config.
gbrunin May 22, 2026
40ccde6
Cosmetic update to paper.bib
gbrunin May 22, 2026
3ab3bba
Removed unused files.
gbrunin May 22, 2026
2fa6879
Polishing, adding CONTRIBUTING and CITATION, removing printing.
gbrunin May 22, 2026
35b87d6
mypy stuff
gbrunin May 22, 2026
7302ed2
sliced -> slicing, adding tests for better coverage.
gbrunin May 22, 2026
6b54442
Trying to fix CI.
gbrunin May 22, 2026
e032e4a
Trying to fix CI, trial 2.
gbrunin May 22, 2026
f4a2d88
Trying to fix CI, trial 3.
gbrunin May 22, 2026
6e622b0
Trying to fix CI for macOS.
gbrunin May 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 42 additions & 3 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
name: tests

on:
push:

branches: [main]
pull_request:
branches: [main]

Expand All @@ -26,8 +28,10 @@ jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ['3.9', '3.10', '3.11']
# OVITO does not yet ship wheels for Python 3.13, so we cap at 3.12.
python-version: ['3.10', '3.11', '3.12']

steps:
- uses: actions/checkout@v4
Expand All @@ -46,7 +50,42 @@ jobs:
pip install .[dev,all]

- name: Test
run: pytest --cov=wetting_angle_kit --cov-report=xml
run: pytest --cov=wetting_angle_kit --cov-report=xml --cov-fail-under=70

- name: Upload coverage to Codecov
if: matrix.python-version == '3.11'
uses: codecov/codecov-action@v4
with:
files: ./coverage.xml
fail_ci_if_error: false
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

# macOS smoke test: confirms the package installs and the non-OVITO code
# paths work on macOS. OVITO is skipped because it does not ship via PyPI
# for macOS; install via its conda channel locally if you need it.
test-macos:
runs-on: macos-latest

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v6
with:
python-version: '3.11'
cache: pip
cache-dependency-path: pyproject.toml

- name: Install dependencies (ASE only, no OVITO)
run: |
python -m pip install --upgrade pip
pip install .[dev,ase]

- name: Test (skip OVITO-dependent tests)
run: |
pytest \
--ignore=tests/test_parser/test_parser_dump.py \
--ignore=tests/test_contact_angle_methods

docs:
runs-on: ubuntu-latest
Expand Down
9 changes: 4 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Auto-generated version file
wetting_angle_kit/_version.py
src/wetting_angle_kit/_version.py

# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down Expand Up @@ -73,11 +73,10 @@ instance/
.scrapy

# Sphinx documentation
doc/build/doctrees
doc/build/generate-stamp
doc/source/api/m*.rst
docs/build/doctrees
docs/build/generate-stamp

doc/source/changelog.rst
docs/source/changelog.rst

# PyBuilder
.pybuilder/
Expand Down
11 changes: 4 additions & 7 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
default_language_version:
python: python3
exclude: ^(.github/|tests/test_data/abinit/)
exclude: ^(\.github/|docs/build/)
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.4.6
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.12
hooks:
- id: ruff
args: [--fix]
Expand Down Expand Up @@ -33,12 +33,9 @@ repos:
rev: v1.10.0
hooks:
- id: mypy
files: ^src/
files: ^src/wetting_angle_kit/
additional_dependencies:
- tokenize-rt==4.1.0
- types-paramiko
- pydantic~=2.0
- types-python-dateutil
- repo: https://github.com/codespell-project/codespell
rev: v2.3.0
hooks:
Expand Down
42 changes: 42 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
cff-version: 1.2.0
message: "If you use wetting-angle-kit in your research, please cite it using the metadata below."
title: "wetting-angle-kit: a Python package to streamline the computation of wetting contact angles of nanodroplets on surfaces"
type: software
license: BSD-3-Clause
repository-code: "https://github.com/Matgenix/wetting_angle_kit"
url: "https://matgenix.github.io/wetting-angle-kit"
authors:
- family-names: Taillandier
given-names: Gabriel
orcid: "https://orcid.org/0009-0006-9544-0982"
affiliation: "Matgenix; University of Crete"
- family-names: Monti
given-names: Edoardo
orcid: "https://orcid.org/0009-0002-8340-7940"
affiliation: "Imperial College London; Toyota Motor Europe"
- family-names: Brunin
given-names: Guillaume
orcid: "https://orcid.org/0000-0003-1159-8389"
affiliation: "Matgenix"
- family-names: Bouquiaux
given-names: Julien
orcid: "https://orcid.org/0000-0003-1982-052X"
affiliation: "Matgenix"
- family-names: Froudakis
given-names: George
orcid: "https://orcid.org/0000-0002-6907-1822"
affiliation: "University of Crete"
- family-names: Rignanese
given-names: Gian-Marco
orcid: "https://orcid.org/0000-0002-1422-1205"
affiliation: "Matgenix; UCLouvain"
- family-names: Waroquiers
given-names: David
orcid: "https://orcid.org/0000-0001-8943-9762"
affiliation: "Matgenix"
keywords:
- molecular dynamics
- contact angle
- wetting
- nanodroplets
- python
101 changes: 101 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Contributing to wetting-angle-kit

Thanks for your interest in contributing. This document covers the basics
for getting a local development environment running, the conventions the
project follows, and how to report problems.

## Reporting issues and asking for help

- **Bugs and feature requests:** open an issue at
[github.com/Matgenix/wetting_angle_kit/issues](https://github.com/Matgenix/wetting_angle_kit/issues).
Please include a minimal reproducer (trajectory snippet or code) when
possible, the Python version, and the package version (the git commit
if you installed from source).
- **Questions / usage support:** also via the issue tracker — tag the
issue with `question`.

## Development setup

The project requires Python 3.10 or higher and uses a `src/` layout. We
recommend using conda to manage the environment, as OVITO is distributed
through its own conda channel and is awkward to install otherwise.

```bash
git clone https://github.com/Matgenix/wetting_angle_kit.git
cd wetting_angle_kit
conda create -n wetting-angle-kit python=3.11
conda activate wetting-angle-kit
conda install --strict-channel-priority -c https://conda.ovito.org -c conda-forge ovito=3.11.3
pip install -e .[dev,all,doc]
pre-commit install
```

`[all]` pulls in both trajectory backends (`ase`, `ovito`) and the
visualization helpers. If you do not need the OVITO backend (i.e. you
are not working with LAMMPS dump files), skip the `conda install ovito`
line and install with `pip install -e .[dev,ase,doc]` instead.

## Running tests

```bash
pytest # full suite
pytest -m "not slow" # skip slow tests
pytest -m unit # unit tests only
pytest -m integration # end-to-end on fixture trajectories
pytest --cov=wetting_angle_kit
```

Fixture trajectories live under `tests/trajectories/`. Integration tests
read these files and assert on contact-angle values within reasonable
bands; if you add a new method or change a numerical default, expect to
update those tolerances.

## Code style and quality

The project uses pre-commit to enforce a consistent style. After
`pre-commit install`, every commit runs:

- `ruff` (lint + format)
- `mypy` in strict mode on `src/wetting_angle_kit/`
- `codespell`
- RST and YAML sanity checks

Run all hooks manually with:

```bash
pre-commit run --all-files
```

Other conventions:

- Public functions and classes get NumPy-style docstrings with
Parameters / Returns / Raises sections.
- Type hints are required (`disallow_untyped_defs = true`).
- Prefer raising `ValueError` (or a more specific exception) over
`assert` for runtime invariants; asserts are removed under `python -O`.

## Adding a new parser

The parser ABC is in
[src/wetting_angle_kit/parsers/base.py](src/wetting_angle_kit/parsers/base.py).
Implement `parse()`, `frame_count()`, and (if applicable) `box_size`. The
factory in
[src/wetting_angle_kit/parsers/factory.py](src/wetting_angle_kit/parsers/factory.py)
dispatches by file extension — add your extension there. Mirror the existing
parsers' handling of orthogonal cells and periodic boundary conditions.

## Adding a new contact-angle method

Subclass `BaseContactAngleAnalyzer`
([src/wetting_angle_kit/contact_angle_methods/analyzer.py](src/wetting_angle_kit/contact_angle_methods/analyzer.py))
and register it in the analyzer factory so it can be picked up by name.
Add an integration test in `tests/test_contact_angle_methods/` that
exercises the method on one of the fixture trajectories.

## Pull requests

1. Branch off `main`.
2. Add tests for any new behaviour.
3. Run `pre-commit run --all-files` and `pytest` locally before pushing.
4. Open a pull request describing the change and linking any related
issue.
41 changes: 33 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
# wetting_angle_kit

[![Tests](https://github.com/Matgenix/wetting_angle_kit/actions/workflows/testing.yml/badge.svg)](https://github.com/Matgenix/wetting_angle_kit/actions/workflows/testing.yml)
[![codecov](https://codecov.io/gh/Matgenix/wetting_angle_kit/branch/main/graph/badge.svg)](https://codecov.io/gh/Matgenix/wetting_angle_kit)
[![PyPI version](https://img.shields.io/pypi/v/wetting_angle_kit.svg)](https://pypi.org/project/wetting_angle_kit/)
[![Python versions](https://img.shields.io/pypi/pyversions/wetting_angle_kit.svg)](https://pypi.org/project/wetting_angle_kit/)
[![License: BSD 3-Clause](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](LICENSE)
[![Documentation](https://img.shields.io/badge/docs-matgenix.github.io-blue)](https://matgenix.github.io/wetting-angle-kit)

wetting_angle_kit provides modular tools to parse MD trajectories (LAMMPS dump, XYZ, ASE) and compute droplet contact angles using two complementary approaches:

1. Sliced Method (per-frame circle fit) – robust against transient shape changes.
1. Slicing Method (per-frame circle fit) – robust against transient shape changes.
2. Binning Density Method – averages frames into a density field for a single representative angle.

The documentation is available [here](https://matgenix.github.io/wetting-angle-kit), you can find examples and tutorials.
Expand All @@ -13,7 +20,7 @@ The documentation is available [here](https://matgenix.github.io/wetting-angle-k

Before installing wetting_angle_kit, ensure you have the following prerequisites:

1. **Python 3.9 or higher**: Make sure you have Python 3.9 or higher installed on your system.
1. **Python 3.10 or higher**: Make sure you have Python 3.10 or higher installed on your system.
2. **Conda**: Ensure you have Conda installed. If not, you can install it from [here](https://docs.conda.io/en/latest/miniconda.html).

Core (only to analyse simple xyz trajectories):
Expand Down Expand Up @@ -47,19 +54,37 @@ conda install --strict-channel-priority -c https://conda.ovito.org -c conda-forg


```python
from wetting_angle_kit import (
XYZParser, SlicedContactAngleAnalyzer, BinningContactAngleAnalyzer,
detect_parser_type, contact_angle_analyzer
from wetting_angle_kit.contact_angle_methods import (
BinningContactAngleAnalyzer,
SlicingContactAngleAnalyzer,
)
from wetting_angle_kit.parsers import XYZParser, XYZWaterFinder

trajectory_file = "trajectory.xyz"

# Identify water oxygen atoms by neighbor count. ``particle_type_wall``
# lists the symbols of the substrate atoms so they are excluded.
finder = XYZWaterFinder(trajectory_file, particle_type_wall=["C"])
oxygen_ids = finder.get_water_oxygen_indices(frame_index=0)

parser = XYZParser(trajectory_file)

sliced = SlicedContactAngleAnalyzer(parser, output_repo="out_sliced", atom_indices=oxygen_ids, droplet_geometry="spherical", delta_gamma=5)
results = sliced.analyze(frame_range=range(0, 50))
slicing = SlicingContactAngleAnalyzer(
parser,
output_dir="out_slicing",
atom_indices=oxygen_ids,
droplet_geometry="spherical",
delta_gamma=5,
)
results = slicing.analyze(frame_range=range(0, 50))
print(results["mean_angle"], results["std_angle"])

binning = BinningContactAngleAnalyzer(parser, output_dir="out_binned", atom_indices=oxygen_ids, droplet_geometry="spherical")
binning = BinningContactAngleAnalyzer(
parser,
output_dir="out_binned",
atom_indices=oxygen_ids,
droplet_geometry="spherical",
)
results_binning = binning.analyze(frame_range=range(0, 200))
print(results_binning["mean_angle"], results_binning["std_angle"])
```
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# wetting_angle_kit Documentation

This package provides both *sliced* and *binning* methods to compute the contact angle of droplets from molecular dynamics simulations.
This package provides both *slicing* and *binning* methods to compute the contact angle of droplets from molecular dynamics simulations.

## Structure
- `examples/`: Ready-to-run example scripts and notebooks
Expand Down
Binary file removed docs/build/doctrees/API/index.doctree
Binary file not shown.
Binary file removed docs/build/doctrees/environment.pickle
Binary file not shown.
Binary file removed docs/build/doctrees/examples/index.doctree
Binary file not shown.
Binary file removed docs/build/doctrees/index.doctree
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed docs/build/doctrees/introduction/index.doctree
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed docs/build/doctrees/tutorials/index.doctree
Binary file not shown.
4 changes: 0 additions & 4 deletions docs/build/html/.buildinfo

This file was deleted.

4 changes: 0 additions & 4 deletions docs/build/html/.buildinfo.bak

This file was deleted.

Loading
Loading