Skip to main content

mofmotion

Structural motion analysis for flexible metal–organic frameworks. Given crystal structures of the same framework in two or more states, mofmotion identifies which structural units move, how they move, by how much, and how the motion changes pores and local chemical environments.

Install

pip install mofmotion

Requires Python ≥ 3.11 with numpy, scipy, pymatgen and spglib.

For development or the unreleased version:

git clone https://github.com/owenxyzhang-seseTJ/mofmotion
cd mofmotion
pip install -e '.[dev]'

Inside iCloud Drive, .pth files are hidden and editable installs break; install a copy instead:

uv pip install --python .venv/bin/python --no-deps --no-build-isolation --reinstall .

Command line

mofmotion compare closed.cif open.cif --html report.html
Command Purpose
clean SRC DST Repair the CIF text: encoding, BOM, line endings, typographic and control characters
audit CIF... Read-only audit: input flags, symmetry expansion check, short contacts, space group
bonds CIF... Periodic bond graph with covalent, coordination and uncertain edges
units CIF... Structural units: nodes, linkers, terminal ligands, free components
pores CIF... LCD, PLD, channel dimensionality, accessible volume, bottleneck window, pore lining
compare CIF CIF... Motion, pores, gating, classification and attribution for consecutive states

clean never overwrites its source; pass --encoding gb18030 when detection fails. audit cleans the text in memory, accepts --json, --block and --tol (fractional tolerance for coincident sites, default 0.01), and exits with 1 if any report has errors. pores and compare take --probe (probe radius, default 1.65 Å, CO2) and --spacing (grid spacing, default 0.2 Å). Free guests are removed before pore calculation and listed in the result; pores --keep-guests keeps them. compare --no-attribution skips the slower attribution step. Summaries and reports are written in Chinese.

Python

from pathlib import Path

from mofmotion import audit_cif, bond_graph, compare, html_report, structural_units

report = audit_cif("open.cif")
print(report.summary())
if report.ok:
    graph = bond_graph(report.structure)
    units = structural_units(graph)
    for k, unit in enumerate(units.units):
        print(unit.kind, unit.formula, unit.dimension, units.coords(k).shape)

c = compare("closed.cif", "open.cif")
print(c.summary())
for f in c.findings:
    print(f.label, f.evidence)
Path("report.html").write_text(html_report(c), encoding="utf-8")

compare accepts paths or pymatgen Structure objects; compare_states chains more than two states.

Method

Audit. The CIF is parsed after text cleaning. Symmetry operations are expanded independently and checked against pymatgen in site count, weighted composition and cell. Periodic short contacts below 1.0 Å (H–H), 0.75 Å (with H) or 1.0 Å (heavy atoms) are listed as clashes. The space group is determined with spglib (symprec 0.01) for ordered structures only. Raw atom_site rows are flagged as missing_data, outside_cell, partial_occupancy, disorder_group, special_positions, symmetry_duplicates or periodic_duplicates. Flags are evidence; removing guests or choosing a disorder component is left to the caller.

Bond graph. Each edge Bond(i, j, image, length, kind, reason) joins site i to site j translated by the integer lattice vector image. With q = d / (r_i + r_j) and Cordero covalent radii:

Pair Certain Uncertain
non-metal–non-metal q ≤ 1.15, covalent 1.15 < q ≤ 1.30
metal–non-metal q ≤ 1.20, coordination 1.20 < q ≤ 1.35
metal–metal — q ≤ 1.15

Uncertain edges carry a reason: short, distance, disorder or metal-metal. Hydrogen bonds are not edges.

Structural units. Units are built from certain edges plus edges uncertain only because of disorder or metal-metal. A node is a metal together with directly coordinated carbon-free species with a single non-H atom (μ-O, OH, H2O, halide); metals bonded only to carbon belong to their ligand. Every other covalent molecule is a linker, terminal ligand or free component, depending on whether it connects to two or more, one or no other units. Coordinated solvent therefore belongs to the framework. Finite units are unwrapped across cell boundaries and placed with their centroid in the cell; dimension is the rank of periodic cycles. Occupancies are kept, so a half-occupied guest reads as C0.5H2O0.5. A free component is not necessarily a removable solvent.

Motion. If one state is a supercell of the other (by the count of its rarest framework element), the smaller cell is expanded over all Hermite normal form matrices of that index. The B cell is transformed to the A setting by an integer matrix and framework atoms are paired. The report gives the fraction of pairable heavy framework atoms that were paired and the cost gap per atom to the next distinct pairing; below 95 % or 0.05 Å the result is flagged as unreliable. Inputs that fail the audit are rejected. Global rotation is removed by polar decomposition G = UR, and U gives principal strains and volume change. Each unit is fitted as a rigid body, giving translation, rotation angle, rotation mode (spin, tilt, rotate), residual and non-affine displacement. Bond lengths, angles and torsions are compared on the A topology; those spanning more than one unit are hinges. Bond formation, cleavage and coordination number changes are listed separately.

Pores. On a fractional grid, each point stores its distance to the nearest van der Waals surface. Points reachable by the probe centre are joined into channels under periodic boundaries, and the rank of their cycles gives the dimensionality. LCD = 2 max d. PLD is the largest threshold that still percolates, found by bisection; the voxels at that threshold form the bottleneck and the surrounding atoms the window. Both states are evaluated on the same grid, so channels correspond by voxel overlap (kept, closed, opened, split, merged). Gating is decided by whether the probe percolates in each state. Accuracy is about one grid spacing.

Classification and attribution. classify turns motion and pore changes into findings (breathing, swelling or shrinking, network sliding, unit rotation, translation or deformation, node hinge, torsion, coordination change, guest change, gating, pore change, local environment), each with the values that triggered it. Thresholds live in THRESHOLDS. Attribution splits A → B into the lattice and each group of moving units, builds hybrid structures, and reports for each factor a forward effect (applying it alone), a backward effect (removing it from B) and an interaction term. This is a geometric sensitivity analysis, not a unique decomposition.

Limits

Two end-point structures give a geometric difference, not a pathway, barrier or gate-opening pressure. Atom mapping can be ambiguous when the framework has automorphisms. Moving fragments independently breaks shared bonds, so pore changes have no unique share per motion.

Development

.venv/bin/ruff format src tests && .venv/bin/ruff check . && .venv/bin/python -I -m pytest -q

Module layout is described in docs/architecture.md. Released under the MIT license.

Metadata

Release files for mofmotion 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mofmotion 0.1.2
File Size Uploaded Provenance
mofmotion-0.1.2.tar.gz 70.6 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for mofmotion 0.1.2
File Interpreter ABI Platform
mofmotion-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 110.2 kB

Release files / mofmotion-0.1.2.tar.gz

Download URL mofmotion-0.1.2.tar.gz
Size 70.6 kB
Tags Source
SHA-256 checksum
How to use checksums
e787be024008d9ecc84941981be9b7af079a7f28720a9b628806c41ef112f7e2
BLAKE2b-256 checksum
How to use checksums
487e858cdfcadca9df0e1b80f22dca5134be791f03f2f85e796b07ecf7c09d0e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.13

Release files / mofmotion-0.1.2-py3-none-any.whl

Download URL mofmotion-0.1.2-py3-none-any.whl
Size 39.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2d472ffb6f3b11f63df2438f349fa0a46ef36b9c811443379c972fc33a1e9b4d
BLAKE2b-256 checksum
How to use checksums
5d69e33f7285d3e4d19c3628f4b81cf567cba5b098febf90c0a9305dbda4666b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.13

Security for mofmotion 0.1.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page