Metadata-Version: 2.5
Name: riper
Version: 0.3.0
Summary: ASE Calculator for TURBOMOLE's RIPER module
Project-URL: Homepage, https://www.turbomole.org
Author-email: Manas Sharma <manassharma07@live.com>
Keywords: ase,calculator,dft,periodic,riper,turbomole
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Chemistry
Classifier: Topic :: Scientific/Engineering :: Physics
Requires-Python: >=3.9
Requires-Dist: ase>=3.23
Requires-Dist: numpy>=1.20
Provides-Extra: test
Requires-Dist: pytest; extra == 'test'
Description-Content-Type: text/markdown

# RIPER: ASE Calculator for TURBOMOLE

`riper` is an [ASE](https://ase-lib.org/) calculator interface for the RIPER module in
[TURBOMOLE](https://www.turbomole.org/): Kohn-Sham DFT with Gaussian basis sets for
molecules and for systems with one-, two- or three-dimensional periodicity. The
calculator writes the TURBOMOLE `coord` and `control` files, runs `riper`, and returns
energies, forces, the stress tensor, dipole and magnetic moments, orbital energies,
occupations and k points in ASE units. Band structures, densities of states, RT-TDDFT
spectra and cube files are one method call each.

Version 0.3 keeps the `RIPERCalculator` interface of 0.2.x (scripts written for 0.2.x run
unchanged) on a new backend built on ASE's template-calculator API. It targets TURBOMOLE
8.1 and exposes only features documented in the 8.1 manual.

## Installation

From PyPI:

```bash
pip install riper
```

For development from this repository:

```bash
pip install -e .
pytest tests                                         # unit tests on recorded outputs
RIPER_TEST_COMMAND=riper_smp pytest tests/test_integration.py   # needs riper
```

Requirements: TURBOMOLE 8.1 or newer, Python >= 3.9, ASE >= 3.23, NumPy.

## TURBOMOLE environment

Before using the calculator, make sure TURBOMOLE is configured in the shell where Python
runs, for example:

```bash
export TURBODIR=/path/to/TURBOMOLE
export TURBOIMG=/path/to/TURBOMOLE
export PARA_ARCH=SMP
source $TURBODIR/Config_turbo_env
command -v riper_smp
```

The calculator does not ship TURBOMOLE. It calls the executable selected with
`riper_command` (`riper`, `riper_smp` or a full path); `ncore` sets the thread-count
variables (`PARNODES`, `OMP_NUM_THREADS`, `MKL_NUM_THREADS`) for the RIPER subprocess
only. Without `riper_command` the `[riper]` section of `~/.config/ase/config.ini` is
used if present (`command = riper_smp`, `ncore = 8`), otherwise `riper` from `PATH`.

Before the first calculation the calculator runs riper once on a one-atom input (0.3 s,
cached per command) and reads the version from the banner. Versions older than 8.1 are
rejected with a clear error, a missing executable is reported before any input is
written, and the detected version is available as `calc.riper_version`
(`check_version=False` skips the probe).

## Quick start: water single point

```python
from ase.build import molecule
from riper import RIPERCalculator

water = molecule("H2O")
water.calc = RIPERCalculator(
    functional="pbe",
    basis="def2-SVP",
    auxbasis="universal",
    dipole=True,
    riper_command="riper_smp",
    directory="calculations/water_pbe",
)

energy = water.get_potential_energy()      # eV
forces = water.get_forces()                # eV/Angstrom
dipole = water.get_dipole_moment()         # e*Angstrom
band_gap = water.calc.get_band_gap()       # eV
print(water.calc.get_eigenvalues().round(2), water.calc.get_fermi_level())
```

After a successful run `band_gap`, `efermi` (Fermi level) and `dipole_moment` are also stored in
`atoms.info`, so an extXYZ export (`ase.io.write("water.extxyz", water)`) carries them
together with energy, forces, stress and magnetic moments.

## Periodic example: silicon with stress and band structure

```python
from ase.build import bulk
from riper import RIPERCalculator

si = bulk("Si", "diamond", a=5.43)
si.calc = RIPERCalculator(
    functional="pbe",
    basis="pob-TZVP-rev2",
    auxbasis="universal",
    nkpoints=[4, 4, 4],
    calculate_stress=True,
    riper_command="riper_smp",
    directory="calculations/si",
)

print(si.get_potential_energy(), si.get_stress())   # eV, eV/Angstrom^3 (ASE Voigt order)
bs = si.calc.band_structure("GXWKGL", npoints=100)  # ase.spectrum.band_structure.BandStructure
bs.subtract_reference().plot(filename="si_bands.png", emin=-12, emax=8)
dos = si.calc.get_dos(width=0.1, pdos={"elements": ["si"], "resolve": "l"})
```

The periodicity follows `atoms.pbc`: zero, one, two or three periodic directions. RIPER
needs chains along x and slabs in the xy plane; the calculator rotates any orientation
(nanotubes along z, slabs with `pbc=[True, False, True]`, tilted in-plane vectors) into
RIPER's frame and rotates forces, stress and dipoles back. No vacuum is needed in
non-periodic directions. For 2D systems only the entries of `nkpoints` belonging to the
periodic cell vectors are used, for 1D one entry.

## Calculator parameters

```python
RIPERCalculator(
    basis=None, auxbasis="universal", ecp=None,
    functional="pbe", nkpoints=None,
    charge=0, uks=False, unpaired=0,
    smear=False, sigma=0.01, desnue=None,
    dispersion=None,
    scfconv=7, scfiterlimit=50, denconv=None, ricore=5000, gridsize="m5",
    riper_command=None, ncore=None, directory=".",
    extra_keywords=None, extra_groups=None,
    convergence_check="error",
    calculate_stress=False, dipole=False, plot_dens=False, forces=None, restart=True,
    dftu=None, relativistic=None, kramers=False,
    check_version=True,
)
```

| Option | Type / units | Meaning |
| --- | --- | --- |
| `basis` | `str`, `dict` or `None` | Orbital basis. If `None`, `def2-SVP` for 0D and `pob-DZVP-rev2` for periodic systems. A dict assigns per element, e.g. `{"Mg": "pob-DZVP-rev2", "O": "pob-TZVP-rev2"}`. |
| `auxbasis` | `str` or `dict` | Auxiliary basis, default `universal`, written as `jbas`. |
| `ecp` | `str`, `dict` or `None` | Effective core potential, e.g. `"def2-ecp"`; a dict assigns per element. |
| `functional` | `str` | TURBOMOLE functional keyword (`pbe`, `b3-lyp`, `pbe0`, `hse06`, `scan0`, ...). `xc` is accepted as a synonym. |
| `nkpoints` | list of `int` or `dict` | k-point mesh; only the periodic directions are written. Default `[2, 2, 2]`. `{"density": 4.0}` chooses the mesh from a k-point density (per 1/Angstrom). `kpts` is accepted as a synonym. Even subdivisions are written as `nkpoints n n n shifted` (the 8.1 mesh; newer versions honour the word and then give the same numbers). |
| `charge` | `int` | Total charge (`$atomdens total_charge`). |
| `uks` | `bool` | Unrestricted Kohn-Sham (`$uhf`). |
| `unpaired` | `int` | Unpaired electrons (`$atomdens unpaired`), implies `uks=True`. |
| `smear`, `sigma` | `bool`, Hartree | Gaussian smearing with width `sigma` (`$riper sigma`). |
| `desnue` | `float` | Fixed number of unpaired electrons per cell in smeared UKS runs (`$riper desnue`). |
| `dispersion` | `None`, `"d3"`, `"d3bj"`, `"d4"` | `$disp3`, `$disp3 bj` or `$disp4`. `disp3=True` is accepted for `"d3"`. |
| `scfconv`, `scfiterlimit`, `denconv`, `ricore`, `gridsize` | TURBOMOLE meaning | `$scfconv N` (10^-N Hartree), `$scfiterlimit`, `$denconv`, `$ricore` (MB), grid size. |
| `riper_command`, `ncore` | `str`, `int` | Executable and thread count for the RIPER subprocess (see above). |
| `extra_keywords` | `dict` | Additional `$riper` sub-keywords, e.g. `{"epssmt": "1.0d-8", "marij": False}`. |
| `extra_groups` | `dict` | Additional data groups, e.g. `{"$title": ["my run"], "$marij": []}`. |
| `convergence_check` | `"error"`, `"warning"`, `"ignore"` | What to do when the SCF does not converge (`RIPERConvergenceError`, a warning, or nothing). `calc.converged` records the outcome. |
| `calculate_stress` | `bool` | Compute the stress tensor in every periodic run (also computed on demand). `stress=True` is accepted as a synonym. |
| `dipole` | `bool` | Compute the dipole moment in every run (`$fields electric on`; also on demand). |
| `plot_dens` | `bool` | Write the density cube `td.cub` in every run (`$pointvalper fmt=cub`). |
| `forces` | `bool` or `None` | Compute the gradient (default yes, except with `dftu`). |
| `restart` | `bool` | Reuse `RIPER.BANDS` of the directory when basis, functional, mesh, charge, spin and smearing are unchanged (stale files are removed otherwise). |
| `dftu` | `dict` | Dudarev DFT+U, `{"zn": ("d", 10.0)}` with U_eff in eV (`$dftpu`); energies only in 8.1. |
| `relativistic`, `kramers` | `"2c"`, `bool` | Two-component calculation (`$soghf`, use `dhf-*-2c` basis sets and `dhf-ecp-2c`), Kramers-restricted with `kramers=True`. |
| `directory` | `str` | Working directory. |

Unknown keywords raise a `TypeError`.

## Results

`calc.results` uses ASE's names: `energy` (RIPER `TOTAL ENERGY`; with smearing the
energy extrapolated to zero smearing, `ENERGY (sigma->0)`), `free_energy` (`FREE ENERGY`,
the quantity forces and stress are consistent with; `atoms.get_potential_energy(force_consistent=True)`),
`forces` (from the `gradient` file), `stress` (ASE Voigt order, `sigma = (1/V) dE/deps` with
the ASE cell volume; negative diagonal = the cell wants to expand), `dipole`, `magmom`
(unpaired electrons from P*S), `fermi_level`, `eigenvalues` (nspins, nkpts, nbands),
`occupations`, `ibz_kpoints`, `kpoint_weights`, `nbands`, `nspins`, `nkpts` (from
`riper_bands.txt`), `band_gap`, `vbm`, `cbm` (RIPER's Fermi level statistics),
`riper_energies` (all printed energy terms in eV), `scf_cycles`, `riper_version`.

With these, `calc.get_eigenvalues()`, `calc.get_fermi_level()`, `ase.dft.dos.DOS(calc)` and
`ase.dft.bandgap.bandgap(calc)` work. For shifted even meshes RIPER lists the Gamma point in
addition to the mesh (weight 0); 8.1 includes it in the band-gap statistics.

## Post-SCF tasks

Each task reruns riper in the calculation directory, restarting from the converged
bands, with its output in `riper.<task>.out`.

| Method | riper input | Returns |
| --- | --- | --- |
| `band_structure(path=None, npoints=60, density=None, projected=None)` | `$kpoints kptlines` along the ASE band path | `ase.spectrum.band_structure.BandStructure` (eV, reference = Fermi level); with `projected={"elements": [...], "atoms": [...], "resolve": "l"}` the Mulliken fat-band weights as `bs.projections` |
| `get_dos(width=0.1, npts=1000, emin=None, emax=None, pdos=None)` | `$dosper` (+ `$pdosper`) | `DOSResult`: `energies` (eV), `fermi_level`, `total`, `columns` (`s`, `p`, ...), `projections` (`el_o`, `at1_mg`, ... -> columns), `files` |
| `rttddft_spectrum(resolution=None, polarization="iso")` | `$rttddft spectrum [fine] [iso|x|y|z]` | dict with `energies` (eV) and `intensities` from `rtspec` |
| `write_cubes(["dens"])` | `$pointvalper fmt=cub` | list of cube files |

`width`, `emin`, `emax` are in eV (converted to Hartree for riper); the DOS window
defaults to Fermi level -20 eV ... +15 eV.

## Files written

`coord`, `control`, `riper.out`, `gradient`, `.riper_ase.json` (bookkeeping: frame
rotation, restart fingerprint) and, from riper, `riper_bands.txt`, the restart files
`RIPER.BANDS*`, `mos`/`alpha`/`beta`, and per task `riper.bands.out`/`bands.xyz`/
`bands_proj.xyz`, `riper.dos.out`/`dos`/`pdos_*`, `riper.rttddft.out`/`rtspec`,
`riper.cubes.out`/`*.cub`.

## Changes from 0.2.9

Scripts written for 0.2.9 run unchanged. Behaviour that changed: energies of smeared runs
follow the ASE convention above; forces come from the `gradient` file (`$grad file=gradient`
is written) instead of the `$grad` block in `control`; stress is read from the current
RIPER output (the formats 0.2.x parsed no longer exist); even meshes are written with
`shifted`; structures in any orientation are handled; stale `RIPER.BANDS` files are
removed when the electronic settings change; the riper version is checked once. See
`CHANGELOG.md`.

## Examples

`examples/example01` to `example23` cover single points, optimisations, 1D/2D/3D
systems, stress and cell optimisation, NEB, vibrations, phonons, hybrid functionals,
dispersion, cube files, dipoles, smearing/`desnue`, ECPs and mixed basis sets.
`example24` to `example36` show the additions of 0.3: results overview, band structure,
DOS/PDOS, equation of state, molecular dynamics, bilayer graphene stacking, CO on a MgO
slab, open-shell/charged molecules, DFT+U, RT-TDDFT spectrum, 1c versus 2c iodine, hybrid
band gap versus k mesh, elastic constants from the stress tensor.
Each script writes into `calculations/`.

## Contact

Manas Sharma

- manas.sharma@turbomole.org
- manas.sharma@uni-jena.de
- manassharma07@live.com
