Metadata-Version: 2.4
Name: glomos
Version: 0.3.2
Summary: GLobal Optimization of MOlecular Systems
Author-email: Filiberto Ortiz Chi <fortiz666@gmail.com>, Aileen Garcia Cano <garciacanoaileen@gmail.com>, Gabriel Merino <gabriel.merino2@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/fortizchi/glomos
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: aegon[ani]>=1.3.10
Requires-Dist: growpal>=0.1.4
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Dynamic: license-file

# GLOMOS

**GLobal Optimization of MOlecular Systems — a genetic algorithm for the global optimization of atomic clusters and molecules, built on top of [AEGON](https://pypi.org/project/aegon/).**

GLOMOS orchestrates the genetic algorithm (initial population, crossover, mutation, structural deduplication, stop criteria) and delegates every energy evaluation and local optimization to AEGON's calculator registry. GLOMOS itself never talks to a quantum chemistry code or a potential directly — it only calls `aegon.calculators.build(calc_type, **kwargs)`, so any calculator AEGON supports is automatically available to the genetic algorithm. The GA operators themselves (crossover, mutation, and parent selection) live in `glomos.generation`, not AEGON — AEGON only supplies the random/symmetry-constrained structure generators, geometry utilities, and USR discrimination they're built on. Parent selection has two independent implementations: fitness-proportional roulette wheel (the conventional GEGA driver) and structural clustering (the GCLUS driver, which replaces fitness entirely with MBTR-descriptor/Ward clustering) — see [Genetic algorithm search](#genetic-algorithm-search) and [Clustering-based genetic algorithm search](#clustering-based-genetic-algorithm-search).

---

## Table of Contents

- [Features](#features)
- [Installation](#installation)
- [Dependencies](#dependencies)
- [The INPUT file](#the-input-file)
- [Supported calculators](#supported-calculators)
- [Stochastic kick search](#stochastic-kick-search)
- [Genetic algorithm search](#genetic-algorithm-search)
- [Clustering-based genetic algorithm search](#clustering-based-genetic-algorithm-search)
- [Rotamer / conformer search](#rotamer--conformer-search)
- [Citation](#citation)
- [Authors](#authors)
- [License](#license)

---

## Features

- **Four search drivers** — `heuristic_kick.py` (staged local-opt + discrimination, no evolutionary operators), `heuristic_ga.py` (roulette-wheel GA over clusters, "GEGA"), `heuristic_clustering.py` (structural-clustering GA over clusters, "GCLUS" — same operators as GEGA, but parent selection replaces fitness with MBTR/Ward clustering), `heuristic_ga_rotamers.py` (GA over dihedral angles for conformer search, "RTMR").
- **Own GA operators (`glomos.generation`)** — cluster crossover/mutation (`generation/cluster_crossover.py`/`cluster_mutant.py`, shared by GEGA and GCLUS — GCLUS additionally supports two children per crossover pair and chained multi-operator mutants), rotamer crossover/mutation (`generation/rotamer_crossover.py`/`rotamer_mutant.py`), fitness-proportional roulette selection and structural-clustering selection (both in `generation/selection.py`) all live in GLOMOS itself, not AEGON — AEGON only supplies the random/symmetry-constrained structure generators, geometry utilities, and USR discrimination they're built on.
- **Calculator-agnostic** — every driver dispatches through AEGON's registry: LJ, Sutton-Chen, TiO2, EMT, ANI, Gaussian, ORCA, MOPAC, GULP, VASP, selected by a single `calculator` key.
- **Unit-consistent** — one `units` key (`eV`/`atomic`/`kcal`) fixes energy and force together across every calculator.
- **Plain-text input files** — `INPUT_GLOMOS_<CALCULATOR>_<ALGORITHM>_<SYSTEM>.txt` (`GEGA`/`GCLUS`/`KICK`/`RTMR`), same convention as `solids/examples`.

---

## Installation

```bash
pip install glomos
```

Requires Python >= 3.10. GLOMOS depends on AEGON with the `[ani]` extra (installs `torch`/`torchani` for the ANI calculator); no separate installation step is needed.

---

## Dependencies

| Package | Role |
|---|---|
| [aegon](https://pypi.org/project/aegon/) `[ani]` | Structure generation, unit conversion, and the calculator registry (LJ, SC, TiO2, EMT, ANI, Gaussian, ORCA, MOPAC, GULP, VASP) |
| [growpal](https://pypi.org/project/growpal/) | MBTR descriptors and Ward agglomerative clustering (`libdescriptors`/`libclustering`), used by the GCLUS driver's structural-clustering parent selection |

All other dependencies (ASE, NumPy, SciPy, Numba, Pydantic, PyYAML, joblib, torch, torchani) come transitively through `aegon[ani]`; `dscribe`, `molsympy`, and `scikit-learn` come transitively through `growpal`.

---

## The INPUT file

GLOMOS reads a plain-text input file with a composition block and key-value
parameters (parsed by `aegon.libstdio.read_main_input`):

```
---COMPOSITION---
H  4
O  2
---COMPOSITION---

#EVOLUTIVE PARAMETERS:
nof_initpop             4    #Initial Population
nof_matings             2    #Number of matings
nof_mutants             2    #Number of mutants
nof_randoms             2    #Insertion of new individuals

#ENERGY UNITS: eV | atomic (Eh) | kcal (kcal/mol)
units                   kcal   #sets energy AND force units together

#DISCRIMINATION PARAMETERS:
tol_similarity          0.96
tol_energy              0.10
cutoff_energy           10.0
cutoff_population       10

#STOP CRITERION:
nof_generations         10
nof_repeats             3
nof_stagnant            5

#THEORY LEVEL:
calculator              GAUSSIAN
nof_processes           4

#OUTPUT FILE:
initial_file            initial.xyz
output_file             summary.xyz
```

Calculator-specific keys (`metal_symbol` for SC, `ani_model` for ANI,
`vasp_potcar`/`vasp_latsp` for VASP, the `---GAUSSIAN---`/`---ORCA---`/
`---MOPAC---`/`---GULP---`/`---VASP---` route blocks) apply only to the
calculator selected. Binary paths and scratch folders come from AEGON's site
configuration (`python -m aegon.config show`), not from this file.

Every run prints a warning for any key or block present in the INPUT file but
never actually read by the algorithm/calculator combination selected — e.g. a
typo, a key that only applies to a different calculator, or a stop-criterion
key from `KICK` left over after switching an INPUT file to `GEGA`. Silent
otherwise.

Boolean keys (`discriminate`, `rename`, `disc_unconnected_opt` for `KICK`;
`matings`, `mutants`, `two_childs`, `two_mutations` for `GCLUS`) accept only
`True`/`False` (case-insensitive) via `read_main_input.get_bool` — deliberately
not a grab-bag of alternate spellings (no `yes`/`no`, `1`/`0`, `on`/`off`).

---

## Supported calculators

| `calculator` | Backend | Native units | KICK | GEGA | GCLUS | RTMR | Notes |
|---|---|---|:---:|:---:|:---:|:---:|---|
| `LJ` | `aegon.libcalc_lj` | eV | ✅ | ✅ | ✅ | — | Generic Lennard-Jones, no external binary |
| `SC` | `aegon.libcalc_sc` | eV | ✅ | ✅ | ✅ | — | Sutton-Chen; requires `metal_symbol` |
| `TIO2` | `aegon.libcalc_tio2` | eV | ✅ | ✅ | ✅ | — | Buckingham + Coulomb + LJ for TiO2 |
| `EMT` | `aegon.libcalc_emt` | eV | ✅ | ✅ | ✅ | — | ASE Effective Medium Theory; Al, Ni, Cu, Pd, Ag, Pt, Au |
| `ANI` | `aegon.libcalc_ani` | kcal/mol | ✅ | ✅ | ✅ | ✅ | Machine-learned potential (ANI1x/ANI1ccx/ANI2x); requires `aegon[ani]` |
| `GAUSSIAN` | Gaussian 16 | kcal/mol | ✅ | ✅ | ✅ | ✅ | External binary |
| `ORCA` | ORCA | kcal/mol | ✅ | ✅ | ✅ | ✅ | External binary |
| `MOPAC` | MOPAC | kcal/mol | ✅ | ✅ | ✅ | ✅ | External binary |
| `GULP` | GULP | eV | ✅ | ✅ | ✅ | — | External binary; periodic and cluster-in-vacuum-box |
| `VASP` | VASP | kcal/mol | ✅ | ✅ | ✅ | — | External binary; periodic, requires POTCAR |

`GCLUS` dispatches through the identical `_build_calculator` used by `GEGA`
(all 10 calculators). `RTMR` is limited to `ANI`/`GAUSSIAN`/`ORCA`/`MOPAC` —
the rest have no organic-molecule parameterization or are otherwise not the
tool for a conformer search. `GCLUS` itself has no molecular-graph/bonding
constraint (see its own section below), so unlike `RTMR` it can technically
run any composition through any of the 10 calculators, including organic
compositions — that combination builds raw atomic arrangements, not
chemically-sensible conformers.

---

## Stochastic kick search

`heuristic_kick.py`: a population goes through `number_of_stages` local-opt +
discrimination passes, no crossover/mutation. Template-driven engines
(Gaussian, ORCA, MOPAC, GULP, VASP) take one input block per stage
(`---GULP1---`/`---GULP2---`, `---INCAR1---`/`---KPOINTS1---` for VASP), so
stages can run at different levels of theory.

```python
from glomos.heuristic_kick import stochastic_kick

population = stochastic_kick('INPUT_GLOMOS_LJ_KICK_Mo8.txt')
```

Examples: `x_run_glomos_{lj,sc,tio2,emt}_kick_{mo8,cu8,tio2,au8}.py`,
`..._gulp_kick_cu8.py`, `..._{ani,orca,gaussian,vasp}_kick_h2o.py` (seeded
from the matching GEGA run's `summary.xyz`), `..._mopac_kick_si5o6.py`
(MNDO, self-generated population — see [Genetic algorithm search](#genetic-algorithm-search)),
and `..._gaussian_kick_b10.py` (multi-stage boron refinement).

---

## Genetic algorithm search

`heuristic_ga.py`: roulette-wheel crossover/mutation, energy-cutoff + USR
deduplication, stop on max generations / repeated isomers / stagnant cycles.

```python
from glomos.heuristic_ga import genetic_algorithm

population = genetic_algorithm('INPUT_GLOMOS_GAUSSIAN_GEGA_H2O.txt')
for mol in population[:5]:
    print(mol.info['i'], mol.info['e'], mol.info['c'])
```

Population is written to `output_file` after every generation. Examples:
`x_run_glomos_{lj,sc,tio2,emt}_gega_{mo8,cu8,tio2,au8}.py`,
`..._gulp_gega_cu8.py`, `..._{ani,orca,gaussian,vasp}_gega_h2o.py`,
`..._mopac_gega_si5o10.py` (MNDO).

`nof_opt_attempts` (default 1, same key and semantics as `KICK` above) retries
any structure whose `info['c']` isn't 1 — i.e. the calculator's job didn't
reach a real stationary point — from its current geometry, up to that many
total attempts, before it's allowed into fitness-proportional selection.
Applies to both the initial population and every generation's crossovers/
mutants, and works the same way for every calculator in the registry (not
just the external QM codes): `info['c']` is set uniformly by every
`*CalculatorStrategy.optimize_parallel()`, so the retry loop only depends on
that, never on which calculator produced it.

`nof_randoms` (default 0) inserts that many brand-new random individuals into
every generation, alongside the usual crossovers and mutants — a way to
reintroduce diversity into a search that's converging too quickly around one
basin. Candidates go through the same structural-redundancy check as
crossover/mutation offspring before being accepted: USR similarity plus
energy tolerance against the running batch itself, against every
non-optimized structure proposed so far this run, and against every
optimized structure already accepted, so a random insertion can't keep
reintroducing a structure the search has already explored or converged to.

### Running several searches in sequence

```python
from glomos.heuristic_ga import genetic_algorithm

genetic_algorithm('INPUT_GLOMOS_LJ_GEGA_Mo8.txt')
genetic_algorithm('INPUT_GLOMOS_SC_GEGA_Cu8.txt')
genetic_algorithm('INPUT_GLOMOS_GAUSSIAN_GEGA_H2O.txt')
```

---

## Clustering-based genetic algorithm search

`heuristic_clustering.py`: structurally identical to `heuristic_ga.py`'s GEGA
driver — same calculator dispatch (`_build_calculator`, all 10 registered
calculators), same `nof_opt_attempts` retry-on-non-convergence and
`nof_randoms` random-insertion, same stop criteria — but replaces
fitness-proportional roulette selection with
`glomos.generation.selection.Descriptor_and_clustering()`: every generation,
the current population is grouped into `nof_groups` structural clusters (MBTR
descriptor + Ward agglomerative clustering, delegated to
`growpal.libclustering`/`growpal.libdescriptors` — the same machinery GrowPAL
uses to pick diverse candidates after a growth step) and the lowest-energy
representative of each cluster becomes a "possible father" for crossover and
mutation.

```python
from glomos.heuristic_clustering import genetic_algorithm

population = genetic_algorithm('INPUT_GLOMOS_LJ_GCLUS_Mo20.txt')
```

GCLUS replaces GEGA's `nof_matings`/`nof_mutants` (target counts) with:

- `nof_groups` — number of structural clusters (and therefore "possible
  fathers") per generation.
- `matings` / `mutants` (`True`/`False`) — whether to run crossover/mutation
  at all. When `matings` is on, **every pairwise combination** of possible
  fathers is crossed (not a fixed target count), so offspring count depends
  on how many pairs survive discrimination.
- `two_childs` (`True`/`False`) — keep both children a single Deaven-Ho cut
  produces (the complementary fragment pairing), not just one.
- `two_mutations` (`True`/`False`) — chain two mutation operators per mutant
  instead of one (`make_mutant(..., n_mutations=2)`).

Examples: `x_run_glomos_{lj,sc,tio2,emt}_gclus_{mo8,cu8,tio2,au8}.py`
(plus `..._lj_gclus_mo20.py`, a second LJ size), `..._gulp_gclus_cu8.py`,
`..._{ani,orca,gaussian,vasp}_gclus_h2o.py`, `..._mopac_gclus_si5o10.py`.

---

## Rotamer / conformer search

`heuristic_ga_rotamers.py`: a GA over dihedral angles of a single molecule.
Rotatable bonds are identified from the molecular graph of a `rotamer_seed`
XYZ file; no `COMPOSITION` block.

```python
from glomos.heuristic_ga_rotamers import conformational

population = conformational('INPUT_ROTAMERS.txt')
```

`calculator` is `ANI` (`ani_model` selects `ANI1x`/`ANI1ccx`/`ANI2x`), `MOPAC`,
`GAUSSIAN`, or `ORCA`.

- `examples/INPUT_GLOMOS_MOPAC_RTMR_C8H9NO2.txt` / `..._ANI_RTMR_C8H9NO2.txt`
  — paracetamol, `ANI1ccx`.
- `examples/INPUT_GLOMOS_GAUSSIAN_RTMR_C2H6O.txt` / `..._ORCA_RTMR_C2H6O.txt`
  — ethanol, PBE0/Def2SVP.

---

## Citation

If you use GLOMOS in your research, please cite the associated manuscript (in preparation), and the AEGON backend it builds on:

> López-Castro C., Ortiz-Chi F., Merino G.
> *An Efficient Growth Pattern Algorithm (GrowPAL) for Cluster Structure Prediction.*
> J. Chem. Theory Comput. **2024**, 20, 4939–4948.

---

## Authors

- **Aileen Garcia Cano** — Facultad de Ingeniería, Universidad Autónoma de Yucatán, Mérida, México
- **Gabriel Merino** — Departamento de Física Aplicada, Cinvestav-IPN, Mérida, México
- **Filiberto Ortiz-Chi** — Secihti-Departamento de Física Aplicada, Cinvestav-IPN, Mérida, México

---

## License

See [LICENSE](LICENSE).
