Metadata-Version: 2.4
Name: aegon
Version: 1.3.9
Summary: Atomic Environment for Global OptimizatioN
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/aegon
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: ase>=3.22
Requires-Dist: numpy>=1.24
Requires-Dist: scipy
Requires-Dist: py3Dmol
Requires-Dist: numba>=0.61.0
Requires-Dist: pydantic>=2
Requires-Dist: pyyaml
Requires-Dist: joblib
Requires-Dist: networkx
Provides-Extra: ani
Requires-Dist: torch; extra == "ani"
Requires-Dist: torchani; extra == "ani"
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Dynamic: license-file

# AEGON

**Atomic Environment for Global OptimizatioN — an open-source Python framework for the global optimization of atomic clusters and molecules.**

AEGON is built natively on top of the [Atomic Simulation Environment (ASE)](https://wiki.fysik.dtu.dk/ase/) and operates directly on `ase.Atoms` objects. It provides a self-contained toolkit covering every stage of a global optimization workflow, organized into three subpackages that mirror the three stages of the search: **`generation`** (random, symmetry-constrained, and genetic-algorithm-operator structure creation), **`optimization`** (local relaxation — built-in potentials and external QM/FF codes behind one plugin registry), and **`discrimination`** (structure deduplication). Performance-critical routines are accelerated through [Numba](https://numba.readthedocs.io/) just-in-time compilation.

---

## Table of Contents

- [Features](#features)
- [Installation](#installation)
- [Dependencies](#dependencies)
- [Module Overview](#module-overview)
- [Usage](#usage)
- [Reference Databases](#reference-databases)
- [Sutton-Chen Parameters](#sutton-chen-parameters)
- [Known Dead Code](#known-dead-code)
- [Citation](#citation)
- [Authors](#authors)
- [License](#license)

---

## Features

- **Random structure generation** — nine structural templates (compact 3D, diffuse 3D, planar 2D, spherical shell, wire, ring, two-ring, helix, eye) with covalent-radii-based distance constraints and BFS connectivity verification.
- **Symmetry-constrained generation** — orbit-by-orbit placement for 30+ molecular point groups (C1 through Ih), with automatic fallback when the composition is incompatible with the requested symmetry.
- **Periodic crystal generation** — space-group-aware random crystal structures for all 230 space groups using ASE symmetry operations.
- **Built-in potential energy calculators** — Lennard-Jones (LJ) and Sutton-Chen (SC) potentials with Numba-accelerated energy and force evaluation; L-BFGS-B local minimization; ASE Effective Medium Theory (EMT) via BFGS; a Numba-accelerated periodic empirical engine (LJ + Buckingham + Ewald, cell+positions relaxed via `UnitCellFilter`) for TiO2, MgAl2O4, MgSiO3, and SrTiO3; ANI machine-learned potentials (ANI1x/ANI1ccx/ANI2x, optional `[ani]` extra).
- **Calculator registry** — a plugin-style registry (`aegon.optimization.build(calc_type, **kwargs)`) that drives full parallel local optimizations for LJ, SC, TIO2, EMT, PERIODIC, ANI, GAUSSIAN, ORCA, MOPAC, GULP, and VASP behind one interface. A generic `'ASE'` entry dynamically wraps any `ase.calculators.Calculator` by module + class name (optionally relaxing cell and positions together via an `ase.filters` cell filter for periodic structures), so new ASE-compatible potentials work without new registry code.
- **Periodic-aware GULP/VASP** — `GulpEngine`/`VaspEngine` auto-detect already-periodic input (`any(atoms.pbc)`) and preserve the real cell instead of vacuum-boxing it via `molecule2poscar` (which stays the default for non-periodic clusters), so the same registry entries serve both cluster codes (`growpal`) and crystal structure prediction (`solids`).
- **External quantum chemistry interfaces** — per-code parsers under `aegon.parsing` (`libcode_gaussian`/`libcode_orca`/`libcode_mopac`/`libcode_gulp`/`libcode_vasp`: geometry, trajectory, termination status), plus a unified convenience parser (`aegon.parsing.read_out`) for output files from Gaussian, ORCA, VASP, GULP, and MOPAC; input generation and batch execution with parallel queuing (`libengine_gaussian`/`libengine_orca`/`libengine_mopac`/`libengine_gulp`/`libengine_vasp`).
- **Layered site configuration** — `aegon.config` resolves installation-specific settings (binary paths, scratch folders) from an environment variable, an XDG user config file, or packaged defaults, validated with Pydantic — kept separate from per-run parameters.
- **Consistent unit system** — `aegon.units` ties energy and force units together as one choice (`eV`/`eV·Å⁻¹`, `atomic`/`Eh·bohr⁻¹`, or `kcal`/`kcal·mol⁻¹·Å⁻¹`), applied uniformly by every calculator.
- **Genetic algorithm operators** — mutation (atom displacement, twist), Deaven-Ho cut-and-splice crossover, dihedral rotamer exploration along bridge bonds, and fitness-proportional roulette wheel selection with a tanh-based fitness mapping.
- **Structure discrimination** — USR (Ultrafast Shape Recognition) descriptors for fast deduplication/filtering of cluster pools, and MBTR descriptor-based comparison for periodic crystal pools.
- **Reference cluster databases** — pre-optimized LJ clusters (5–130 atoms) and Sutton-Chen clusters for ten transition and main-group metals (Ag, Al, Au, Cu, Ir, Ni, Pb, Pd, Pt, Rh, up to 90 atoms), loaded lazily and cached per session.
- **Visualization** — inline Jupyter/Colab rendering with py3Dmol; support for periodic structures with unit-cell edges.

---

## Installation

```bash
pip install aegon
```

Requires Python >= 3.10. To use the ANI machine-learned potentials (`torch`/`torchani`, a heavy optional dependency not needed for anything else in AEGON):

```bash
pip install aegon[ani]
```

### Google Colab

```python
!pip install aegon
```

---

## Dependencies

| Package | Role |
|---|---|
| [ASE](https://wiki.fysik.dtu.dk/ase/) | Atomic structure representation (`Atoms` objects) |
| [NumPy](https://numpy.org/) | Array operations throughout |
| [SciPy](https://scipy.org/) | L-BFGS-B optimization, k-d tree, sparse graph |
| [Numba](https://numba.pydata.org/) | JIT-compiled energy, force, and descriptor kernels |
| [py3Dmol](https://github.com/3dmol/3Dmol.js) | Inline 3D visualization in Jupyter/Colab |
| [Pydantic](https://docs.pydantic.dev/) | Validation of layered site configuration (`aegon.config`) |
| [PyYAML](https://pyyaml.org/) | Site configuration file format |
| [joblib](https://joblib.readthedocs.io/) | Process-based parallelism for calculators (`opt_LJ_parallel`, `opt_SC_parallel`, `opt_ASE_parallel` (EMT included), ANI) |
| [PyTorch](https://pytorch.org/) / [TorchANI](https://aiqm.github.io/torchani/) | ANI machine-learned potentials — optional, `pip install aegon[ani]` |

[MatterSim](https://github.com/microsoft/mattersim) (`pip install mattersim`) is
*not* a dependency or extra of AEGON — AEGON's own source never imports it, it's
only used in `examples/example_opt_crystal_mattersim.py` through the generic
`'ASE'` strategy (`optimization/potentials/ase_generic.py`), which can wrap any
ASE-compatible calculator without AEGON code changes. Adding it as a formal
extra was deliberately rejected: MatterSim pulls in a large, unrelated
dependency tree (pymatgen, torch_geometric, phonopy/phono3py, wandb, the Azure
SDK, ...) that has nothing to do with AEGON's minimal core.

**Known conflict if you install both `aegon[ani]` and `mattersim` in the same
environment:** MatterSim requires `torch>=2.2.0` with no upper bound, while
TorchANI 2.7.9 declares `torch<=2.8,>=2.0`. `pip install mattersim` will
happily upgrade `torch` past 2.8 to satisfy its own constraint, which `pip
check` then reports as a broken requirement for `torchani`. In practice ANI
has been verified to still run correctly against `torch==2.13.0` (installed by
MatterSim) — this is a stale declared ceiling in TorchANI's metadata, not an
observed runtime failure — but the two packages are not a clean, warning-free
combination, and there's no way to prevent this from `aegon`'s `pyproject.toml`
since neither package is one of its extras.

---

## Module Overview

AEGON's own code is organized into three subpackages that mirror the three stages
of a global optimization search, plus shared infrastructure used across all three.

### 1. `generation` — creating structures

Three structure types (cluster, crystal, rotamer), each split the same way: a
`_random` module for stochastic generation and, where a GA drives the search,
matching `_crossover`/`_mutant` modules for its operators. Each pair shares
the same two public entry points: a single-pair primitive
(`crossover(parent_a, parent_b, ...)`, kept as `crossover_deavenho` for
clusters since that names a specific published algorithm) and a
population-level driver for one GA generation (`popgen_children`/
`popgen_mutants`), identically named across all three structure types.

| Module | Description |
|---|---|
| `generation/cluster_random.py` | Random cluster generators (nine structural templates, parallel batch generation) and symmetry-constrained generators for 30+ point groups |
| `generation/cluster_crossover.py` | Deaven-Ho cut-and-splice crossover for clusters (`crossover_deavenho`, `popgen_children`) |
| `generation/cluster_mutant.py` | Mutation operators for clusters: atom displacement, twist, overlap resolution (`popgen_mutants`) |
| `generation/crystal_random.py` | Space-group-aware periodic crystal generator for all 230 space groups |
| `generation/crystal_crossover.py` | Cut-and-splice crossover for periodic crystals (`crossover`), used by `solids` |
| `generation/crystal_mutant.py` | Mutation operators for periodic crystals: lattice strain, atom exchange (`popgen_mutants`), used by `solids` |
| `generation/rotamer_random.py` | Stochastic rotamer generation: dihedral rotamer search along bridge bonds using the molecular graph (`make_random_rotamers`) |
| `generation/rotamer_crossover.py` | Crossover operator for rotamers (`crossover`, `popgen_children`) |
| `generation/rotamer_mutant.py` | Mutation operator for rotamers (`popgen_mutants`) |
| `generation/selection.py` | Roulette wheel selection with tanh-based fitness proportional to energy ranking — used internally by `cluster_crossover.py`/`cluster_mutant.py`'s `popgen_children`/`popgen_mutants`, and standalone by GA drivers (`glomos`, `solids`) |

### 2. `optimization` — relaxing structures

| Module | Description |
|---|---|
| `optimization/registry.py` | Plugin registry (`register_calculator`, `build`, `available`) — every calculator strategy below registers here, so `aegon.optimization.build(calc_type, **kwargs)` drives all of them behind one interface |
| `optimization/potentials/lj.py` | Lennard-Jones energy, forces, and L-BFGS-B local minimization (Numba-accelerated) + `'LJ'` strategy |
| `optimization/potentials/sc.py` | Sutton-Chen potential for 10 metals with parameters; Numba-accelerated energy/forces and `opt_sc`/`opt_SC_parallel` + `'SC'` strategy |
| `optimization/potentials/tio2.py` | Buckingham-Coulomb-LJ potential for TiO2 clusters + `'TIO2'` strategy |
| `optimization/potentials/emt.py` | `'EMT'` strategy: a thin `ASECalculatorStrategy` preset (position-only, BFGS, fmax=0.001, steps=200) for ASE's Effective Medium Theory potential — no separate implementation, reuses `ase_generic.py`'s engine |
| `optimization/potentials/crystal_empirical.py` | Periodic empirical engine (LJ + Buckingham + Ewald, cell+positions relaxed via `UnitCellFilter`) with built-in `PotentialSpec`s for TiO2, MgAl2O4, MgSiO3, SrTiO3 + `'PERIODIC'` strategy (`spec='TIO2'`/`'MGAL2O4'`/`'MGSIO3'`/`'SRTIO3'`, or a custom `PotentialSpec`) |
| `optimization/potentials/ase_generic.py` | Dynamically wraps any `ase.calculators.Calculator` by module+class name + `'ASE'` strategy |
| `optimization/potentials/ani.py` + `optimization/ani_backend.py` | ANI (ANI1x/ANI1ccx/ANI2x) machine-learned potential via torchani + `'ANI'` strategy. Split into a thin, eagerly-imported strategy (alongside the other potentials) and a heavy backend imported lazily only when the strategy actually runs — deliberately kept *outside* `potentials/`, since unlike every file in there it must never be added to an eager `__init__.py` import, so `import aegon.optimization` never requires the optional `[ani]` extra |
| `optimization/external/<code>.py` | `'GAUSSIAN'`/`'ORCA'`/`'MOPAC'`/`'GULP'`/`'VASP'` strategies — thin adapters that delegate the actual work to `aegon.parsing` |
| `parsing/libcode_<code>.py` | Output parsing and geometry/trajectory extraction for each external code (`gaussian`, `orca`, `mopac`, `gulp`, `vasp`) |
| `parsing/libengine_<code>.py` | Input generation and batch execution for each external code (`gaussian`, `orca`, `mopac`, `gulp`, `vasp`) |
| `parsing/readouts.py` | `read_out` — unified dispatch (`geo`/`traj`) over `parsing/*` for Gaussian, ORCA, VASP, GULP, and MOPAC output files; re-exported as `aegon.parsing.read_out` |

### 3. `discrimination` — filtering redundant structures

| Module | Description |
|---|---|
| `discrimination/cluster_usr.py` | USR descriptor computation, batch deduplication (`deduplicate_by_usr`), and filtering against a reference pool — clusters/molecules |
| `discrimination/crystal_mbtr.py` | MBTR descriptor-based structure comparison and deduplication for periodic crystal pools (requires the optional `dscribe` dependency) |

### Shared infrastructure

| Module | Description |
|---|---|
| `data/clusterdb.py` | `aegondb` class — unified dispatch (by model name) over pre-optimized LJ and SC reference clusters, delegating to `data/lj.py`/`data/sc.py` |
| `data/lj.py` | Direct access to LJ reference clusters via `get_lj_cluster(N)` |
| `data/sc.py` | Direct access to SC reference clusters via `get_sc_cluster(N, symbol)` |
| `io/poscar.py` | POSCAR/CONTCAR file reading and writing |
| `io/xyz.py` | Concatenated multi-structure XYZ I/O (`readxyzs`, `writexyzs` — plain or, via `extended=True`, ASE's extended XYZ with cell/PBC and optional forces) |
| `io/stdio.py` | Composition I/O: reading composition blocks from AEGON input files, cluster naming |
| `io/atoms2image.py` | Static structure rendering to image files, with unit-cell edges and optional force arrows |
| `io/gcolab.py` | Inline py3Dmol visualization for Jupyter and Google Colab (`viewmol_ASE`) |
| `geometry.py` | Distance, graph/connectivity, rotation, and alignment utilities on `ase.Atoms` (pure geometry, no I/O) |
| `population.py` | Bookkeeping over lists of `ase.Atoms` shared by all three blocks: labeling (`rename`), ranking (`sort_by_energy`), filtering (`cutter_nonconnected`, `cutter_energy`) |
| `units.py` | Paired energy/force unit conversion (`energy_factor`, `force_factor`, `unit_labels`) shared by every calculator |
| `queuing.py` | Parallel bash script execution via `multiprocessing.Queue` |
| `config/` | Layered site configuration (env var → XDG user config → packaged defaults), validated with Pydantic; inspect with `python -m aegon.config show` |

---

## Usage

Three complete examples below, one per subpackage from
[Module Overview](#module-overview) — this is the same generate →
optimize → discriminate sequence a GA driver (`glomos`, `solids`) repeats
every generation, shown here as three standalone steps.

### 1. Generating structures

`generation/cluster_random.py` covers both unconstrained and
symmetry-constrained cluster generation; `generation/crystal_random.py` and
`generation/rotamer_random.py` cover the other two structure types (periodic
crystals, single-molecule conformers — see [Module Overview](#module-overview)).

```python
from aegon.generation.cluster_random import make_molecules_random, make_clusters_symmetric, make_cluster_symmetric

composition = ['Au'] * 13

# Unconstrained random generation, covalent-radii distance constraints + BFS
# connectivity check, generated in parallel
random_pop = make_molecules_random(composition, count=100, n_cores=4)

# Symmetry-constrained generation: each structure gets an automatically
# compatible point group
symmetric_pop = make_clusters_symmetric(composition, count=100, n_cores=4)

# Or fix a specific point group
mol_ih = make_cluster_symmetric(composition, point_group='Ih')
```

### 2. Optimizing structures

Every calculator — built-in potentials, ANI, or an external code — is driven
through the same registry entry point, `aegon.optimization.build`:

```python
from aegon.optimization import build, available

print(available())
# ['ANI', 'ASE', 'EMT', 'GAUSSIAN', 'GULP', 'LJ', 'MOPAC', 'ORCA', 'PERIODIC', 'SC', 'TIO2', 'VASP']

calc = build('SC', metal_symbol='Au', units='eV')
optimized = calc.optimize_parallel(random_pop, n_jobs=4)

# Periodic empirical potentials (no external binary) work the same way
calc_periodic = build('PERIODIC', spec='TIO2', units='eV')
optimized_crystal = calc_periodic.optimize_parallel(crystal_population, n_jobs=4)
```

`units` (`'eV'`/`'atomic'`/`'kcal'`) fixes energy **and** force together,
applied uniformly by every calculator via `aegon.units.energy_factor`/`force_factor`.
The potential-specific functions (`opt_LJ_parallel`, `opt_sc`/`opt_SC_parallel`,
...) underneath each `'LJ'`/`'SC'`/... strategy are also directly importable
from `optimization/potentials/*.py` if you don't need the registry indirection.

External codes need binary paths and scratch folders, resolved from a layered
site configuration (env var → XDG user config → packaged defaults, Pydantic-validated):

```bash
python -m aegon.config show
```

To read an already-finished calculation instead of running a new one:

```python
from aegon.parsing import read_out

reader = read_out()
mol  = reader.geo('gaussian', 'output.log')    # final geometry
traj = reader.traj('orca', 'calculation.out')  # full optimization trajectory
```

### 3. Discriminating structures

```python
from aegon.population import sort_by_energy
from aegon.discrimination.cluster_usr import deduplicate_by_usr

ranked = sort_by_energy(optimized, opt=1)
unique = deduplicate_by_usr(ranked, tols=0.99, tole=0.1, mono=True)
print(f"{len(optimized)} → {len(unique)} unique structures")
for mol in unique[:5]:
    print(mol.info['i'], mol.info['e'])
```

`deduplicate_by_usr` ranks by USR (Ultrafast Shape Recognition) descriptor
similarity within `tole` (eV) of each other; `mono=True` skips the descriptor's
moment-of-inertia component (irrelevant for single-element clusters). For
periodic crystal pools, `discrimination/crystal_mbtr.py` provides the same
deduplication via MBTR descriptors (requires the optional `dscribe` dependency).

### Genetic algorithm operators

```python
from aegon.generation.cluster_mutant import atom_displacement, twist_mutation
from aegon.generation.cluster_crossover import crossover_deavenho
from aegon.generation.selection import get_roulette_wheel_selection

# Select parents by roulette wheel (fitness-proportional)
parents = get_roulette_wheel_selection(optimized, nmating=20)

# Mutation
mutant = atom_displacement(parents[0], delta=0.4)

# Deaven-Ho cut-and-splice crossover
atomlist = parents[0].get_chemical_symbols()
children = crossover_deavenho(parents[0], parents[1], atomlist)
```

Writing structures back out is a property of the `ase.Atoms` object itself
(periodic or not, with or without forces), not of which code produced it:
`aegon.io.xyz.writexyzs` (plain or, with `extended=True`, ASE's extended XYZ
format) or `aegon.io.poscar.writeposcars`.

### Visualize in Jupyter / Colab

```python
from aegon.io.gcolab import viewmol_ASE

viewmol_ASE(mol, width=500, height=500)
```

---

## Reference Databases

AEGON ships two bundled databases loaded lazily at runtime. Each entry is returned as an `ase.Atoms` object.

| Database | Potential | Available elements | Sizes |
|---|---|---|---|
| `LJ_clusters_data.npz` | Lennard-Jones (ε = 1 eV, r₀ = 2^(1/6) σ = 3 Å) | any (Mo by default) | N = 5–130 |
| `SC_<El>_clusters_data.npz` | Sutton-Chen | Ag, Al, Au, Cu, Ir, Ni, Pb, Pd, Pt, Rh | N = 5–90 |

These datasets were generated with the GrowPAL diversity-preserving algorithm and validated against the Wales reference database (LJ) and literature results (SC).

### Access via direct functions

```python
from aegon.data.lj import get_lj_cluster
from aegon.data.sc import get_sc_cluster

# LJ cluster: info keys are 'i' (ID string) and 'e' (energy in eV)
lj38 = get_lj_cluster(38)
print(lj38.info['i'], lj38.info['e'])

# SC cluster
au20 = get_sc_cluster(20, symbol='Au')
print(au20.info['i'], au20.info['e'])
```

### Access via aegondb

```python
from aegon.data.clusterdb import aegondb

# LJ cluster: info keys are 'i' (ID string) and 'e' (energy in eV) — same
# convention as the direct functions above, since aegondb delegates to them
lj38 = aegondb.get(N=38, model='LJ')
print(lj38.info['i'], lj38.info['e'])

# SC cluster
au20 = aegondb.get(N=20, model='SC', element='Au')

# List available sizes
print(aegondb.list_available(model='SC', element='Pt'))
```

### Compare Sutton-Chen energies between two metals

```python
import numpy as np
from aegon.optimization.potentials.sc import SUTTON_CHEN_PARAMS
from aegon.data.sc import get_sc_cluster

metal_type1, metal_type2 = 'Pt', 'Au'
epsilon1 = SUTTON_CHEN_PARAMS[metal_type1]['epsilon']
epsilon2 = SUTTON_CHEN_PARAMS[metal_type2]['epsilon']

for i in range(4, 90 + 1):
    atoms1 = get_sc_cluster(i, symbol=metal_type1)
    atoms2 = get_sc_cluster(i, symbol=metal_type2)
    e1     = atoms1.info['e'] / epsilon1
    e2     = atoms2.info['e'] / epsilon2
    deltae = np.abs(e1 - e2)
    print("%9s vs. %9s  dE=%f" % (atoms1.info['i'], atoms2.info['i'], deltae))
```

---

## Sutton-Chen Parameters

AEGON includes the original Sutton-Chen parameters from Sutton & Chen, *Philos. Mag. Lett.* **1990**, 61, 139–146, for ten metals:

| Element | n | m | ε (eV) | a (Å) | C |
|---------|---|---|--------|--------|---|
| Ni | 9 | 6 | 1.5707×10⁻² | 3.52 | 39.432 |
| Cu | 9 | 6 | 1.2382×10⁻² | 3.61 | 39.432 |
| Rh | 12 | 6 | 4.9371×10⁻³ | 3.80 | 144.41 |
| Pd | 12 | 7 | 4.1790×10⁻³ | 3.89 | 108.27 |
| Ag | 12 | 6 | 2.5415×10⁻³ | 4.09 | 144.41 |
| Ir | 14 | 6 | 2.4489×10⁻³ | 3.84 | 334.94 |
| Pt | 10 | 8 | 1.9833×10⁻² | 3.92 | 34.408 |
| Au | 10 | 8 | 1.2793×10⁻² | 4.08 | 34.008 |
| Pb | 10 | 7 | 5.5765×10⁻³ | 4.95 | 45.778 |
| Al | 7  | 6 | 3.3147×10⁻² | 4.05 | 16.339 |

```python
from aegon.optimization.potentials.sc import SUTTON_CHEN_PARAMS

params = SUTTON_CHEN_PARAMS['Pd']
print(params['n'], params['m'], params['epsilon'])
```

---

## Known Dead Code

Functions currently unreferenced anywhere in the codebase, flagged here so they are easy to find:

| Function | Location | Why it's dead |
|---|---|---|
| `get_best_geometry_vasp` | `parsing/libcode_vasp.py` | Never called. `get_geometry_vasp` already scans the *entire* OUTCAR ionic-step trajectory (via `get_traj_vasp`) and returns the lowest-energy structure regardless of convergence — the same "best available" behavior `get_best_geometry_gaussian`/`get_best_geometry_orca` provide for those codes, just unified into one function instead of two. `libengine_vasp.py`'s retry/rescue logic calls `get_geometry_vasp` directly and never needed the separate `_best_` variant. |
| `display_info(moleculein, stage_string, dicc_term)` (3-argument form) | `parsing/libcode_gulp.py` | Never called; superseded by `GulpEngine`'s own print statements in `libengine_gulp.py`. Note `growpal.libgrowpal` has its own unrelated 2-argument `display_info` — same name, different function, don't confuse the two. |
| `tag` — **removed**, was `io/poscar.py` | Never called anywhere in the codebase. Tagged atoms by atomic-number groups without reordering them; superseded in every real use case by `order_and_tag`, which does the same tagging plus the reordering POSCAR output actually needs. |

---

## Citation

If you use AEGON in your research, please cite the associated manuscript (in preparation).

AEGON is the optimization backend used in:

> Gutiérrez-Campos I., Merino G., Ortiz-Chi F.
> *Morphological Diversity as a Selection Principle in Growth-Based Global Optimization.*

> 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

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

---

## License

MIT — see [LICENSE](LICENSE).
