---
og:title: Quantum Espresso
---

# ⚛️ ☕ Quantum Espresso

[Quantum Espresso](https://www.quantum-espresso.org/) (QE) es un código DFT que
implementa pseudopotenciales y ondas planas de manera eficiente para obtener
la densidad electrónica de sistemas materiales y moleculares,
que es muy usado por un buen número de usuarias/os en los clusters de UNC Supercómputo.
El código está diseñado para correr de manera eficiente en infraestructuras HPC
aprovechando los protocolos de paralelización MPI/OpenMP.

El software está disponible a través de [modules](/tutoriales/modules).


## Almacenamiento

Los clusters cuentan con distintos sistemas para el almacenamiento de archivos.
Es importante distinguir entre almacenamiento local y almacenamiento remoto.

Ver [almacenamiento `/home` y `/scratch`](/infra/almacenamiento).


### Buenas prácticas en la gestión de datos

* **Almacene archivos esenciales y de largo plazo en la partición home**

    Para tareas computacionales, copie los archivos de entrada necesarios al espacio _scratch_ local al inicio de su trabajo
    y transfiera los archivos de salida de regreso a la partición _home_ al finalizar.
    Este enfoque aprovecha las capacidades de alta velocidad del espacio _scratch_ durante la computación,
    asegurando al mismo tiempo que los datos importantes se preserven en la partición _home_.

* **Monitoree y limpie regularmente los archivos en la partición home**

    Para liberar espacio y cumplir con las políticas del sistema respecto al uso de almacenamiento de datos
    ya que el espacio en la partición _home_ es limitado.
    Se recomienda fuertemente borrar todos los archivos temporales
    que hayan sido generados por los códigos de cálculo y mantener sólo los archivos esenciales.


### Archivos generados por QE

El proceso de entrada/salida (I/O) de archivos en una corrida típica
de optimización de geometría (`relax`) con QE implica la creación y gestión
de varios archivos temporales y permanentes que contienen información esencial para la simulación.

A continuación, se detalla cómo QE maneja estos archivos durante una ejecución de `pw.x` en modo `relax`.

#### Variables clave

* **outdir**

    Especifica el directorio donde QE almacena archivos temporales y de salida. Debe ser accesible para todos los procesos en ejecuciones paralelas. Por ejemplo:

    ```
    &CONTROL
        outdir = '/scratch/$USER/qe_temp'
        ...
    /
    ```

    Es recomendable que `outdir` apunte a un sistema de archivos rápido y local al nodo de cómputo para mejorar el rendimiento de I/O.
    Recomendamos usar la variable del entorno `ESPRESSO_TMPDIR` para definir el directorio de almacenamiento de archivos
    en lugar de la variable `outdir` en el input para QE.

* **prefix**

    Define el prefijo utilizado para nombrar los archivos generados. Por ejemplo:

    ```
    &CONTROL
        prefix = 'mi_simulacion'
        ...
    /
    ```

    QE creará un subdirectorio llamado `mi_simulacion.save/` dentro de `outdir`,
    que contendrá los archivos necesarios para reiniciar o continuar la simulación.

#### Estructura de directorios

Durante una corrida de `pw.x` en modo `relax`, QE genera varios archivos:

* **prefix.save/**

    Contiene información esencial de la simulación, como la densidad de carga, potenciales y funciones de onda.
    Este directorio es crucial para reiniciar simulaciones interrumpidas.​

* **prefix.wfc\***

    Archivos binarios que almacenan las funciones de onda de los electrones.​

* **prefix.igk\***

    Contienen información sobre los vectores de onda utilizados en la simulación.​

* **prefix.dos, prefix.bands, etc.**

    Dependiendo de las opciones de salida seleccionadas,
    QE puede generar archivos adicionales con información sobre la densidad de estados,
    bandas electrónicas, entre otros.


Durante los cálculos realizados con `pw.x`,
cada ciclo `scf` genera un proceso de lectura/escritura que puede involucrar desde unos pocos cientos de MB hasta decenas de GB,
dependiendo del tamaño del sistema y de los parámetros utilizados.
En modos como `relax`, `md` o `neb`, este proceso se repite múltiples veces, ya que se ejecutan varios ciclos `scf` sucesivos.
Por este motivo, no se recomienda escribir los archivos temporales en el directorio /home,
ya que no está optimizado para operaciones intensivas de I/O.
Para mejorar el rendimiento, se aconseja redireccionar estos archivos a un directorio temporal en cada nodo de cálculo.


```{seealso}
Para ver un ejemplo de cómo implemetar esto, ver [script de slurm](#script-de-slurm).
```

## Paralelismo

Quantum Espresso implementa varias estrategias de paralelización,
y entenderlas es de gran importancia para aprovechar al máximo un cluster de alto rendimiento.
Todos los detalles sobre paralelismo en QE se pueden consultar en
[esta sección del manual](https://www.quantum-espresso.org/Doc/user_guide/node17.html).
Resumimos aquí algunas ideas principales.

### Estrategias comunes

Las estrategias de paralelización más comúnmente utilizados son:

* MPI (_"Message Passing Interface"_):
    * Capa principal de paralelismo (estados electrónicos).
    * Distribuye los puntos-k y las funciones de onda entre los procesos (ranks).

* OpenMP (multihilo):
    * Puede usarse para paralelizar bucles sobre grillas FFT, grillas en el espacio real y vectores-G.
    * Funciona mejor cuando se combina con MPI en una configuración híbrida.

* Paralelismo por Grupos o "Pools" (`-npool`):
    * Útil cuando se tienen múltiples puntos-k.
    * Cada grupo maneja un subconjunto de puntos-k, muy eficiente para metales o sistemas grandes.

* Paralelismo por Imágenes (`-nimage`):
    * Para cálculos NEB.
    * Distribuye cada imagen de la trayectoria a diferentes grupos de procesos.

* Paralelismo en la Diagonalización (`-ndiag`):
    * Paraleliza la diagonalización de matrices para cada punto-k.
    * Particularmente importante para sistemas grandes con muchas bandas.
    * Funciona con ScaLAPACK o ELPA.

* Grupos de Tareas (`-ntg`):
    * Menos utilizado.
    * Distribuye las FFT entre grupos para reducir el uso de memoria.


### Guía rápida de parámetros

:::{list-table}
:header-rows: 1
:widths: 10 25 20 10

*   - Parámetro
    - Descripción
    - Cuándo Usarlo
    - Ejemplo

*   - `-npool N`
    - **Paralelismo por puntos k**.<br>Distribuye los puntos k en N grupos.
    - Sistemas con múltiples puntos k (metales, superceldas, grillas densas).
    - `-npool 4`

*   - `-nimage N`
    - **Paralelismo por imágenes**(NEB o caminos de reacción).<br>Cada imagen recibe un grupo de procesos MPI.
    - Cálculos NEB.
    - `-nimage 8`

*   - `-ndiag N`
    - **Paralelismo en la diagonalización**.<br>Mediante ScaLAPACK o ELPA. Paraleliza sobre los índices de bandas.
    - Sistemas grandes con muchas bandas (>100) o funcionales híbridos.
    - `-ndiag 8`

*   - `-ntg N`
    - **Paralelismo por grupos de tareas**.<br>Distribuye las FFTs para reducir el uso de memoria.
    - Grandes redes de FFT o cuando la memoria por núcleo es limitada.
    - `-ntg 2`

*   - `-nb N`
    - **Paralelismo por bandas**.<br>Distribuye las funciones de onda dentro de cada punto k.
    - Sistemas muy grandes con muchas bandas, especialmente con DFT híbrido.
    - `-nb 2`

*   - `-nt N`
    - **Multihilo OpenMP**.<br>Se usa para paralelismo híbrido MPI + OpenMP.
    - Cuando se usa una combinación de MPI y OpenMP.
    - `export OMP_NUM_THREADS=2`

*   - `-input`
    - Especifica el archivo de entrada (alternativo a `< entrada.in` o `-in`).
    - Sintaxis alternativa opcional.
    - `-input scf.in`
:::


### Guía para elegir los parámetros

:::{list-table}
:header-rows: 1

*   - Característica del sistema
    - Estrategia Recomendada

*   - Pocos átomos, muchos puntos k
    - Usar `-npool`

*   - Muchos átomos, pocos puntos k
    - Usar `-ndiag`, `-nt` o `-nb`

*   - Cálculo NEB
    - Usar `-nimage` junto con `-npool`

*   - Cálculo de fonones
    - Usar `-npool` en `ph.x` para paralelizar por puntos q

*   - Memoria limitada por núcleo
    - Usar `-ntg`

*   - DFT híbrido o meta-GGA
    - Usar `-nb`, `-ndiag` y aumentar el uso de hilos/memoria
:::


### Guía para encontrar el punto óptimo

Para poder escoger los parámetros de paralelización de manera adecuada y juiciosa
es absolutamente necesario realizar un estudio de _benchmarking_.

Paso a paso:

1. **Seleccionar un sistema representativo**

    Elegir un sistema realista y representativo del sistema bajo estudio, por ejemplo:

    * Un sólido periódico con ~100 - 300 átomos
    * Un camino de NEB con ~6 - 8 imágenes
    * Un sistema de molécula + superficie con 1 - 4 puntos-k

    ```{admonition} Importante
    :class: info
    Para todos los benchmarks, mantener fija la entrada: geometría, cutoffs, pseudopotenciales, etc.
    ```

1. **Probar configuraciones paralelas distintas**

    Variar el número de `ranks MPI`, `hilos OpenMP` y usar distintas combinaciones de `-npool`, `-ndiag`, etc.

    :::{list-table}
    :header-rows: 1

    *   - ID de prueba
        - Nodos
        - MPI por nodo
        - Hilos
        - Núcleos totales
        - Notas
    *   - A
        - 1
        - 32
        - 2
        - 64
        - MPI por socket + OpenMP
    *   - B
        - 2
        - 64
        - 1
        - 128
        - Todo MPI
    *   - C
        - 4
        - 32
        - 2
        - 256
        - Híbrido MPI + OpenMP
    *   - D
        - 4
        - 64
        - 1
        - 256
        - Todo MPI (nodos densos)
    :::

    Probar diferentes combinaciones, como por ejemplo:

    * `-npool 4`, `-ndiag 4`, `-nb 2`
    * `-npool 8`, `-ndiag 8`, sin paralelismo por bandas
    * Usar `OMP_NUM_THREADS` para habilitar OpenMP

1. **Medir tiempos y escalabilidad**

    Después de cada corrida, extraer del output:

    * El tiempo total transcurrido (wall time)
    * Las líneas de "CPU/Wall" informadas por QE para cada una de las rutinas (de ser necesario)

    :::{dropdown} Ejemplo de salida
    ```{code-block}
    :caption: output.log
    :emphasize-lines: 6

    Parallel routines

        PAW routines
        PAW_pot      :      8.70s CPU     12.33s WALL (     204 calls)

        PWSCF        :   3m43.66s CPU   5m11.34s WALL


    This run was terminated on:  10:55:31  24Apr2025

    =------------------------------------------------------------------------------=
    JOB DONE.
    =------------------------------------------------------------------------------=
    ```
    :::

    Para obtener el tiempo total de ejecución se puede usar:

    ```{code-block} console
    $ grep "PWSCF.*WALL" output.log
    ```

    O agregar al script:

    ```{code-block} console
    time srun … pw.x …
    ```


1. **Calcular la eficiencia paralela**

    Usar un tiempo de referencia {math}`T_{ref}`​ (por ejemplo, la corrida en 1 nodo) y calcular:

    * Aceleración (speedup):

    ```{math}
    S_p = \frac{T_{ref}}{T_p}
    ```

    * Eficiencia:

    ```{math}
    E_p = \frac{S_p}{p}
    ```

    donde {math}`p` es la cantidad de nodos (o núcleos totales).

    Una **eficiencia menor al 50 - 60 % indica sobreescalado**: se están malgastando núcleos.

1. **Identificar el punto óptimo**

    Graficar:

    * Tiempo de ejecución vs cantidad de núcleos
    * Eficiencia vs cantidad de núcleos

    Buscar el _"hombro de la curva"_, es decir el punto donde agregar más núcleos apenas mejora el tiempo.
    Este es el punto **óptimo de paralelización**.

    Ejemplo: Si duplicar los núcleos de 128 a 256 sólo reduce el tiempo un 15 %, conviene quedarse en 128.


### Buenas prácticas para usuarios (resumen)

:::{list-table}
:header-rows: 1
:widths: 1 2

*   - Situación
    - Estrategia paralela recomendada
*   - Sistema con muchos puntos-k
    - Aumentar `-npool` (un múltiplo del número total de cores; idealmente los puntos k deben distribuirse homogéneamente entre los pools)
*   - Sistema con muchas bandas
    - Usar `-ndiag`, `-nb`, y `OMP_NUM_THREADS > 1`
*   - NEB con muchas imágenes
    - Usar `-nimage` (idealmente una imagen por nodo)
*   - Poca memoria por núcleo
    - Usar `-ntg`, reducir hilos, activar paralelismo por bandas
:::


## Script de slurm

A continuación un script de ejemplo para usar como _template_,
que incluye algunas de las buenas practicas que ya fueron explicadas:

```{code-block} slurm
:linenos:
:caption: submit.sh
:emphasize-lines: 35-36,42
#!/bin/bash
#SBATCH --partition=multi
#SBATCH --nodes=4
#SBATCH --ntasks-per-node=64
#SBATCH --cpus-per-task=1
#SBATCH --time=0-1:00
#SBATCH --exclusive
#SBATCH --output=output.log
#SBATCH --error=error.log

# Load Quantum ESPRESSO
module load quantum-espresso/7.2

export OMP_NUM_THREADS=$SLURM_CPUS_PER_TASK

echo "USER = $USER"
echo "SLURM_JOB_ID = $SLURM_JOB_ID"
echo "SLURMD_NODENAME = $SLURMD_NODENAME"
echo "SLURM_NODELIST = $SLURM_NODELIST"
echo "SLURM_NNODES = $SLURM_NNODES"

# Define scratch and temporary directories
export SCRATCH_DIR=/scratch/$USER/$SLURM_JOB_ID
export TMP_DIR=./tmp

# Create directory for temporary files in /home partition
mkdir -p "$TMP_DIR"
# Create directories for temporary files in each local node /scratch partition
srun --ntasks=$SLURM_NNODES --ntasks-per-node=1 mkdir -p "$SCRATCH_DIR"

# Copy previous run's temporary files to scratch directory
srun --ntasks=$SLURM_NNODES --ntasks-per-node=1 rsync -a "$TMP_DIR/" "$SCRATCH_DIR/"

# Define relevant QE environment variables
# Location of pseudopotentials - Edit this line if necessary
export ESPRESSO_PSEUDO=~/QE_PSP
# Directory for temporary files - Do not edit this line!
export ESPRESSO_TMPDIR="$SCRATCH_DIR"
# NB: Do not use keywords pseudo_dir & outdir in QE input

# Run QE
srun --cpu_bind=cores pw.x -in pw_relax.inp >> pw_relax.out
exit_status=$?
if [ $exit_status -ne 0 ]; then
    echo "pw.x exited with status $exit_status"
    # Optionally perform additional error handling here
fi

# Synchronize files from scratch to home directory
echo "Initiating sync at $(date)"
srun --ntasks=$SLURM_NNODES --ntasks-per-node=1 rsync -a "$SCRATCH_DIR/" "$TMP_DIR/"
if [ $? -ne 0 ]; then
    echo "Warning: rsync operation failed."
elif
    echo "Sync completed."
fi

```

En este ejemplo, se utilizan 4 nodos para lanzar un cálculo ejecutando `pw.x`.

En principio, lo único que sería necesario editar es:

* **La ruta a los pseudopotenciales** (línea 36)
    se recomienda, tal como en el ejemplo, utilizar la variable de ambiente `ESPRESSO_PSEUDO` en lugar de declarar la ruta en el input
* **Nombres de los archivos input / output** (línea 42)

El input que se utilice en este caso debe contener siguientes _keywords_ en el módulo `&CONTROL`:

```
&CONTROL
    calculation = 'relax' ,
    restart_mode = 'restart' ,
    max_seconds =  172000,
    disk_io = 'low' ,
    verbosity = 'high' ,
    nstep = 50 ,
/
```

Como se puede ver que **no se utilizan** las _keywords_ `outdir` ni `pseudo_dir`,
ya que ambos directorios vienen definidos en el script de slurm mediante las variables de ambiente `ESPRESSO_TMPDIR` y `ESPRESSO_PSEUDO`, respectivamente.

Por otro lado, es importante utilizar la _keyword_ `max_seconds` para evitar que el cálculo exceda el tiempo límite de la cola de cálculo,
para que de este modo se escriban archivos temporales para un hacer un _restart_.

El `restart_mode` se pone directamente en `restart`, ya que en la nueva versión de QE no da error aunque el cálculo sea `from_scratch`.
De este modo, si es necesario realizar un restart,
**solo basta volver a enviar el script a la cola sin realizar ninguna modificación**, ni en el propio script ni el input.

Incluso, **es posible lanzar los sucesivos cálculos en distinta cantidad de nodos** de manera transparente.


```{note}
Al ejecutar `pw.x` en modo `scf`, los archivos se escriben únicamente al finalizar el ciclo de autoconsistencia,
ya sea porque se alcanzó la convergencia (`conv_thr`),
se completó el número máximo de pasos (`electron_maxstep`)
o se agotó el tiempo máximo asignado al cálculo (`max_seconds`).

En cualquier caso, ocurre un único evento de escritura,
por lo que este tipo de cálculos pueden realizarse de la manera habitual,
es decir, utilizando la partición /home para almacenar los archivos temporales.

De todas formas, el script propuesto es igualmente compatible y se recomienda su uso para minimizar posibles errores.
```
