Metadata-Version: 2.4
Name: ase_qsm
Version: 0.2.3
Summary: QSM-style chain-of-states path optimizer for ASE with tangent-smoothed chain updates, climbing-image saddle search, adaptive redistribution, and SCF-failure resilience.
Author: ss0832
License-Expression: GPL-3.0-or-later
Project-URL: Homepage, https://github.com/ss0832/ase_qsm
Project-URL: Repository, https://github.com/ss0832/ase_qsm
Project-URL: Bug Tracker, https://github.com/ss0832/ase_qsm/issues
Keywords: computational chemistry,minimum energy path,transition state,nudged elastic band,string method,QSM,ASE
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Chemistry
Classifier: Topic :: Scientific/Engineering :: Physics
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE.md
Requires-Dist: numpy>=1.24
Requires-Dist: scipy>=1.10
Requires-Dist: ase>=3.23
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Provides-Extra: ts-refine
Requires-Dist: ASE-RSIRFO>=0.1.3; extra == "ts-refine"
Provides-Extra: gfn-xtb
Requires-Dist: tblite[ase]>=0.3; extra == "gfn-xtb"
Dynamic: license-file

# ase_qsm

[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)

**QSM-style chain-of-states path optimizer for ASE.**

`ase_qsm` optimizes discrete chain-of-states paths between two molecular
configurations. 



## Installation

```bash
pip install ase_qsm
```

From a source checkout:

```bash
git clone https://github.com/ss0832/ase_qsm
cd ase_qsm
pip install -e ".[dev]"
pytest
```

Requires Python 3.10+, NumPy 1.24+, SciPy 1.10+, ASE 3.23+.

## Basic example

Prepare endpoint geometries as external structure files, for example
`reactant.xyz` and `product.xyz`. The two endpoint files must use the
same atom count and the same atom ordering; use `auto_match_atoms=True`
only when you intentionally want `ase_qsm` to reorder atoms before path
construction.

```python
from ase.io import read
from ase.calculators.emt import EMT
from ase_qsm import QSM, finalize_path

reactant = read("reactant.xyz")
product = read("product.xyz")

# Replace EMT() with the calculator appropriate for the endpoint files.
calc = EMT()

# QSM with 11 images and explicit run/report settings.
# The run-level controls mirror the examples/08 suite invocation:
#   --steps 32 --output-dir lst_reaction_suite_results --full-hessian-for none
qsm = QSM(
    images=[reactant, product],
    calculator=calc,
    n_images=11,
    fmax=0.05,
    max_iter=32,
    blend_ratio=0.5,            # 50/50 FIRE/RFO
    hessian_mode="model",       # Fischer model Hessian per node
    redistribute_interval=1,    # redistribute nodes every 1 eligible steps
    trajectory="qsm.traj",
)
qsm.run()

report = finalize_path(
    qsm,
    "results",
    refine_ts=False,
    full_hessian_for="none",
)
print("TS candidates:", report.ts_candidates)

for k, atoms in enumerate(qsm.images):
    print(f"image {k}: E = {atoms.get_potential_energy():.6f} eV")
```

### Additional usage patterns

Endpoint-only input can omit `n_images`. In that case the constructor
infers the initial image count from the configured pre-climb target
chord and minimum image count. Replace `EMT()` with the calculator
appropriate for the system.

```python
from ase.io import read
from ase.calculators.emt import EMT
from ase_qsm import QSM

reactant = read("reactant.xyz")
product = read("product.xyz")

qsm = QSM([reactant, product], calculator=EMT(), fmax=0.05)
converged = qsm.run()
print("converged:", converged)
print("images:", len(qsm.images))
```

For a pre-built image stack, pass all images directly. The constructor
then ignores `n_images` and `interpolation`.

```python
from ase.io import read
from ase.calculators.emt import EMT
from ase_qsm import QSM

images = read("initial_path.xyz", index=":")
qsm = QSM(images, calculator=EMT(), fix_endpoints="both")
qsm.run(steps=100)
```

Use `QSMConfig` when several controls should be recorded as one object.
The example below enables climbing-image selection and monitor
redistribution, while keeping final reporting limited by skipping
post-run Hessian and TS-refinement work.

