from __future__ import annotations
__author__= "Sabyasachi Tiwari, Shashi Mishra"
__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.set_files import*
from EPWpy.utilities.epw_pp import *
from EPWpy.utilities.printing import *
from EPWpy.utilities.write_job import *
[docs]
class PyPrepare(SetDir):
"""
The :class:`PyPrepare` class handles setup tasks required before running
EPW or related first-principles simulations. It inherits from :class:`SetDir`
and is mainly responsible for preparing and transferring necessary files
(e.g., pseudopotentials, input decks) to the working directory.
This class is typically used internally by :class:`PyRun`, but it can also
be invoked independently for pre-processing or file management tasks.
**Attributes**
---------------
prefix : str
Name prefix used for input/output files and working directories.
pseudo : str
Path to the pseudopotential directory or file.
files : list of str
List of files to be transferred or linked for the run.
folder_name : str
Name of the folder where files will be prepared (default: ``'dummy'``).
dolink : bool
Whether to create symbolic links instead of copying files
(default: ``False``).
"""
def __init__(self, prefix, pseudo, files=None):
"""
Initialize a :class:`PyPrepare` object.
Parameters
-----------
prefix : str
Base prefix for the simulation (e.g., system name).
pseudo : str
Path to the pseudopotential(s) used for the calculation.
Only needed for VASP calculations
files : list of str, optional
List of additional input or data files to prepare
(default: an empty list).
Notes
-----
- Sets default folder name to ``'dummy'``.
- Disables symbolic linking (`dolink = False`) by default.
"""
if files is None:
files = []
self.prefix = prefix
self.pseudo = pseudo
self.files = files
self.folder_name = 'dummy'
self.dolink = False
[docs]
def decorated_save(func):
"""
Saves state
"""
def inner(self,**kwargs):
fold = kwargs['name']
self.save = Save_state(' ',' ')
dict1 = {f'{util}':fold}
self._save_json('state',dict1)#self.pw_system)
func(self,**kwargs)
return inner
# @decorated_save
[docs]
def prepare_custom(self, name: str = 'custom', filename: str = 'custom.in'):
"""
Prepare a custom calculation directory and transfer input files.
This method sets up a folder for a custom calculation and copies
the necessary input files to that directory. It can be used for any
calculation type where the inputs are not standard SCF/EPW workflows.
Parameters
----------
name : str, optional
Name of the directory to create for the custom calculation (default: ``"custom"``).
filename : str, optional
Name of the input file to copy into the custom directory (default: ``"custom.in"``).
Workflow
--------
1. Creates a new directory ``name`` via ``makedir()``.
2. Transfers required files using ``run_transfer(name); these are provided through file_transfer=[]``.
3. Copies the specified input file into the new directory using ``run()``.
Notes
-----
- Useful for setting up calculations that do not follow the standard SCF/EPW workflow.
- Ensures the folder structure is ready for independent runs.
Example
-------
>>> prepare_custom(name="my_custom_calc", filename="my_input.in")
"""
self.makedir(name)
self.run_transfer(name)
self.run(f'{filename}',f'{name}')
[docs]
def prepare_scf(self, name: str = 'scf', filename: str = 'scf.in'):
"""
Prepare a self-consistent field (SCF) calculation directory.
This method creates a folder for an SCF calculation and copies the
SCF input file into that directory. It ensures the directory structure
is ready for running Quantum ESPRESSO SCF calculations.
Parameters
----------
name : str, optional
Name of the directory to create for the SCF calculation (default: ``"scf"``).
filename : str, optional
Name of the SCF input file to copy (default: ``"scf.in"``).
Workflow
--------
1. Creates the SCF directory using ``makedir()``.
2. Copies the specified input file into the SCF directory using ``run()``.
Notes
-----
- Assumes the SCF input file exists in the current working directory.
- This setup is a prerequisite for performing SCF calculations.
Example
-------
>>> prepare_scf(name="scf_run", filename="si.scf.in")
"""
self.makedir(name)
self.run(f'{filename}',f'{name}')
[docs]
def prepare_nscf(self, name: str = 'nscf', filename: str = 'nscf.in'):
"""
Prepare a non-self-consistent field (NSCF) calculation directory.
This method sets up the folder structure and transfers necessary input
files for an NSCF calculation. It uses the same workflow as `prepare_post_scf()`,
ensuring that the required SCF outputs are available for the NSCF run.
Parameters
----------
name : str, optional
Name of the directory for the NSCF calculation (default: ``"nscf"``).
filename : str, optional
Name of the NSCF input file to copy (default: ``"nscf.in"``).
Workflow
--------
1. Calls `prepare_post_scf()` to create the directory and transfer files.
2. Ensures SCF outputs and NSCF input are in place for the calculation.
Notes
-----
- Assumes that the corresponding SCF calculation has already been completed.
- This setup is required for proper NSCF or bandstructure calculations.
Example
-------
>>> prepare_nscf(name="nscf_run", filename="si.nscf.in")
"""
self.prepare_post_scf(name, filename)
[docs]
def prepare_bs(self, name: str = 'bs', filename: str = 'bs.in'):
"""
Prepare a bandstructure (BS) calculation directory.
This method sets up the folder structure and transfers necessary input
files for a bandstructure calculation. It uses the same workflow as
`prepare_post_scf()`, ensuring that required SCF outputs are available
for the BS run.
Parameters
----------
name : str, optional
Name of the directory for the bandstructure calculation (default: ``"bs"``).
filename : str, optional
Name of the bandstructure input file to copy (default: ``"bs.in"``).
Workflow
--------
1. Calls `prepare_post_scf()` to create the directory and transfer files.
2. Ensures SCF outputs and BS input are in place for the calculation.
Notes
-----
- Assumes that a corresponding SCF calculation has already been completed.
- This setup is necessary for proper bandstructure analysis.
Example
-------
>>> prepare_bs(name="bs_run", filename="si.bs.in")
"""
self.prepare_post_scf(name, filename)
[docs]
def prepare_ph(self, name: str = 'ph', filename: str = 'ph.in'):
"""
Prepare a phonon (PH) calculation directory.
This method sets up the folder structure and transfers necessary input
files for a phonon calculation. It uses the same workflow as
`prepare_post_scf()`, ensuring that required SCF outputs are available
for the phonon run.
Parameters
----------
name : str, optional
Name of the directory for the phonon calculation (default: ``"ph"``).
filename : str, optional
Name of the phonon input file to copy (default: ``"ph.in"``).
Workflow
--------
1. Calls `prepare_post_scf()` to create the directory and transfer files.
2. Ensures SCF outputs and PH input are in place for the calculation.
Notes
-----
- Assumes that a corresponding SCF calculation has already been completed.
- Necessary for running phonon calculations using Quantum ESPRESSO.
Example
-------
>>> prepare_ph(name="ph_run", filename="si.ph.in")
"""
self.prepare_post_scf(name, filename)
[docs]
def prepare_pp(self, name: str = 'pp', filename: str = 'pp.in'):
"""
Prepare a post-processing (PP) calculation directory.
Copies the PP input file into the specified directory.
Parameters
----------
name : str, optional
Target directory name (default: "pp").
filename : str, optional
PP input file name (default: "pp.in").
Example
-------
>>> prepare_pp(name="pp_run", filename="si.pp.in")
"""
self.prepare_post_scf(name, filename)
[docs]
def prepare_q2r(self, name: str = 'ph', filename: str = 'q2r.in'):
"""
Prepare a q2r calculation directory.
Copies the q2r input file into the specified folder.
Parameters
----------
name : str, optional
Target directory (default: "ph").
filename : str, optional
Input file name (default: "q2r.in").
Example
-------
>>> prepare_q2r(name="ph_run", filename="si.q2r.in")
"""
self.run(f'{filename}', f'./{name}')
[docs]
def prepare_dvscf_q2r(self, name: str = 'ph', filename: str = 'dvscf_q2r.in'):
"""
Prepare a dvscf_q2r calculation directory.
Copies the dvscf_q2r input file into the specified folder.
Parameters
----------
name : str, optional
Target directory (default: "ph").
filename : str, optional
Input file name (default: "dvscf_q2r.in").
Example
-------
>>> prepare_dvscf_q2r(name="ph_run", filename="si.dvscf_q2r.in")
"""
self.run(f'{filename}', f'./{name}')
[docs]
def prepare_postahc(self, name: str = 'ph', filename: str = 'postahc.in'):
"""
Prepare a postahc calculation directory.
Copies the postahc input file into the specified folder.
Parameters
----------
name : str, optional
Target directory (default: "ph").
filename : str, optional
Input file name (default: "postahc.in").
Example
-------
>>> prepare_postahc(name="ph_run", filename="si.postahc.in")
"""
self.run(f'{filename}', f'./{name}')
[docs]
def prepare_bands(self, name: str = 'bs', filename: str = 'bands.in'):
"""
Prepare a bandstructure (BANDS) calculation directory.
Copies the BANDS input file into the specified folder.
Parameters
----------
name : str, optional
Target directory (default: "bs").
filename : str, optional
Input file name (default: "bands.in").
Example
-------
>>> prepare_bands(name="bs_run", filename="si.bands.in")
"""
self.run(f'{filename}', f'./{name}')
[docs]
def prepare_matdyn(self, name: str = 'ph', filename: str = 'matdyn.in'):
"""
Prepare a matdyn calculation directory.
Copies the matdyn input file into the specified folder.
Parameters
----------
name : str, optional
Target directory (default: "ph").
filename : str, optional
Input file name (default: "matdyn.in").
Example
-------
>>> prepare_matdyn(name="ph_run", filename="si.matdyn.in")
"""
self.run(f'{filename}', f'./{name}')
[docs]
def prepare_dynmat(self, name: str = 'ph', filename: str = 'dynmat.in'):
"""
Prepare a dynmat calculation directory.
Copies the dynmat input file into the specified folder.
Parameters
----------
name : str, optional
Target directory (default: "ph").
filename : str, optional
Input file name (default: "dynmat.in").
Example
-------
>>> prepare_dynmat(name="ph_run", filename="si.dynmat.in")
"""
self.run(f'{filename}', f'./{name}')
[docs]
def prepare_nscf2supercond(self, name = 'nscf', filename = 'nscf2supercond.in'):
"""
Prepares an nscf2supercond calculation
"""
self.run_transfer(name)
self.run(f'{filename}',f'./{name}')
[docs]
def prepare_phdos(self, name = 'ph', filename = 'phdos.in'):
"""
Prepares a phdos calculation
"""
self.run_transfer(name)
self.run(f'{filename}',f'./{name}')
[docs]
def prepare_nscf_tetra(self, name = 'nscf_tetra', filename = 'nscf.in'):
"""
Prepares a nscf tetragonal calculation
"""
self.run_transfer(name)
self.prepare_post_scf(name, filename)
[docs]
def prepare_dos(self, name = 'nscf', filename = 'dos.in'):
"""
Prepares a dos calculation
"""
self.run_transfer(name)
self.run(f'{filename}',f'./{name}')
[docs]
def prepare_pdos(self, name = 'nscf', filename = 'pdos.in'):
"""
Prepares a pdos calculation
"""
self.run_transfer(name)
self.run(f'{filename}', f'./{name}')
[docs]
def prepare_fbw(self, name = 'fbw', filename = 'fbw.in'):
"""
Prepares FBW calculation
"""
self.prepare_post_epw(name, filename)
[docs]
def prepare_outerbands(self, name = 'epw_outerbands', filename = 'epw_outerbands.in'):
"""
Prepares outerbands calculation
"""
self.prepare_post_epw(name, filename)
[docs]
def prepare_fbw_mu(self, name = 'fbw_mu', filename = 'fbw_mu.in'):
"""
Prepares fbw mu calculation
"""
self.prepare_post_epw(name, filename)
[docs]
def prepare_nesting(self, name = 'nesting', filename = 'nesting.in'):
"""
Prepares nesting calculation
"""
self.prepare_post_epw2(name, filename)
[docs]
def prepare_phselfen(self, name = 'phselfen', filename = 'phselfen.in'):
"""
Prepares phselfen calculation
"""
self.prepare_post_epw2(name, filename)
[docs]
def prepare_zg(self, name: str = 'zg', filename: str = 'zg.in'):
"""
Prepare a ZG calculation directory.
This method sets up the folder and transfers all necessary input files
for a ZG calculation, including SCF outputs, phonon dynamical matrices,
force constants, and any additional files required.
Parameters
----------
name : str, optional
Target directory for the ZG calculation (default: "zg").
filename : str, optional
ZG input file name (default: "zg.in").
Workflow
--------
1. Creates the directory if it doesn't exist.
2. Transfers files from previous SCF and PH calculations.
3. Copies the specified ZG input file.
Example
-------
>>> prepare_zg(name="zg_run", filename="si.zg.in")
"""
self.makedir(name)
for file in self.files:
self.run(file, f'./{name}')
self.run(f'{filename}', f'./{name}')
self.run(f'./{self.scf_fold}/' + str(self.prefix) + '.save', './zg')
self.run(f'./{self.ph_fold}/_ph0', f'./{name}')
self.run(f'./{self.ph_fold}/*dyn*', f'./{name}')
self.run(f'./{self.ph_fold}/*.fc', f'./{name}')
self.run(f'./{self.scf_fold}/scf.in', f'./{name}')
[docs]
def prepare_eps(self, name: str = 'eps', filename: str = 'eps.in'):
"""
Prepare an epsilon.x calculation directory for QE.
Transfers the NSCF outputs and wavefunction files required for
dielectric function calculations using epsilon.x.
Parameters
----------
name : str, optional
Target directory for the epsilon calculation (default: "eps").
filename : str, optional
Input file for epsilon.x (default: "eps.in").
Workflow
--------
1. Creates the target directory.
2. Transfers NSCF save folder and wavefunction files.
3. Copies the epsilon input file.
Example
-------
>>> prepare_eps(name="eps_run", filename="si.eps.in")
"""
self.makedir(name)
self.run(f'{filename}', f'./{name}')
self.run(f'./{self.nscf_fold}/' + str(self.prefix) + '.save', f'./{name}')
self.run(f'./{self.nscf_fold}/*wfc*', f'./{name}')
[docs]
def prepare_epw1(self, name: str = 'epw', filename: str = 'epw1.in'):
"""
Prepare EPW coarse-grid (epw1) calculation directory.
Sets up the folder for the first EPW step using coarse k/q grids.
Transfers necessary NSCF and phonon outputs, copies the EPW input file,
and optionally performs post-processing.
Parameters
----------
name : str, optional
Target directory for the EPW calculation (default: "epw").
filename : str, optional
Input file for EPW step 1 (default: "epw1.in").
Workflow
--------
1. Creates the directory if it doesn’t exist.
2. Transfers NSCF `.save` folder and phonon `_ph0` directory, or links them if `dolink=True`.
3. Copies dynamical matrices from the PH folder.
4. Copies the EPW input file.
5. Transfers additional files and runs post-processing (`pp()`).
Example
-------
>>> prepare_epw1(name="epw_run", filename="si.epw1.in")
"""
self.makedir(name)
cwd = os.getcwd()
if not self.dolink:
self.run(f'./{self.nscf_fold}/' + str(self.prefix) + '.save', f'./{name}')
self.run(f'./{self.ph_fold}/_ph0', f'./{name}')
else:
self.link(f'{cwd}/{self.nscf_fold}/{self.prefix}.save', f'./{name}')
self.link(f'{cwd}/{self.ph_fold}/_ph0', f'{cwd}/{name}')
self.run(f'./{self.ph_fold}/' + str(self.prefix) + '.dyn*', f'./{name}')
self.run(f'{filename}', f'./{name}')
self.run_transfer(name)
self.changedir(name)
self.pp()
self.changedir('../')
[docs]
def prepare_epw2(self, name: str = 'epw', filename: str = 'epw2.in'):
"""
Prepare EPW second-step (interpolated) calculation directory.
Sets up the EPW coarse-to-fine interpolation step by transferring
necessary files from the previous EPW calculation and copying the
step 2 input file.
Parameters
----------
name : str, optional
Target directory for the EPW calculation (default: "epw").
filename : str, optional
Input file for EPW step 2 (default: "epw2.in").
Workflow
--------
1. Transfers files from the previous EPW calculation.
2. Copies the EPW step 2 input file into the target directory.
Example
-------
>>> prepare_epw2(name="epw_run", filename="si.epw2.in")
"""
self.run_transfer(name)
self.run(f'{filename}', f'./{name}')
[docs]
def prepare_epw3(self, name: str = 'epw', filename: str = 'epw3.in'):
"""
Prepare EPW third-step calculation directory.
Sets up the final EPW calculation step, typically for computing
properties on dense k/q grids. Transfers files from the previous
EPW step and copies the step 3 input file.
Parameters
----------
name : str, optional
Target directory for the EPW calculation (default: "epw").
filename : str, optional
Input file for EPW step 3 (default: "epw3.in").
Workflow
--------
1. Transfers files from the previous EPW calculation.
2. Copies the EPW step 3 input file into the target directory.
Example
-------
>>> prepare_epw3(name="epw_run", filename="si.epw3.in")
"""
self.run_transfer(name)
self.run(f'{filename}', f'./{name}')
[docs]
def prepare_wannier(self, name = 'wannier', filename = ''):
"""
Prepares Wannier files
"""
self.makedir(name)
self.run_transfer(name)
self.run(f'./{self.nscf_fold}/'+str(self.prefix)+'.save', f'./{name}')
self.run(str(self.prefix)+'.win', f'./{name}')
self.run(str(self.prefix)+'.pw2wan', f'./{name}')
[docs]
def prepare_dummy(self):
""" Prepares a dummy run on any type """
self.makedir(self.folder_name)
self.run_transfer(self.outfile)
self.run(self.infile, self.outfile)
[docs]
def prepare_post_scf(self, name, filename):
"""
Prepare the post-SCF environment for EPW calculations.
This method sets up the necessary folder structure and transfers
required files from a completed SCF calculation to a new working
directory for subsequent EPW workflows.
Parameters
----------
name : str
Name of the target directory where post-SCF files will be prepared.
filename : str
Name of the input file or folder to be copied for the particular calculation.
Workflow
--------
1. Creates a new directory ``name`` via ``makedir()``.
2. Transfers essential files using ``run_transfer(name)``.
3. Copies the specified SCF output or folder to the new directory using ``run()``.
4. Ensures the SCF save folder (``{prefix}.save``) is available in the post-SCF directory.
Notes
-----
- Assumes that a standard SCF calculation has already been completed.
- This step is necessary to prepare the input files and folder structure
for EPW or further post-processing calculations.
Example
-------
>>> prepare_post_scf(name="epw_workdir", filename="si.scf.out")
"""
self.makedir(name)
self.run_transfer(name)
self.run(f'{filename}', f'./{name}')
self.run(f'./{self.scf_fold}/'+str(self.prefix)+'.save', f'./{name}')
[docs]
def prepare_post_epw(self, name, filename):
"""
Prepares a post epw calculation outside epw folder
"""
self.makedir(f'{name}')
self.link(f'../{self.epw_fold}/{self.prefix}.ephmat', f'{name}')
self.run(f'./{self.epw_fold}/*.fmt', f'{name}')
self.run(f'./{self.epw_fold}/{self.prefix}.a2f', f'{name}')
self.run(f'./{self.epw_fold}/{self.prefix}.dos', f'{name}')
self.run(f'{filename}', f'{name}')
[docs]
def prepare_post_epw2(self, name, filename):
"""
Prepares a post epw calculation outside epw folder
"""
self.makedir(f'{name}')
self.run(f'../{self.epw_fold}/{self.prefix}.epmatwp',f'{name}')
self.run(f'./{self.epw_fold}/*.fmt', f'{name}')
self.run(f'./{self.epw_fold}/{self.prefix}.ukk', f'{name}')
self.run(f'./{self.ph_fold}/{self.prefix}._band.kpt', f'{name}')
self.run(f'{filename}', f'{name}')
[docs]
def prepare_abinit(self, name='abinit', filename = 'abi.abi'):
"""
Prepares an Abinit calculation
"""
self.makedir(f'{name}')
self.run(f'{filename}',f'./{name}')
[docs]
def prepare_vasp(self, name='vasp', filename = 'INCAR', structure = 'POSCAR'):
"""
Prepares an Abinit calculation
"""
self.makedir(f'{name}')
self.run(f'{filename}', f'./{name}')
self.run(f'KPOINTS', f'./{name}')
self.run(f'{self.pseudo}', f'./{name}/POTCAR')
self.run(f'{structure}', f'./{name}/POSCAR')
[docs]
def prepare_vasp_nscf(self, name='bands', filename = 'INCAR',
name_vasp = 'vasp', structure ='POSCAR'):
"""
Prepares an Abinit calculation
"""
fold = f'{name_vasp}/{name}'
self.makedir(f'{fold}')
self.run(f'{filename}', f'./{fold}')
self.run(f'KPOINTS', f'./{fold}')
self.run(f'{name_vasp}/WAVECAR', f'./{fold}')
self.run(f'{name_vasp}/CHGCAR', f'./{fold}')
self.run(f'{name_vasp}/POTCAR', f'./{fold}')
self.run(f'{structure}', f'./{fold}/POSCAR')
self.run_transfer(f'{fold}')
[docs]
def run_transfer(self, name: str):
"""
Transfer a list of files into a specified directory.
Iterates over the `self.files` list and copies each non-None file
into the target directory using the class's `run` method.
Parameters
----------
name : str
Target directory where files should be copied.
Example
-------
>>> run_transfer("epw_run")
"""
for file in self.files:
if file is not None:
self.run(file, f'./{name}')
[docs]
def link(self, infile: str, outfile: str):
"""
Create a symbolic link from `infile` to `outfile`.
This method either writes the `ln -sf` command to a script if
script-writing mode is enabled, or executes it immediately in
the shell.
Parameters
----------
infile : str
Path to the source file.
outfile : str
Path where the symbolic link will be created.
Example
-------
>>> link("path/to/source.save", "./epw_run/source.save")
"""
cmd = f'ln -sf {infile} {outfile}'
if self.writer.script_params['write_script']:
self.writer.write_execute(cmd)
else:
sp.Popen(cmd, shell=True).wait()
[docs]
def run(self, infile, outfile):
"""
Execute the main preparation step by copying or linking files.
This method handles the transfer of input files required for a given
EPW or Quantum ESPRESSO calculation. Depending on whether symbolic
linking is enabled (:attr:`dolink`), the method either copies the files
or creates symbolic links.
If script writing is enabled (i.e.,
``self.writer.script_params['write_script'] == True``), the
corresponding shell command is written to the run script instead of
being executed immediately.
**Parameters**
----------------
infile : str
Path to the input file or directory to be copied or linked.
outfile : str
Destination path or filename for the prepared file.
**Behavior**
-------------
- If :attr:`dolink` is ``False`` → performs a deep copy using ``cp -r``.
- If :attr:`dolink` is ``True`` → creates a symbolic link in the
current working directory.
- If ``self.writer.script_params['write_script']`` is enabled, the
copy/link command is written to the script instead of being executed.
**Examples**
-------------
>>> prep = PyPrepare(prefix='si', pseudo='Si.pz-vbc.UPF')
>>> prep.run('scf.in', 'workdir/scf.in')
This copies ``scf.in`` into the working directory.
"""
if (self.dolink == False):
cmd = 'cp -r ' + str(infile) + ' ' + str(outfile)
if (self.writer.script_params['write_script'] == True):
self.writer.write_execute(cmd)
else:
sp.Popen(cmd, shell=True).wait()
else:
cwd = os.getcwd()
self.link(f'{cwd}/{infile}', f'{cwd}/{outfile}')
[docs]
def pp(self):
try:
prefix=self.prefix.replace('\'','')
if (self.writer.script_params['write_script'] == True):
self.writer.write_pp(prefix)
else:
run_pp(prefix)
except FileNotFoundError:
prefix='\''+str(prefix)+'\''
run_pp(prefix)