Physics-inspired geometry initialization for molecular and non-covalent optimization warm starts.
Geonit prepares chemically reasonable starting coordinates before a slower downstream optimizer, such as xTB, a force-field optimizer, or a quantum chemistry workflow, is allowed to spend real compute. It is designed for cases where the input geometry is usable enough to infer connectivity, but may contain stretched bonds, compressed angles, steric clashes, or fragile non-covalent contacts.
The central design choice is conservative: Geonit should make the smallest useful correction that improves optimizer readiness without moving the system into a different local minimum.
Repository:
https://github.com/Prasanna163/Geoinit.git
Geonit takes molecular coordinates, infers topology and chemically important structure classes, scores the geometry with a lightweight physical functional, and returns either relaxed coordinates or a selected warm-start candidate.
It is not a replacement for xTB, DFT, molecular dynamics, or a production force field. It is a pre-optimizer and selector for safer initial geometries.
Typical use cases:
- Cleaning distorted XYZ geometries before xTB or DFT optimization.
- Detecting basic geometry quality issues before running expensive jobs.
- Comparing raw, UFF, guarded Geonit, and Geonit initialization paths.
- Preparing non-covalent complexes while preserving fragment identity.
- Running reproducible benchmark suites for geometry-initialization research.
- Embedding geometry warm-start selection inside a larger Python pipeline.
Geometry optimization often begins from structures that were drawn manually, retrieved from approximate coordinates, generated from templates, or composed by placing fragments near each other. These structures can contain local geometry errors that are small enough to look plausible but large enough to waste optimization steps or push an optimizer into a different basin.
Geonit targets this early stage. It emphasizes:
- Bond-length sanity from covalent-radius rules.
- Valence-angle consistency from coordination environments.
- Clash removal without excessive global movement.
- Rigid-fragment preservation for aromatic rings, carbonyls, amides, and linear multiple-bond fragments.
- Non-covalent contact awareness for hydrogen-bond, polar, halogen, and pi-type interactions.
- Raw-coordinate fallback when the selector cannot prove that a candidate is safe.
The important distinction is basin safety. UFF and similar initializers can produce larger step savings, but they may also move a distorted structure into a different local minimum. Geonit is built around avoiding that tradeoff.
Geonit generates a small portfolio of candidates, scores each candidate for chemical feasibility and movement from the original geometry, and selects a safe candidate only when it passes hard filters. Otherwise it falls back to the raw input coordinates.
Selection considers:
- Chemical strain score.
- Clash score.
- Contact graph damage.
- Class-specific risk score.
- Raw-coordinate RMSD movement.
- Fragment drift in complexes.
- Estimated downstream xTB step benefit.
The scalar Geonit functional is a weighted sum of bonded, angular, steric, and dispersion-like terms:
E_total = w_bond * E_bond
+ w_angle * E_angle
+ w_clash * E_clash
+ w_disp * E_disp
+ w_coul * E_coul
+ w_rigid * E_rigid
+ w_anchor * E_anchor
Default weights are:
bond=10.0, angle=5.0, clash=1.0, disp=0.1, coul=0.0
Implemented terms include:
| Term | Purpose |
|---|---|
| Bond stretch | Penalizes deviation from covalent-radius-based reference lengths. |
| Angle bend | Penalizes deviation from coordination-based ideal angles. |
| Clash wall | Penalizes compressed non-bonded contacts. |
| Dispersion | Adds damped attractive non-bonded stabilization. |
| Rigid restraints | Preserve detected rigid subgraphs and chemically fragile fragments. |
| Anchors | Stabilize fragment/contact placement during candidate cleanup. |
Geonit detects structure classes that need extra care:
- Linear multiple-bond chains such as CO2-like fragments.
- Carbonyls and amides.
- Aromatic rings and planar ring systems.
- Rigid subgraphs that should not be distorted internally.
- Multi-fragment non-covalent complexes.
- Hydrogen-bond, polar, halogen, and pi-contact motifs.
These classes are used by guards, candidate generation, and selector rejection rules.
Geonit V1.0 adds an opt-in vectorized energy/gradient engine. The same kernel can run on:
- NumPy, always available and used as the safe CPU fallback.
- PyTorch CPU, when PyTorch is installed.
- CUDA, when PyTorch and a CUDA device are available.
The auto backend uses problem size to avoid paying GPU transfer overhead for
small molecules.
Backend controls:
--engine scalar # legacy scalar functional
--engine auto # size-aware vectorized backend selection
--engine numpy # force NumPy vectorized backend
--engine torch-cpu # force PyTorch CPU backend if available
--engine cuda # request CUDA, falling back safely if unavailable
Physics profile controls:
--profile v1 # frozen V1 behavior
--profile v2 # improved V2 physics
The V2 physics profile includes curvature-matched harmonic angle bending, bond-order-aware stiffness, and Becke-Johnson-style damped dispersion.
The repository includes benchmark evidence packs for the V0.8 and V1.0 selectors. The benchmark suite contains 21 molecules and complexes with 10 distortions each, for 210 total trials.
Headline V0.8 result:
Geonit V0.8
xTB success: 210/210
same-minimum: 210/210
different-basin risk: 0/210
total steps: 6506 vs 6746 raw
accepted non-raw: 81/210
Headline V1.0 result:
Geonit V1.0
xTB success: 210/210
same-minimum: 210/210
different-basin risk: 0/210
total steps: 6506 vs 6746 raw
accepted non-raw: 139/210
Interpretation:
- V1.0 preserves the V0.8 selector safety result.
- V1.0 increases useful non-raw acceptance from 81 to 139 trials.
- Complexes become warm-startable under the safe V1.0 selector path.
- UFF has larger step savings in this suite, but much higher basin risk.
Evidence files live under:
benchmark_results_v0_8/
preliminary_results_v1_0/
project_tracking/
git clone https://github.com/Prasanna163/Geoinit.git
cd Geoinit
python -m pip install -e .The package requires Python 3.10 or newer.
Core dependencies:
numpy
scipy
pandas
matplotlib
Optional dependencies:
torchfor PyTorch CPU or CUDA acceleration.rdkitfor UFF baseline benchmarking.xtbexecutable onPATHfor downstream xTB validation benchmarks.joblibfor parallel benchmark execution when--workersis not1.
python -m pip install git+https://github.com/Prasanna163/Geoinit.gitpython -m pip install build wheel
python -m buildIf you do not use build, the wheel can also be produced through pip:
python -m pip wheel . --no-deps -w distThe package exposes the console command:
geonit
For compatibility with earlier scripts, geoinit is also installed as an alias.
It can also be run as a module:
python -m geoinit --helpGeonit currently exposes command-line entry points, not HTTP or FastAPI endpoints.
Top-level commands:
geonit relax
geonit report
geonit benchmark
geonit benchmark-xtb
geonit relax-complex
Legacy scalar relaxation:
geonit relax examples\water_bad.xyz -o outputs\water_relaxed.xyzGeonit V1.0:
geonit relax examples\water_bad.xyz --mode select -o outputs\water_select.xyzGeonit V0.9 complex-candidate expansion:
geonit relax examples\complexes\water_dimer_bad.xyz --mode select-v0.9 -o outputs\water_dimer_v0_9.xyzGeonit V1.0:
geonit relax examples\methanol_bad.xyz --mode select-v1.0 -o outputs\methanol_v1_0.xyzUseful relaxation options:
--maxiter N
--sigma VALUE
--weights-bond VALUE
--weights-angle VALUE
--weights-clash VALUE
--weights-disp VALUE
--clash-mode exp|compact
--mode legacy|select|select-v0.9|select-v1.0
geonit report examples\methanol_bad.xyzThe report prints:
- Formula and atom count.
- Detected bonds, angles, and non-bonded pairs.
- Total Geonit energy and component breakdown.
- Bond-error and clash diagnostics.
- A detailed bond table.
- Highest angle deviations.
geonit benchmark --suite medium --out outputs\geoinit_geometric_summary.csvSuites:
small
medium
The xTB benchmark shells out to the xtb executable and compares raw, UFF,
guarded Geonit, and Geonit initialization paths.
geonit benchmark-xtb --suite medium --distortions 10 --baselines raw uff geoinit_guarded geoinit_select --out preliminary_results_v1_0\geoinit_v1_0_casewise_results.csvV1.0 selector benchmark:
geonit benchmark-xtb --suite medium --distortions 10 `
--baselines raw uff geoinit_guarded geoinit_select `
--select-version v1_0 `
--engine auto `
--profile v2 `
--workers -1 `
--out preliminary_results_v1_0\geoinit_v1_0_casewise_results.csvImportant benchmark options:
--suite small|medium
--distortions N
--baselines raw uff geoinit_guarded geoinit_select
--select-version v0_8|v0_9|v1_0
--engine scalar|auto|numpy|torch-cpu|cuda
--profile v1|v2
--workers N
--charge INT
--uhf INT
--xtb-method gfn0|gfn1|gfn2
geonit relax-complex examples\dmf_bad.xyz examples\water_bad.xyz -o outputs\dmf_water_relaxed.xyzThis command reads host and guest XYZ files, stacks them into one complex, and applies the complex-aware relaxation path.
Geonit can be used as a normal Python module inside scripts, notebooks, workflow managers, or backend services.
from geoinit.core.atoms import Molecule
mol = Molecule.from_xyz("examples/water_bad.xyz")
print(mol.symbols)
print(mol.coords)
mol.to_xyz("outputs/copy.xyz", comment="Copied by Geonit")from geoinit.core.atoms import Molecule
from geoinit.core.topology import Topology
mol = Molecule.from_xyz("examples/methanol_bad.xyz")
topology = Topology(mol.symbols, mol.coords)
print(topology.bonds)
print(topology.angles)
print(topology.nonbonded_pairs)from geoinit.core.atoms import Molecule
from geoinit.optimize.relax import relax
mol = Molecule.from_xyz("examples/water_bad.xyz")
result = relax(mol.symbols, mol.coords, maxiter=500)
print(result.success)
print(result.n_steps)
print(result.final_energy)from geoinit.core.atoms import Molecule
from geoinit.optimize.selector import select_initial_geometry
mol = Molecule.from_xyz("examples/water_bad.xyz")
selection = select_initial_geometry(mol.symbols, mol.coords)
print(selection.selected_name)
print(selection.accepted)
coords_for_xtb = selection.selected_coordsfrom geoinit.core.atoms import Molecule
from geoinit.optimize.selector import select_initial_geometry, v0_9_selection_policy
mol = Molecule.from_xyz("examples/complexes/water_dimer_bad.xyz")
selection = select_initial_geometry(
mol.symbols,
mol.coords,
policy=v0_9_selection_policy(),
)from geoinit.core.atoms import Molecule
from geoinit.optimize.selector import select_initial_geometry, v1_0_selection_policy
mol = Molecule.from_xyz("examples/complexes/benzene_co2_bad.xyz")
selection = select_initial_geometry(
mol.symbols,
mol.coords,
policy=v1_0_selection_policy(),
)from geoinit.core.atoms import Molecule
from geoinit.optimize.relax import relax
mol = Molecule.from_xyz("examples/methanol_bad.xyz")
result = relax(
mol.symbols,
mol.coords,
engine="auto",
profile="v2",
)Use Geonit before launching xTB or another downstream optimizer:
from pathlib import Path
from geoinit.core.atoms import Molecule
from geoinit.core.io_xyz import write_xyz
from geoinit.optimize.selector import select_initial_geometry, v1_0_selection_policy
def prepare_for_xtb(input_xyz: str, output_xyz: str) -> str:
mol = Molecule.from_xyz(input_xyz)
selection = select_initial_geometry(
mol.symbols,
mol.coords,
policy=v1_0_selection_policy(),
)
write_xyz(
output_xyz,
mol.symbols,
selection.selected_coords,
comment=f"Geonit selected={selection.selected_name} accepted={selection.accepted}",
)
return output_xyz
prepared = prepare_for_xtb("input.xyz", "prepared.xyz")Then call your downstream optimizer on prepared.xyz.
The safest integration contract is:
- Keep the original raw coordinates.
- Run Geonit selection.
- Record
selected_name,accepted,fallback_reason, and policy version. - Use
selected_coordsfor downstream optimization. - Compare final downstream energy against raw when validating a new chemistry class.
Minimal result metadata:
metadata = {
"selector_version": selection.policy.selector_version,
"selected_name": selection.selected_name,
"accepted": selection.accepted,
"fallback_reason": selection.fallback_reason,
"candidate_count": len(selection.candidates),
}The repository does not currently define REST endpoints. To expose Geonit through an API, wrap the Python API rather than shelling out unless you need strict process isolation.
Example FastAPI-style wrapper:
from fastapi import FastAPI
from pydantic import BaseModel
from geoinit.optimize.selector import select_initial_geometry, v1_0_selection_policy
app = FastAPI()
class GeometryRequest(BaseModel):
symbols: list[str]
coords: list[list[float]]
@app.post("/api/geonit/select-v1.0")
def select_v1_geometry(req: GeometryRequest):
selection = select_initial_geometry(
req.symbols,
req.coords,
policy=v1_0_selection_policy(),
)
return {
"selected_name": selection.selected_name,
"accepted": selection.accepted,
"fallback_reason": selection.fallback_reason,
"coords": selection.selected_coords.tolist(),
}Suggested service endpoints if you build a backend:
GET /api/health
POST /api/geonit/report
POST /api/geonit/relax
POST /api/geonit/select
POST /api/geonit/select-v1.0
If another application invokes Geonit as a subprocess, use python -m geoinit
for interpreter consistency:
python -m geoinit relax input.xyz --mode select-v1.0 -o prepared.xyzThis avoids ambiguity when multiple Python environments are installed.
Geonit/
|-- pyproject.toml
|-- README.md
|-- Geonit/
| |-- __init__.py
| |-- __main__.py
| |-- cli/
| | `-- main.py
| |-- core/
| | |-- atoms.py
| | |-- bond_rules.py
| | |-- classes.py
| | |-- constraints.py
| | |-- contact_graph.py
| | |-- geometry.py
| | |-- io_xyz.py
| | |-- params.py
| | `-- topology.py
| |-- energy/
| | |-- angle.py
| | |-- bond.py
| | |-- engine.py
| | |-- functional.py
| | `-- nonbonded.py
| |-- compute/
| | `-- backends.py
| |-- optimize/
| | |-- candidates.py
| | |-- complex.py
| | |-- guards.py
| | |-- project.py
| | |-- relax.py
| | `-- selector.py
| |-- benchmark/
| | |-- distortion.py
| | |-- metrics.py
| | |-- plot_benchmark.py
| | |-- select_report.py
| | |-- suite.py
| | |-- uff_runner.py
| | `-- xtb_runner.py
| `-- calibration/
| `-- class_stats.py
|-- examples/
|-- tests/
|-- benchmark_results_v0_8/
|-- benchmark_results_v0_9/
|-- preliminary_results_v1_0/
`-- project_tracking/
pyproject.toml defines the package name and console script:
[project]
name = "geonit"
[project.scripts]
geonit = "geoinit.cli.main:main"
geoinit = "geoinit.cli.main:main"The distribution is named geonit, while the import package remains
geoinit for backward compatibility. Package discovery is explicitly
restricted to geoinit*, so benchmark outputs,
tracking files, examples, and tests are not accidentally included as importable
packages in the wheel.
[tool.setuptools.packages.find]
include = ["geoinit*"]Recommended release workflow:
python -m pytest
python -m build
python -m twine check dist\*For a private/internal install:
python -m pip install dist\geonit-1.0.0-py3-none-any.whlRun the unit test suite:
python -m pytestCurrent local verification for this checkout:
52 passed
The xTB benchmarks require the external xtb executable and are intentionally
separate from the fast unit test suite.
Important generated artifacts:
benchmark_results_v0_8/geoinit_v0_8_casewise_results.csv
benchmark_results_v0_8/FINAL_RESULTS.md
preliminary_results_v1_0/geoinit_v1_0_casewise_results.csv
preliminary_results_v1_0/geoinit_v1_0_benchmark_summary.csv
preliminary_results_v1_0/geoinit_v1_0_candidate_rows.csv
preliminary_results_v1_0/geoinit_v1_0_candidate_class_summary.csv
preliminary_results_v1_0/compare_v0_8_vs_v1_0.py
Use the casewise CSVs for per-trial inspection and the candidate files to understand why a selector accepted or rejected a candidate.
Geonit is intentionally cautious.
The goal is not to maximize apparent step savings on every distorted input. The goal is to provide a useful warm start when that warm start can be justified, and to fall back to the raw geometry when safety is uncertain.
That philosophy is why the benchmark tables track same-minimum rate and different-basin risk, not only convergence speed.
- Input support is currently centered on XYZ coordinates.
- The built-in topology inference is geometry/radius based and can be confused by severely pathological structures.
- xTB validation requires an external
xtbinstallation. - UFF baseline comparison requires RDKit.
- The repository exposes a CLI and Python API, not a built-in web service.
- Licensing metadata and a standalone
LICENSEfile should be added before a formal public release if redistribution terms need to be explicit.
Short description:
Geonit is a physics-inspired molecular geometry initializer that generates
basin-safe warm-start coordinates for downstream optimization workflows.
Long description:
Geonit combines radius-based topology inference, chemical-class detection,
lightweight energy scoring, guarded relaxation, and conservative candidate
selection to prepare molecular and non-covalent complex geometries before
expensive downstream optimization. Its selector-first workflow is benchmarked
against raw xTB and UFF baselines with emphasis on same-minimum preservation and
different-basin risk.