#
from __future__ import annotations
__author__= "Sabyasachi Tiwari"
__copyright__= "Copyright 2024, EPWpy project"
__version__= "1.0"
__maintainer__= "Sabyasachi Tiwari"
__maintainer_email__= "sabyasachi.tiwari@austin.utexas.edu"
__status__= "Production"
__date__= "May 03, 2024"
import numpy as np
import os
import subprocess as sp
from EPWpy.utilities.printing import *
from EPWpy.utilities.write_job import *
#
[docs]
class PyRun:
"""
The **PyRun** class provides an interface to execute external electronic
structure and many-body codes such as **Quantum ESPRESSO (QE)** and **BerkeleyGW (BGW)**
within the **EPWpy** framework.
It can be used both as part of a higher-level EPWpy workflow or as a
standalone runner to execute specific computational tasks.
Parameters
----------
procs : int
Number of processors to be used for the execution.
env : str
Type of execution environment. Typical values include:
- `"mpirun"` for MPI execution,
- `"srun"` for SLURM-based parallel runs,
- `"ibrun"` for TACC systems,
- `"local"` for serial runs.
code : str
Path to the executable code or command to run.
For example: `"/path/to/pw.x"` or `"/path/to/bgw.x"`.
Attributes
----------
env : str
Environment command used to launch the executable.
procs : int
Number of processors assigned to the job.
code : str
Full path or command to the simulation code being executed.
Examples
--------
>>> from epwpy.run import PyRun
>>> run_qe = PyRun(procs=8, env="mpirun", code="/path/to/pw.x")
>>> run_qe
<PyRun object for QE execution>
Notes
-----
- The class is primarily used internally by EPWpy to manage code execution
for different simulation stages (DFT, DFPT, EPW, etc.).
- In HPC environments, `env` should correspond to the launcher provided by
the scheduler (e.g., `ibrun` on TACC or `srun` on SLURM systems).
"""
def __init__(self, procs, env, code):
"""
Initialize a new PyRun instance with the specified runtime configuration.
Parameters
----------
procs : int
Number of processors to use.
env : str
Type of execution environment (`mpirun`, `srun`, `ibrun`, etc.).
code : str
Path to the executable or command to be run.
Returns
-------
None
Note
-------
Every run requires a utility which needs to be provided usinf
obj.util = pw.x
"""
self.procs = procs
self.env = env
self.code = code
self.serial = True
self.write = False
[docs]
@decorated_progress
def run_custom(self, folder='./custom', name='custom', util=None):
"""
Run a custom executable within the EPWpy environment.
This method provides a flexible interface for executing any external
code (e.g., a user-defined tool, a Quantum ESPRESSO utility, or a
post-processing script) using the same EPWpy execution infrastructure.
It automatically handles directory changes, input/output file naming,
and command construction before delegating to :meth:`runner`.
Parameters
----------
folder : str, optional
Directory in which the calculation should be executed.
Default is `'./custom'`.
name : str, optional
Base name of the input/output files. Default is `'custom'`,
corresponding to `'custom.in'` and `'custom.out'`.
util : str or None, optional
Name of the executable or utility to run.
If ``None``, attempts to use the value stored in :attr:`self.util`.
Behavior
--------
- Sets the working directory :attr:`self.dir` to `folder`.
- Defines :attr:`self.filein` and :attr:`self.fileout` based on `name`.
- Constructs :attr:`self.util` using either:
* The provided `util` string, if specified.
* The existing :attr:`self.util` if `util` is ``None``.
- Invokes :meth:`runner` to execute or script-write the command.
Returns
-------
subprocess.Popen or None
The process handle from :meth:`runner`, or ``None`` if the command
is written to a script instead of executed.
Raises
------
ValueError
If both `util` and :attr:`self.util` are ``None``, meaning no
executable is defined.
Examples
--------
>>> run = PyRun(procs=4, env='srun', code='/path/to/qe')
>>> run.run_custom(folder='./pp', name='dos', util='dos.x')
# Executes equivalent to:
# srun -n 4 /path/to/qe/dos.x dos.in > dos.out
Notes
-----
- This method is useful for integrating third-party or auxiliary codes
into automated EPWpy workflows.
- If the environment variables (`self.env`, `self.procs`, etc.)
are properly configured, `run_custom()` behaves identically
to EPW-specific runners.
"""
if util is None:
self.util = f'{util} ' + str(self.proc_set) + ' -in '
else:
self.util = f'{util} '
self.dir = folder
self.filein = name + '.in'
self.fileout = name + '.out'
self.runner()
[docs]
@decorated_progress
def run_scf(self, folder='./scf', name='scf'):
"""
Run a self-consistent field (SCF) calculation using Quantum ESPRESSO.
This method constructs and executes the SCF calculation command,
automatically setting the parallelization options (`-nk` and `-nt`)
based on user input or preconfigured processor settings.
Parameters
----------
folder : str, optional
Directory where the SCF calculation will be executed.
Default is `'./scf'`.
name : str, optional
Base name for the input and output files.
Default is `'scf'`, corresponding to `'scf.in'` and `'scf.out'`.
Behavior
--------
- Determines the number of k-point and thread pools from
:meth:`set_processors` if :attr:`self.proc_set` is ``None``.
- Constructs the Quantum ESPRESSO SCF command using `pw.x`.
- Sets the execution directory (:attr:`self.dir`), input (:attr:`self.filein`),
and output (:attr:`self.fileout`) filenames.
- Calls :meth:`runner` to execute the command or write it to a script.
Returns
-------
subprocess.Popen or None
The process handle from :meth:`runner`, or ``None`` if the job
is written to a script instead of being executed directly.
Notes
-----
- Requires a properly configured Quantum ESPRESSO environment.
- This is typically the first stage of an EPW workflow, preceding
NSCF, PH, and EPW runs.
- Uses `pw.x` as the default executable unless overridden by
:attr:`self.proc_set`.
Examples
--------
>>> run = PyRun(procs=8, env='srun', code='/path/to/qe')
>>> run.run_scf(folder='./scf', name='graphene_scf')
# Executes equivalent to:
# srun -n 8 /path/to/qe/pw.x -nk 4 -nt 2 -in graphene_scf.in > graphene_scf.out
"""
if self.proc_set is None:
nt, nk = self.set_processors(self.procs)
nt = int(nt)
nk = int(nk)
self.util = 'pw.x -nk ' + str(nk) + ' -nt ' + str(nt) + ' -in '
else:
self.util = 'pw.x ' + str(self.proc_set) + ' -in '
self.dir = folder
self.filein = name + '.in'
self.fileout = name + '.out'
print("EPWpy_run.py: run_scf()")
self.runner()
[docs]
@decorated_progress
def run_nscf(self, folder='./nscf', name='nscf'):
"""
Run a non-self-consistent field (NSCF) calculation using Quantum ESPRESSO.
This method executes the NSCF stage, which computes the electronic structure
on a denser k-point grid using the converged charge density from the SCF run.
It constructs the appropriate `pw.x` command and runs it via :meth:`runner`.
Parameters
----------
folder : str, optional
Directory where the NSCF calculation will be executed.
Default is `'./nscf'`.
name : str, optional
Base name for the input and output files.
Default is `'nscf'`, corresponding to `'nscf.in'` and `'nscf.out'`.
Behavior
--------
- Determines the number of thread and k-point pools using
:meth:`set_processors` if :attr:`self.proc_set` is ``None``.
- Constructs the NSCF command using Quantum ESPRESSO’s `pw.x` executable.
- Sets the execution directory (:attr:`self.dir`), input (:attr:`self.filein`),
and output (:attr:`self.fileout`) file paths.
- Calls :meth:`runner` to execute or write the NSCF command.
Returns
-------
subprocess.Popen or None
The process handle returned by :meth:`runner` if executed,
or ``None`` if the job is written to a script instead.
Notes
-----
- The NSCF run should follow a completed SCF calculation in the same system folder.
- This step is essential for generating the wavefunctions required for
phonon or EPW calculations.
- Uses `pw.x` by default, unless overridden via :attr:`self.proc_set`.
Examples
--------
>>> run = PyRun(procs=8, env='srun', code='/path/to/qe')
>>> run.run_nscf(folder='./nscf', name='graphene_nscf')
# Executes equivalent to:
# srun -n 8 /path/to/qe/pw.x -nk 4 -nt 2 -in graphene_nscf.in > graphene_nscf.out
"""
if self.proc_set is None:
nt, nk = self.set_processors(self.procs)
nt = int(nt)
nk = int(nk)
self.util = 'pw.x -nk ' + str(nk) + ' -nt ' + str(nt) + ' -in '
else:
self.util = 'pw.x ' + str(self.proc_set) + ' -in '
self.dir = folder
self.filein = name + '.in'
self.fileout = name + '.out'
self.runner()
[docs]
@decorated_progress
def run_bs(self, folder='./bs', name='bs'):
"""
Run a band structure (BS) calculation using Quantum ESPRESSO.
This method executes the band structure step of a typical DFT workflow.
It reads the converged charge density from a previous SCF calculation
and computes eigenvalues along a predefined high-symmetry k-path.
Parameters
----------
folder : str, optional
Directory where the band structure calculation will be executed.
Default is `'./bs'`.
name : str, optional
Base name for the input and output files.
Default is `'bs'`, corresponding to `'bs.in'` and `'bs.out'`.
Behavior
--------
- If :attr:`self.proc_set` is ``None``, automatically determines
the number of k-point and thread pools using :meth:`set_processors`.
- Constructs the command line for Quantum ESPRESSO’s `pw.x` executable.
- Sets the working directory (:attr:`self.dir`), input file (:attr:`self.filein`),
and output file (:attr:`self.fileout`).
- Executes the calculation via :meth:`runner`.
Returns
-------
subprocess.Popen or None
The process handle returned by :meth:`runner` if executed,
or ``None`` if the job is written to a script instead.
Notes
-----
- Requires a prior SCF calculation with a converged charge density file (`*.save`).
- Typically used for generating electronic band structures along
high-symmetry directions specified in the NSCF k-path.
- Uses `pw.x` as the default executable unless overridden by :attr:`self.proc_set`.
Examples
--------
>>> run = PyRun(procs=8, env='srun', code='/path/to/qe')
>>> run.run_bs(folder='./bs', name='graphene_bs')
# Executes equivalent to:
# srun -n 8 /path/to/qe/pw.x -nk 4 -nt 2 -in graphene_bs.in > graphene_bs.out
"""
if self.proc_set is None:
nt, nk = self.set_processors(self.procs)
nt = int(nt)
nk = int(nk)
self.util = 'pw.x -nk ' + str(nk) + ' -nt ' + str(nt) + ' -in '
else:
self.util = 'pw.x ' + str(self.proc_set) + ' -in '
self.dir = folder
self.filein = name + '.in'
self.fileout = name + '.out'
self.runner()
[docs]
@decorated_progress
def run_ph(self, folder='./ph', name='ph'):
"""
Run a phonon calculation using Quantum ESPRESSO's ph.x module.
This method executes the phonon calculation stage, which computes
phonon frequencies, dynamical matrices, and related properties
required for electron-phonon coupling analysis.
It sets up the appropriate working directory, input/output files,
and constructs the parallel execution command before calling
:meth:`runner`.
Parameters
----------
folder : str, optional
Directory where the phonon calculation will be executed.
Default is `'./ph'`.
name : str, optional
Base name of the input/output files.
Default is `'ph'`, corresponding to `'ph.in'` and `'ph.out'`.
Behavior
--------
- If :attr:`self.proc_set` is ``None``, determines the number of
thread and k-point pools using :meth:`set_processors`.
- Constructs the execution command using `ph.x`, including the
`-pd .true.` flag for parallel diagonalization.
- Sets the working directory (:attr:`self.dir`), input file
(:attr:`self.filein`), and output file (:attr:`self.fileout`).
- Calls :meth:`runner` to execute or script-write the phonon job.
Returns
-------
subprocess.Popen or None
The process handle returned by :meth:`runner` if executed,
or ``None`` if the job is written to a script instead.
Notes
-----
- Requires a prior SCF/NSCF calculation for the converged charge density.
- The `-pd .true.` flag enables parallel phonon calculations.
- This step is essential for subsequent EPW calculations involving
electron-phonon interactions.
Examples
--------
>>> run = PyRun(procs=8, env='srun', code='/path/to/qe')
>>> run.run_ph(folder='./ph', name='graphene_ph')
# Executes equivalent to:
# srun -n 8 /path/to/qe/ph.x -pd .true. -nk 4 -nt 2 -in graphene_ph.in > graphene_ph.out
"""
if self.proc_set is None:
nt, nk = self.set_processors(self.procs)
nt = int(nt)
nk = int(nk)
self.util = 'ph.x -pd .true. -nk ' + str(nk) + ' -nt ' + str(nt) + ' -in '
else:
self.util = 'ph.x ' + str(self.proc_set) + ' -in '
self.dir = folder
self.filein = name + '.in'
self.fileout = name + '.out'
self.runner()
[docs]
@decorated_progress
def run_pp(self, folder='./pp', name='pp'):
"""
Run a post-processing (PP) calculation using Quantum ESPRESSO's pp.x module.
This method executes a generic post-processing step, which can be used to
compute charge densities, potential maps, densities of states, or other
derived quantities from previously calculated wavefunctions or densities.
It sets up the appropriate working directory, input/output files, and
constructs the execution command before delegating to :meth:`runner`.
Parameters
----------
folder : str, optional
Directory where the post-processing calculation will be executed.
Default is `'./pp'`.
name : str, optional
Base name of the input/output files.
Default is `'pp'`, corresponding to `'pp.in'` and `'pp.out'`.
Behavior
--------
- If :attr:`self.proc_set` is ``None``, builds a default serial or
basic parallel `pp.x` command.
- Otherwise, constructs the command using the specified processor setup.
- Sets the working directory (:attr:`self.dir`), input file
(:attr:`self.filein`), and output file (:attr:`self.fileout`).
- Calls :meth:`runner` to execute or script-write the post-processing job.
Returns
-------
subprocess.Popen or None
The process handle returned by :meth:`runner` if executed,
or ``None`` if the job is written to a script instead.
Notes
-----
- Requires a prior SCF or NSCF calculation providing the necessary
wavefunctions or densities.
- The `pp.x` module is flexible; this runner assumes the user has prepared
the input file (`pp.in`) appropriately for the desired analysis.
Examples
--------
>>> run = PyRun(procs=4, env='mpirun', code='/path/to/qe')
>>> run.run_pp(folder='./pp', name='graphene_pp')
# Executes equivalent to:
# mpirun -np 4 /path/to/qe/pp.x -in graphene_pp.in > graphene_pp.out
"""
if self.proc_set is None:
self.util = 'pp.x -in '
else:
self.util = 'pp.x ' + str(self.proc_set) + ' -in '
self.dir = folder
self.filein = name + '.in'
self.fileout = name + '.out'
self.runner()
[docs]
@decorated_progress
def run_epw1(self, folder='./epw', name='epw1'):
"""
Execute the first stage of an EPW (Electron–Phonon Wannier) calculation.
This method configures and runs the **EPW step 1** task, which typically
computes the electron–phonon coupling matrix elements on coarse
Brillouin-zone grids. It sets up the appropriate working directory,
input/output filenames, and executable command, then delegates
execution to the :meth:`runner` method.
Parameters
----------
folder : str, optional
Directory in which the EPW 1 calculation will be executed.
Default is `'./epw'`.
name : str, optional
Base name of the EPW input/output files.
Default is `'epw1'`, corresponding to input file `'epw1.in'`
and output file `'epw1.out'`.
Behavior
--------
- Sets the working directory attribute :attr:`self.dir` to `folder`.
- Defines :attr:`self.filein` and :attr:`self.fileout` using `name`.
- Constructs :attr:`self.util`, the command to run `epw.x`, depending on
whether :attr:`self.proc_set` is defined:
* If :attr:`self.proc_set` is ``None``:
builds ``epw.x -nk <nprocs> -in``.
* Otherwise:
uses the explicit process configuration stored in
:attr:`self.proc_set`.
- Calls :meth:`runner` to execute (or script-write) the command.
Returns
-------
subprocess.Popen or None
The process handle from :meth:`runner`, or ``None`` if
the run command is only written to a script.
Notes
-----
- Relies on pre-defined attributes such as :attr:`self.procs`,
:attr:`self.env`, and :attr:`self.code`, which should be set before
invoking this method.
- Designed to integrate into EPWpy’s automated workflow; users normally
call higher-level workflow functions rather than this routine directly.
Examples
--------
>>> run = PyRun(procs=8, env='mpirun', code='/path/to/qe')
>>> run.proc_set = None
>>> run.run_epw1(folder='./epw', name='epw1')
# Executes equivalent to:
# mpirun -np 8 /path/to/qe/epw.x -nk 8 -in epw1.in > epw1.out
"""
self.dir = folder
self.filein = name + '.in'
self.fileout = name + '.out'
if self.proc_set is None:
nk = int(self.procs)
self.util = f'epw.x -nk {nk} -in '
else:
self.util = f'epw.x {self.proc_set} -in'
self.runner()
[docs]
@decorated_progress
def run_abinit(self, folder='./abinit', name='abi'):
"""
Runner for abinit
"""
self.dir = folder
nk = int(self.procs)
self.filein = name+'.abi'
self.fileout = name+'.log'
self.util = 'abinit '
self.runner()
[docs]
@decorated_progress
def run_vasp(self, folder='./vasp', name=' ', flavor='std'):
"""
Runner for abinit
"""
self.dir = folder
nk = int(self.procs)
self.filein = name#+'.abi'
self.fileout = 'output'#+'.log'
self.util = f'vasp_{flavor}'
self.runner()
[docs]
@decorated_progress
def run_epw2(self, folder='./epw', name='epw2'):
"""
Execute the second stage of an EPW (Electron–Phonon Wannier) calculation.
This method launches **EPW step 2**, which typically performs
fine-grid interpolation of electron–phonon coupling data,
transport property calculations, or other post-processing tasks
following EPW step 1. It configures the appropriate run parameters
and calls the :meth:`runner` method to execute the job.
Parameters
----------
folder : str, optional
Directory where the EPW step 2 calculation will be executed.
Default is `'./epw'`.
name : str, optional
Base name of the EPW input/output files.
Default is `'epw2'`, corresponding to input file `'epw2.in'`
and output file `'epw2.out'`.
Behavior
--------
- Sets the working directory attribute :attr:`self.dir` to `folder`.
- Defines :attr:`self.filein` and :attr:`self.fileout` using `name`.
- Constructs :attr:`self.util`, the EPW command string, depending on
whether :attr:`self.proc_set` is defined:
* If :attr:`self.proc_set` is ``None``:
builds ``epw.x -nk <nprocs> -in``.
* Otherwise:
uses the explicit process setup stored in
:attr:`self.proc_set`.
- Calls :meth:`runner` to execute or write the command script.
Returns
-------
subprocess.Popen or None
The process handle from :meth:`runner`, or ``None`` if the run
command is only written to a job script.
Notes
-----
- Assumes that :attr:`self.procs`, :attr:`self.env`, :attr:`self.code`,
and :attr:`self.proc_set` (if applicable) have been initialized.
- Typically called automatically by higher-level EPWpy workflow
managers after successful completion of :meth:`run_epw1`.
Examples
--------
>>> run = PyRun(procs=8, env='mpirun', code='/path/to/qe')
>>> run.proc_set = None
>>> run.run_epw2(folder='./epw', name='epw2')
# Executes equivalent to:
# mpirun -np 8 /path/to/qe/epw.x -nk 8 -in epw2.in > epw2.out
"""
self.dir = folder
self.filein = name + '.in'
self.fileout = name + '.out'
if self.proc_set is None:
nk = int(self.procs)
self.util = f'epw.x -nk {nk} -in '
else:
self.util = f'epw.x {self.proc_set} -in'
self.runner()
[docs]
@decorated_progress
def run_epw3(self, folder='./epw', name='epw3'):
"""
Runner for epw3
"""
self.dir = folder
nk = int(self.procs)
self.filein = name+'.in'
self.fileout = name+'.out'
self.util = 'epw.x -nk '+str(nk)+' -in '
self.runner()
[docs]
@decorated_progress
def run_zg(self, folder='./zg', name='zg'):
"""
Run a ZG (Zeta-Gamma) calculation using the ZG.x executable.
This method executes the ZG step of the workflow, which is typically
used for specialized post-processing or analysis tasks defined by the
ZG.x module. It sets up the working directory, input/output files, and
constructs the execution command before delegating to :meth:`runner`.
Parameters
----------
folder : str, optional
Directory where the ZG calculation will be executed.
Default is `'./zg'`.
name : str, optional
Base name for the input and output files.
Default is `'zg'`, corresponding to `'zg.in'` and `'zg.out'`.
Behavior
--------
- Sets :attr:`self.dir`, :attr:`self.filein`, and :attr:`self.fileout`.
- Constructs the command string :attr:`self.util` as `'ZG.x -in '`.
- Calls :meth:`runner` to execute or script-write the ZG calculation.
Returns
-------
subprocess.Popen or None
The process handle returned by :meth:`runner` if executed,
or ``None`` if the job is written to a script instead.
Notes
-----
- Requires a properly prepared input file (`zg.in`) according to the
ZG.x module specifications.
- This runner does not currently support parallelization flags,
but could be extended to include them if needed.
Examples
--------
>>> run = PyRun(procs=4, env='mpirun', code='/path/to/zg')
>>> run.run_zg(folder='./zg', name='graphene_zg')
# Executes equivalent to:
# mpirun -np 4 /path/to/zg/ZG.x -in graphene_zg.in > graphene_zg.out
"""
self.dir = folder
self.filein = name + '.in'
self.fileout = name + '.out'
self.util = 'ZG.x -in '
self.runner()
[docs]
@decorated_progress
def run_q2r(self, folder='./ph', name='q2r'):
"""
Run a q2r calculation using Quantum ESPRESSO's q2r.x module.
This method executes the q2r step, which converts
dynamical matrices (computed by `ph.x`) from reciprocal space
to real-space interatomic force constants.
The output is typically used for phonon interpolation or
subsequent electron-phonon calculations.
Parameters
----------
folder : str, optional
Directory where the q2r calculation will be executed.
Default is `'./ph'`.
name : str, optional
Base name for the input/output files.
Default is `'q2r'`, corresponding to `'q2r.in'` and `'q2r.out'`.
Behavior
--------
- Sets the working directory (:attr:`self.dir`), input file (:attr:`self.filein`),
and output file (:attr:`self.fileout`).
- Constructs the command string (:attr:`self.util`) as `'q2r.x -in '`.
- Delegates execution or script-writing to :meth:`runner`.
Returns
-------
subprocess.Popen or None
The process handle returned by :meth:`runner` if executed,
or ``None`` if the job is written to a script instead.
Notes
-----
- Requires prior phonon calculations using `ph.x` to generate
the necessary dynamical matrices.
- The generated real-space force constants are essential for
EPW or post-processing phonon calculations.
Examples
--------
>>> run = PyRun(procs=4, env='mpirun', code='/path/to/qe')
>>> run.run_q2r(folder='./ph', name='graphene_q2r')
# Executes equivalent to:
# mpirun -np 4 /path/to/qe/q2r.x -in graphene_q2r.in > graphene_q2r.out
"""
self.dir = folder
self.filein = name + '.in'
self.fileout = name + '.out'
self.util = 'q2r.x -in '
self.runner()
[docs]
@decorated_progress
def run_dvscf_q2r(self, folder='./ph', name='dvscf_q2r'):
"""
Runner for dvscf_q2r
"""
self.dir=folder
self.filein=name+'.in'
self.fileout=name+'.out'
self.util='dvscf_q2r.x -in '
self.runner()
[docs]
@decorated_progress
def run_postahc(self, folder='./ph', name='postahc'):
"""
Runner for postahc
"""
self.dir = folder
self.filein = name+'.in'
self.fileout = name+'.out'
self.util = 'postahc.x -in '
self.runner()
[docs]
@decorated_progress
def run_eps(self, folder='./eps', name='eps'):
"""
Runner for epsilon
"""
self.dir = folder
self.filein = name+'.in'
self.fileout = name+'.out'
self.util = 'epsilon_Gaus.x -in'
self.runner()
[docs]
@decorated_progress
def run_nscf_tetra(self, folder='./nscf_tetra', name='nscf'):
namein = name+'.in'
nameout = name+'.out'
nt, nk = self.set_processors(self.procs)
nt = int(nt)
nk = int(nk)
self.dir = folder
self.filein = name+'.in'
self.fileout = name+'.out'
if self.proc_set is None:
self.util = 'pw.x -nk '+str(nk)+' -nt '+str(nt)+' -in '
else:
self.util = 'pw.x '+str(self.proc_set)+' -in '
#print("running nscf with "+str(nk)+' nk and '+str(nt)+" tasks")
p1 = self.runner()
if(self.serial):
p1.wait()
[docs]
@decorated_progress
def run_bands(self, folder='./bs', name='bands'):
""" Runner for bands """
self.dir = folder
self.filein = name+'.in'
self.fileout = name+'.out'
self.util = 'bands.x -in'
#print("running post-proc bands")
self.runner()
[docs]
@decorated_progress
def run_nscf2supercond(self, folder='./nscf', name='nscf2supercond'):
""" Runner for nscf2supercond """
self.dir = folder
self.filein = name+'.in'
self.fileout = name+'.out'
self.util = '../EPW/bin/nscf2supercond.x -in'
#print("running nscf2supercond to extract eigen energies")
self.runner()
[docs]
@decorated_progress
def run_matdyn(self, folder='./ph', name='matdyn'):
"""
Run a matdyn calculation using Quantum ESPRESSO's matdyn.x module.
This method executes the matdyn step, which calculates phonon
frequencies and eigenvectors along a specified path in the
Brillouin zone, producing phonon band structures and related
properties. It is typically used after q2r to obtain interpolated
phonon bands.
Parameters
----------
folder : str, optional
Directory where the matdyn calculation will be executed.
Default is `'./ph'`.
name : str, optional
Base name for the input/output files.
Default is `'matdyn'`, corresponding to `'matdyn.in'` and `'matdyn.out'`.
Behavior
--------
- Sets the working directory (:attr:`self.dir`), input file (:attr:`self.filein`),
and output file (:attr:`self.fileout`).
- Constructs the command string (:attr:`self.util`) as `'matdyn.x -in'`.
- Calls :meth:`runner` to execute or script-write the matdyn calculation.
Returns
-------
subprocess.Popen or None
The process handle returned by :meth:`runner` if executed,
or ``None`` if the job is written to a script instead.
Notes
-----
- Requires prior q2r calculations to generate real-space force constants.
- Produces interpolated phonon band structures, which can be used
for plotting or electron-phonon calculations in EPW.
Examples
--------
>>> run = PyRun(procs=4, env='mpirun', code='/path/to/qe')
>>> run.run_matdyn(folder='./ph', name='graphene_matdyn')
# Executes equivalent to:
# mpirun -np 4 /path/to/qe/matdyn.x -in graphene_matdyn.in > graphene_matdyn.out
"""
self.dir = folder
self.filein = name + '.in'
self.fileout = name + '.out'
self.util = 'matdyn.x -in'
p1 = self.runner()
[docs]
@decorated_progress
def run_dynmat(self, folder='./ph', name='dynmat'):
"""
Run a dynmat calculation using Quantum ESPRESSO's dynmat.x module.
This method executes the dynmat step, which computes the full dynamical
matrices for the system, typically used for phonon analysis, lattice
dynamics, and further post-processing. It is usually performed after
SCF and NSCF calculations, and optionally after phonon (`ph.x`) runs.
Parameters
----------
folder : str, optional
Directory where the dynmat calculation will be executed.
Default is `'./ph'`.
name : str, optional
Base name for the input/output files.
Default is `'dynmat'`, corresponding to `'dynmat.in'` and `'dynmat.out'`.
Behavior
--------
- Sets the working directory (:attr:`self.dir`), input file (:attr:`self.filein`),
and output file (:attr:`self.fileout`).
- Constructs the command string (:attr:`self.util`) as `'dynmat.x -in'`.
- Calls :meth:`runner` to execute or script-write the dynmat calculation.
Returns
-------
subprocess.Popen or None
The process handle returned by :meth:`runner` if executed,
or ``None`` if the job is written to a script instead.
Notes
-----
- Requires a prior SCF/NSCF calculation and, if applicable, phonon inputs.
- Produces dynamical matrices used for phonon dispersion, EPW calculations,
or other lattice dynamics analyses.
Examples
--------
>>> run = PyRun(procs=4, env='mpirun', code='/path/to/qe')
>>> run.run_dynmat(folder='./ph', name='graphene_dynmat')
# Executes equivalent to:
# mpirun -np 4 /path/to/qe/dynmat.x -in graphene_dynmat.in > graphene_dynmat.out
"""
self.dir = folder
self.filein = name + '.in'
self.fileout = name + '.out'
self.util = 'dynmat.x -in'
p1 = self.runner()
[docs]
@decorated_progress
def run_phdos(self, folder='./ph', name='phdos'):
self.dir = folder
self.filein = name+'.in'
self.fileout = name+'.out'
self.util = 'matdyn.x -in'
#print("running phonon dos calculation")
p1 = self.runner()
[docs]
@decorated_progress
def run_dos(self, folder='./nscf', name='dos'):
"""
Run DOS calculation using QE
"""
self.dir = folder
self.filein = name+'.in'
self.fileout = name+'.out'
self.util = 'dos.x -in'
#print("running electron DOS calculation")
p1 = self.runner()
[docs]
@decorated_progress
def run_pdos(self, folder='./nscf', name='pdos'):
"""
Run PDOS calculation
"""
self.dir = folder
self.filein = name+'.in'
self.fileout = name+'.out'
self.util = 'projwfc.x -in'
#print("running electron PDOS calculation")
p1 = self.runner()
[docs]
@decorated_progress
def run_fbw(self, folder='./fbw', name='fbw'):
nk = int(self.procs)
self.dir = folder
self.filein = name+'.in'
self.fileout = name+'.out'
self.util = 'epw.x -nk '+str(nk)+' -in '
#print("running FBW calculation")
p1 = self.runner()
[docs]
@decorated_progress
def run_fbw_mu(self, folder='./fbw_mu', name='fbw_mu'):
nk = int(self.procs)
self.dir = folder
self.filein = name+'.in'
self.fileout = name+'.out'
self.util = 'epw.x -nk '+str(nk)+' -in '
#print("running FBW+mu calculation")
p1 = self.runner()
[docs]
@decorated_progress
def run_epw_outerbands(self, folder='./epw_outerbands', name='epw_outerbands'):
nk = int(self.procs)
self.dir = folder
self.filein = name+'.in'
self.fileout = name+'.out'
self.util = 'epw.x -nk '+str(nk)+' -in '
#print("running epw calculation with Coulomb correction")
p1 = self.runner()
[docs]
@decorated_progress
def run_nesting(self, folder='./nesting', name='nesting'):
nk = int(self.procs)
self.dir = folder
self.filein = name+'.in'
self.fileout = name+'.out'
self.util = 'epw.x -nk '+str(nk)+' -in '
#print("running nesting function calculation")
p1 = self.runner()
[docs]
@decorated_progress
def run_phselfen(self, folder='./phselfen', name='phselfen'):
nk = int(self.procs)
self.dir = folder
self.filein = name+'.in'
self.fileout = name+'.out'
self.util = 'epw.x -nk '+str(nk)+' -in '
p1 = self.runner()
[docs]
@decorated_progress
def run_wannier(self, folder='./wannier', name='win'):
"""
Runner for Wannier
"""
self.dir = folder
name = name.replace('\'','')
self.filein = name+'.win'
self.fileout = name+'.wout'
self.util = 'wannier90.x -pp '
serial = self.serial
self.serial = True
p1 = self.runner()
self.filein = name+'.pw2wan'
self.fileout = name+'.pw2wan.out'
self.util = 'pw2wannier90.x -pd .true. -in'
p1 = self.runner(changedir = False)
self.filein = name
self.util = 'wannier90.x'
p1 = self.runner(changedir = False)
#self.serial=serial
#self.runner()
[docs]
def run_pw2bgw(self, folder=' ', name=None):
"""
Runner for pw2BGW
"""
self.dir = folder
if(name !=None):
pw2bgw = name
else:
pw2bgw = self.prefix
self.filein = pw2bgw+'.pw2bgw'
self.fileout = pw2bgw+'.out'
self.util = 'pw2bgw.x -pd .true. -in'
self.runner()
[docs]
@decorated_progress
def run_epsilon(self, folder=' ', name=None, flavor='cplx'):
"""
Run the epsilon calculation using the BGW (BerkeleyGW) epsilon module.
This method executes the dielectric function calculation (`epsilon.x`) in
BerkeleyGW. It computes the screened Coulomb interaction, which is essential
for many-body perturbation theory calculations such as GW and excitonic properties.
Parameters
----------
folder : str, optional
Directory where the epsilon calculation will be executed.
Default is a blank string `' '`, which indicates the current directory.
name : str, optional
Base name for the input/output files. If ``None``, defaults to `'epsilon'`.
flavor : str, optional
Flavor of the executable, typically `'cplx'` for complex or `'rpa'` for
real calculations. Default is `'cplx'`.
Behavior
--------
- Sets the working directory (:attr:`self.dir`).
- Determines the input (:attr:`self.filein`) and output (:attr:`self.fileout`)
filenames based on `name`.
- Constructs the execution command (:attr:`self.util`) for `epsilon.flavor.x`.
- Calls :meth:`runner` to execute or script-write the epsilon calculation.
Returns
-------
subprocess.Popen or None
The process handle returned by :meth:`runner` if executed,
or ``None`` if the job is written to a script instead.
Notes
-----
- Requires prior GW-related input preparation (e.g., wavefunctions and screening files).
- This runner supports different flavors of the executable (complex or real).
- Typically used as the first step in a BerkeleyGW GW workflow.
Examples
--------
>>> run = PyRun(procs=8, env='mpirun', code='/path/to/bgw')
>>> run.run_epsilon(folder='./epsilon', name='epsilon.inp', flavor='cplx')
# Executes equivalent to:
# mpirun -np 8 /path/to/bgw/epsilon.cplx.x -in epsilon.inp
"""
self.dir = folder
if name is not None:
eps_file = name
else:
eps_file = 'epsilon'
self.filein = eps_file + '.inp'
self.fileout = eps_file + '.out'
self.util = 'epsilon.' + str(flavor) + '.x -in'
self.runner()
[docs]
@decorated_progress
def run_sigma(self, folder=' ', name=None, flavor='cplx'):
"""
Run the sigma calculation using the BGW (BerkeleyGW) sigma module.
This method executes the self-energy calculation (`sigma.x`) in BerkeleyGW.
It computes the electron self-energy based on a prior dielectric screening
calculation (epsilon) and wavefunctions, which is required for GW quasiparticle
energy corrections.
Parameters
----------
folder : str, optional
Directory where the sigma calculation will be executed.
Default is a blank string `' '`, indicating the current directory.
name : str, optional
Base name for the input/output files. If ``None``, defaults to `'sigma'`.
flavor : str, optional
Flavor of the executable, typically `'cplx'` for complex or `'rpa'` for
real calculations. Default is `'cplx'`.
Behavior
--------
- Sets the working directory (:attr:`self.dir`).
- Determines the input (:attr:`self.filein`) and output (:attr:`self.fileout`)
filenames based on `name`.
- Constructs the execution command (:attr:`self.util`) for `sigma.flavor.x`.
- Calls :meth:`runner` to execute or script-write the sigma calculation.
Returns
-------
subprocess.Popen or None
The process handle returned by :meth:`runner` if executed,
or ``None`` if the job is written to a script instead.
Notes
-----
- Requires prior epsilon and wavefunction calculations.
- This calculation provides the GW quasiparticle self-energy.
- Supports different flavors of the executable (complex or real).
Examples
--------
>>> run = PyRun(procs=8, env='mpirun', code='/path/to/bgw')
>>> run.run_sigma(folder='./sigma', name='sigma.inp', flavor='cplx')
# Executes equivalent to:
# mpirun -np 8 /path/to/bgw/sigma.cplx.x -in sigma.inp
"""
self.dir = folder
if name is not None:
sigma_file = name
else:
sigma_file = 'sigma'
self.filein = sigma_file + '.inp'
self.fileout = sigma_file + '.out'
self.util = 'sigma.' + str(flavor) + '.x -in'
self.runner()
[docs]
@decorated_progress
def run_sig2wan(self, folder=' ', name=None):
"""
Run the sigma-to-Wannier conversion using BGW's sig2wan module.
This method executes the `sig2wan.x` program in BerkeleyGW, which converts
the computed electron self-energy (sigma) into a format compatible with
Wannier interpolation. This step is typically used to interface GW corrections
with Wannier90 or EPW workflows.
Parameters
----------
folder : str, optional
Directory where the sig2wan calculation will be executed.
Default is `' '`, indicating the current directory.
name : str, optional
Base name for the input/output files. If ``None``, defaults to `'sig2wan'`.
Behavior
--------
- Sets the working directory (:attr:`self.dir`).
- Determines the input (:attr:`self.filein`) and output (:attr:`self.fileout`)
filenames based on `name`.
- Constructs the execution command (:attr:`self.util`) for `sig2wan.x`.
- Calls :meth:`runner` to execute or script-write the sig2wan calculation.
Returns
-------
subprocess.Popen or None
The process handle returned by :meth:`runner` if executed,
or ``None`` if the job is written to a script instead.
Notes
-----
- Requires prior sigma calculations from `sigma.x`.
- The output can be used in Wannier90 or EPW workflows for band structure
interpolation including GW corrections.
Examples
--------
>>> run = PyRun(procs=8, env='mpirun', code='/path/to/bgw')
>>> run.run_sig2wan(folder='./sig2wan', name='graphene_sig2wan')
# Executes equivalent to:
# mpirun -np 8 /path/to/bgw/sig2wan.x -in graphene_sig2wan.in > graphene_sig2wan.out
"""
self.dir = folder
if name is not None:
sig2wan_file = name
else:
sig2wan_file = 'sig2wan'
self.filein = sig2wan_file + '.inp'
self.fileout = sig2wan_file + '.out'
self.util = 'sig2wan.x -in'
self.runner()
[docs]
@decorated_progress
def run_kernel(self, folder=' ', name=None, flavor='cplx'):
"""
Run the kernel calculation using the BGW (BerkeleyGW) kernel module.
This method executes the `kernel.x` program in BerkeleyGW, which computes
the electron-hole interaction kernel required for solving the Bethe-Salpeter
Equation (BSE). The kernel is essential for excitonic and optical properties
calculations following GW and epsilon computations.
Parameters
----------
folder : str, optional
Directory where the kernel calculation will be executed.
Default is `' '`, indicating the current directory.
name : str, optional
Base name for the input/output files. If ``None``, defaults to `'kernel'`.
flavor : str, optional
Flavor of the executable, typically `'cplx'` for complex or `'rpa'` for
real calculations. Default is `'cplx'`.
Behavior
--------
- Sets the working directory (:attr:`self.dir`).
- Determines the input (:attr:`self.filein`) and output (:attr:`self.fileout`)
filenames based on `name`.
- Constructs the execution command (:attr:`self.util`) for `kernel.flavor.x`.
- Calls :meth:`runner` to execute or script-write the kernel calculation.
Returns
-------
subprocess.Popen or None
The process handle returned by :meth:`runner` if executed,
or ``None`` if the job is written to a script instead.
Notes
-----
- Requires prior epsilon and sigma calculations.
- Produces the electron-hole kernel necessary for BSE excitonic calculations.
- Supports different flavors of the executable (complex or real).
Examples
--------
>>> run = PyRun(procs=8, env='mpirun', code='/path/to/bgw')
>>> run.run_kernel(folder='./kernel', name='graphene_kernel', flavor='cplx')
# Executes equivalent to:
# mpirun -np 8 /path/to/bgw/kernel.cplx.x -in graphene_kernel.in > graphene_kernel.out
"""
self.dir = folder
if name is not None:
kernel_file = name
else:
kernel_file = 'kernel'
self.filein = kernel_file + '.inp'
self.fileout = kernel_file + '.out'
self.util = 'kernel.' + str(flavor) + '.x -in'
self.runner()
[docs]
@decorated_progress
def run_absorption(self, folder=' ', name=None, flavor='cplx'):
"""
Run the absorption calculation using the BGW (BerkeleyGW) absorption module.
This method executes the `absorption.x` program in BerkeleyGW, which computes
the optical absorption spectrum of the system. It uses the previously computed
electron-hole interaction kernel (from `kernel.x`) and quasiparticle energies
(from `sigma.x`) to calculate excitonic effects and optical properties.
Parameters
----------
folder : str, optional
Directory where the absorption calculation will be executed.
Default is `' '`, indicating the current directory.
name : str, optional
Base name for the input/output files. If ``None``, defaults to `'absorption'`.
flavor : str, optional
Flavor of the executable, typically `'cplx'` for complex or `'rpa'` for
real calculations. Default is `'cplx'`.
Behavior
--------
- Sets the working directory (:attr:`self.dir`).
- Determines the input (:attr:`self.filein`) and output (:attr:`self.fileout`)
filenames based on `name`.
- Constructs the execution command (:attr:`self.util`) for `absorption.flavor.x`.
- Calls :meth:`runner` to execute or script-write the absorption calculation.
Returns
-------
subprocess.Popen or None
The process handle returned by :meth:`runner` if executed,
or ``None`` if the job is written to a script instead.
Notes
-----
- Requires prior kernel and sigma calculations.
- Produces optical absorption spectra, including excitonic effects.
- Supports different flavors of the executable (complex or real).
Examples
--------
>>> run = PyRun(procs=8, env='mpirun', code='/path/to/bgw')
>>> run.run_absorption(folder='./absorption', name='graphene_abs', flavor='cplx')
# Executes equivalent to:
# mpirun -np 8 /path/to/bgw/absorption.cplx.x -in graphene_abs.in > graphene_abs.out
"""
self.dir = folder
if name is not None:
absorption_file = name
else:
absorption_file = 'absorption'
self.filein = absorption_file + '.inp'
self.fileout = absorption_file + '.out'
self.util = 'absorption.' + str(flavor) + '.x -in'
self.runner()
# @decorated_progress
[docs]
def runner(self, changedir=True):
"""
Main execution routine for running external simulation executables.
This method constructs and executes the appropriate command-line
instruction to launch a target code (e.g., Quantum ESPRESSO, EPW, or BGW)
based on the user-specified environment (`env`), number of processors,
and runtime configuration. It supports parallel environments such as
`mpirun`, `srun`, and `ibrun`, as well as serial execution.
If a run script writer is active (`self.writer.script_params['write_script'] == True`),
the method writes the command to a script instead of executing it.
Parameters
----------
changedir : bool, optional
Whether to change to the working directory (`self.dir`) before execution.
Default is ``True``.
Behavior
--------
- If `self.env` is provided, constructs a parallel execution command based on
the environment type (`mpirun`, `srun`, `ibrun`, etc.).
- If no `env` is provided, assumes a direct serial run.
- Redirects standard output to `self.fileout`.
- When `self.writer.script_params['write_script']` is ``True``,
writes the run command to a job script instead of executing.
- When `self.serial` is ``True``, the process waits for completion (`p1.wait()`).
Returns
-------
subprocess.Popen
A Popen process object representing the executed command, unless the
command is only written to a script (in which case no process is launched).
Raises
------
FileNotFoundError
If `changedir=True` but `self.dir` does not exist.
AttributeError
If required attributes such as `self.code`, `self.util`, or `self.filein`
are missing.
Examples
--------
>>> run = PyRun(procs=8, env='mpirun', code='/path/to/qe')
>>> run.util = 'pw.x'
>>> run.filein = 'scf.in'
>>> run.fileout = 'scf.out'
>>> process = run.runner()
>>> process.wait()
Notes
-----
- The constructed command typically looks like:
.. code-block:: bash
mpirun -np 8 /path/to/qe/pw.x scf.in > scf.out
- The method can be adapted for other HPC launchers by extending the
`env` conditionals.
- For debugging, verbosity levels control printed output:
* `verbosity > 1` prints the command before execution
* `verbosity > 2` appends real-time log following (`tail -f`)
"""
if (changedir == True):
try:
os.chdir(self.dir)
except FileNotFoundError:
pass
cmd = None
if self.env:
if (self.env == 'mpirun'):
cmd = f'{self.env} -np {self.procs} {self.code}/{self.util} {self.filein} > {self.fileout}'
elif (self.env == 'srun'):
cmd = f'{self.env} -n {self.procs} {self.code}/{self.util} {self.filein} > {self.fileout}'
elif (self.env == 'ibrun'):
cmd = f'{self.env} {self.code}/{self.util} {self.filein} > {self.fileout}'
else:
cmd = f'{self.env} {self.procs} {self.code}/{self.util} {self.filein} > {self.fileout}'
else:
cmd = f'{self.code}/{self.util} {self.filein} > {self.fileout}'
"""
Runner will not execute run command if type(self.script==dict)
Checking if self.script==dict
"""
if (self.writer.script_params['write_script'] == True):
print("EPWpy_run.py runner(): Script is writing")
write_cmd = cmd.replace(self.code, '$QE')
if (changedir == True):
self.writer.write_execute(f'cd {self.dir}')
self.writer.write_execute(write_cmd)
else:
# print("EPWpy_run.py runner(): Executing instead")
if (self.verbosity > 1):
print('running: ',cmd)
#if (self.verbosity > 2):
# cmd +=f'{cmd} | tail -f {self.fileout}'
p1=sp.Popen(cmd, shell=True)
if (self.env == 'serial'):
p1=sp.Popen('/'+self.code+'/'+self.util+' '+self.filein+' > '+self.fileout,
stdout=self.fileout,
stderr=sp.STDOUT,
universal_newlines=True,
shell=True)
if (self.serial):
if self.verbosity > 2:
for line in p1.stdout:
print(line, end='') # live output
p1.wait()
return(p1)
[docs]
def set_processors(self,procs):
if (np.sqrt(procs)%2 == 0):
return(np.sqrt(procs),np.sqrt(procs))
else:
return(1,procs)