```python
from ase.io import read
from ase.calculators.emt import EMT
from ase_qsm import QSM, QSMConfig, finalize_path

reactant = read("reactant.xyz")
product = read("product.xyz")
calc = EMT()

cfg = QSMConfig(
    climb=True,
    climb_use_rsirfo=True,
    max_climbers=3,
    redistribute_method="monitor",
    adaptive_image_insertion=True,
    adaptive_max_images=40,
)

qsm = QSM([reactant, product], calculator=calc, config=cfg)
qsm.run()
report = finalize_path(
    qsm,
    "qsm_results",
    refine_ts=False,
    full_hessian_for="none",
)
print(report.ts_candidates)
```

By default, `QSM.run()` writes monitoring files under
`qsm_path_history/`. Set `path_history_dir=None` and
`path_metrics_enabled=False` for an in-memory-only run. If
`path_history_dir=None` but path metrics remain enabled, the metric CSVs
are written in the current working directory as `qsm_path_metrics*.csv`.

| File | Written by | Purpose |
|---|---|---|
| `qsm_path_history/path_initial.xyz` | constructor | Initial interpolated or supplied path with per-image metadata. |
| `qsm_path_history/path_iter_*.xyz` | accepted QSM iterations | One complete path snapshot per saved iteration. |
| `qsm_path_history/qsm.log` | constructor/run loop | Persistent text log in addition to `logfile`. |
| `qsm_path_history/path_metrics.csv` | accepted QSM iterations | Per-image diagnostics such as energy, force, chord, and role labels. |
| `qsm_path_history/path_metrics_summary.csv` | accepted QSM iterations | One summary row per saved iteration. |
| `qsm_path_history/path_metrics_wide.csv` | accepted QSM iterations | Wide-format monitoring table for plotting and spreadsheet inspection. |



Three or more input structures are treated as waypoints by default. The
constructor uses the waypoint polyline as a guide and resamples it to the
requested or inferred total image count. This can increase or decrease the
number of supplied structures.

```python
from ase.io import read
from ase.calculators.emt import EMT
from ase_qsm import QSM

waypoints = read("waypoints.xyz", index=":")
qsm = QSM(
    waypoints,
    calculator=EMT(),
    n_images=17,                 # total images after waypoint resampling
    interpolation="lst",
    fix_endpoints="both",
)
qsm.run(steps=32)
```

For a pre-built image stack that should be preserved exactly, opt into
image-stack mode. In this mode the constructor ignores `n_images` and
`interpolation`.

```python
from ase.io import read
from ase.calculators.emt import EMT
from ase_qsm import QSM

images = read("initial_path.xyz", index=":")
qsm = QSM(
    images,
    calculator=EMT(),
    initial_path_input_mode="images",
    fix_endpoints="both",
)
qsm.run(steps=32)
```

### Reaction-path preset

For ordinary reaction-path searches, use the `reaction_path` preset rather
than specifying the full set of QSM controls manually. This preset mirrors the
settings used by `examples/08_lst_reaction_suite.py` and is intended as the
main practical entry point for molecular reaction paths.

```python
from ase.io import read
from ase.calculators.emt import EMT
from ase_qsm import QSM, QSMConfig, finalize_path

reactant = read("reactant.xyz")
product = read("product.xyz")
calc = EMT()

cfg = QSMConfig.reaction_path(
    steps=32,       # QSM iteration limit used by the reaction-suite example
    max_images=40,  # optional upper bound for adaptive image insertion
)

qsm = QSM(
    [reactant, product],
    calculator=calc,
    config=cfg,
)

qsm.run()

report = finalize_path(
    qsm,
    "results",
    refine_ts=False,
    full_hessian_for="none",
)

print("TS candidates:", report.ts_candidates)
```


## References

- ASE: Hjorth Larsen et al., The atomic simulation environment—A Python library for working with atoms. Journal of Physics: Condensed Matter 29, 273002 (2017). DOI: 10.1088/1361-648X/aa680e

- ASE early interface paper: Bahn and Jacobsen, An object-oriented scripting interface to a legacy electronic structure code. Computing in Science & Engineering 4(3), 56–66 (2002). DOI: 10.1109/5992.998641

- Quadratic String Method: Burger and Yang, Quadratic string method for determining the minimum-energy path based on multiobjective optimization. The Journal of Chemical Physics 124(5), 054109 (2006). DOI: 10.1063/1.2163875

## License

GPL-3.0-or-later. See [`LICENSE`](LICENSE) and
[`NOTICE.md`](NOTICE.md) for details.

Copyright (C) 2026 ss0832.
