PAOFLOW.gen.paoflow_driver#
Interactive generator for PAOFLOW main.py driver scripts.
Given the output of a Quantum ESPRESSO run (a <prefix>.save directory) and
the pseudopotential used for it, this CLI asks a few questions and writes a
minimal, clearly-commented main.py that runs PAOFLOW.
Two workflows are supported:
ACBN0 / eACBN0 self-consistent Hubbard U (and intersite V) using the standard basis for the PAO projections.
Full property run from the QE output, where the user picks the properties to compute from a menu. Standard properties use the standard basis; optical properties (dielectric tensor) need the extended basis and the non-local velocity correction, so they are emitted as a separate PAOFLOW run in the same script.
Two further workflows generate multi-phase driver scripts:
Harmonic phonons (
main.phonon.py) – finite-displacement phonons via phonopy (generate supercells -> run pw.x -> analyse forces).Electron-phonon (
main.elphon.py) – the pseudo-atomic-orbital (Agapito & Bernardi) interpolation of QE’s DFPT coupling intoalpha^2F/lambda/Tc(write the ph.x phonon + AHC inputs -> run QE -> analyse).
The generated script is static and heavily commented so it is easy to tweak afterwards.
Attributes#
Functions#
|
Ask for a free-text value with an optional default. |
|
Ask a yes/no question. |
|
Ask for an integer value with a default. |
|
Ask for a floating-point value with a default. |
|
Ask the user to pick one item from choices (list of strings). |
|
Parse a laser-wavelength specification into a list of floats (nm). |
|
|
|
|
|
|
|
Read the suggested eACBN0 intersite-V cutoff from a QE input header. |
Show the property menu and return the ordered set of chosen keys. |
|
|
Assemble a full property-run main.py from the collected config. |
|
Assemble an ACBN0 / eACBN0 main.py from the collected config. |
|
Assemble a main.phonon.py harmonic-phonon workflow from the config. |
|
Assemble a main.elphon.py electron-phonon (PAO route) workflow from the config. |
Assemble a plot.elphon.py for the Eliashberg alpha^2F / lambda output. |
|
|
Assemble a plot.py that mimics the property selection in cfg. |
Assemble a plot.acbn0.py that overlays the ACBN0 / eACBN0 band structures. |
|
Assemble a plot.phonon.py that plots the dispersion, DOS and thermals. |
|
|
Assemble a main.raman.py non-resonant (Placzek) Raman workflow. |
Assemble a plot.raman.py that overlays the broadened Raman spectra. |
|
|
Prompt for configuration shared by both workflows. |
|
Prompt for the full property-run configuration. |
|
Prompt for the harmonic-phonon (finite-displacement) configuration. |
|
Prompt for the electron-phonon (PAO route) configuration. |
|
Prompt for the ACBN0 / eACBN0 configuration. |
|
Module Contents#
- PAOFLOW.gen.paoflow_driver.ask(prompt, default=None)[source]#
Ask for a free-text value with an optional default.
- PAOFLOW.gen.paoflow_driver.ask_int(prompt, default)[source]#
Ask for an integer value with a default.
- PAOFLOW.gen.paoflow_driver.ask_float(prompt, default)[source]#
Ask for a floating-point value with a default.
- PAOFLOW.gen.paoflow_driver.ask_choice(prompt, choices, default)[source]#
Ask the user to pick one item from choices (list of strings).
- PAOFLOW.gen.paoflow_driver.parse_laser_list(spec)[source]#
Parse a laser-wavelength specification into a list of floats (nm).
Accepts a comma- or space-separated list (
'488, 514.5, 532') or a restricted Python expression that evaluates to an iterable of numbers, e.g.'[n for n in range(450, 650, 5)]'. The expression is evaluated with no builtins and onlyrangeexposed, so it cannot import modules or call arbitrary functions.
- PAOFLOW.gen.paoflow_driver.detect_v_cutoff(workdir)[source]#
Read the suggested eACBN0 intersite-V cutoff from a QE input header.
paoflow-gen-qewrites! Suggested eACBN0 intersite V cutoff: <x> Angstrominto the header of the<compound>.scf.init generates. Returns that cutoff (in Angstrom) from the first matching input file, orNoneif no input file or comment is found.
- PAOFLOW.gen.paoflow_driver.HEADER = Multiline-String[source]#
Show Value
"""#!/usr/bin/env python3 """PAOFLOW driver script (generated by paoflow_gen.py). Edit the constants below and the property calls in the run function(s) to customize the calculation. Run with: python main.py # or, in parallel: mpirun -np <N> python main.py """ import os import sys from PAOFLOW import PAOFLOW from PAOFLOW.basis_gen import generate_basis_for_pseudo from PAOFLOW.basis_gen.driver import _default_shells from PAOFLOW.inputs.read_upf import UPF as _UPFParser try: from mpi4py import MPI RANK = MPI.COMM_WORLD.Get_rank() except ImportError: RANK = 0 """
- PAOFLOW.gen.paoflow_driver.BASIS_GEN_BLOCK = Multiline-String[source]#
Show Value
""" def ensure_basis(preset="extended"): """Generate the pseudo-atom basis under BASISPATH for every species. The 'extended' preset is a superset of 'standard' and 'minimal', so generating it once is enough for every configuration used below. """ if RANK != 0: return for upf_path in UPFS: upf = _UPFParser(upf_path) element = upf.element.strip() elem_dir = os.path.join(BASISPATH, element) expected = _default_shells(upf, preset=preset) missing = [ s for s in expected if not os.path.exists(os.path.join(elem_dir, "{}.dat".format(s))) ] if missing: print("Generating pseudo-atom basis for {} under {} ...".format( element, BASISPATH)) generate_basis_for_pseudo( upf_path, BASISPATH.rstrip(os.sep), preset=preset, verbose=True ) else: print("Using existing pseudo-atom basis for {} under {}".format( element, BASISPATH)) """
- PAOFLOW.gen.paoflow_driver.ENERGY_RANGE_BLOCK = Multiline-String[source]#
Show Value
""" def suggest_energy_window(p): """Print the full PAO band range (eV, rel. to E_F) as a suggested window. Must be called after p.pao_eigh(). The eigenvalues ('E_k') are distributed across MPI ranks, so the local extrema are reduced to give every process the same global range. This only prints a hint; the calculation uses the user-defined EMIN / EMAX / NE constants above. """ import numpy as np E_k = p.data_controller.data_arrays['E_k'] emin = float(np.amin(E_k)) emax = float(np.amax(E_k)) if "MPI" in globals(): comm = MPI.COMM_WORLD emin = comm.allreduce(emin, op=MPI.MIN) emax = comm.allreduce(emax, op=MPI.MAX) if RANK == 0: print('Suggested energy window from PAO bands: ' 'EMIN={:.4f}, EMAX={:.4f} eV'.format(emin, emax)) print(' (currently using EMIN={}, EMAX={}, NE={})'.format(EMIN, EMAX, NE)) return emin, emax """
- PAOFLOW.gen.paoflow_driver.PROPERTY_MENU = [('bands', 'Band structure'), ('dos', 'DOS / projected DOS'), ('transport', 'Boltzmann transport...[source]#
- PAOFLOW.gen.paoflow_driver.select_properties()[source]#
Show the property menu and return the ordered set of chosen keys.
- PAOFLOW.gen.paoflow_driver.build_run_script(cfg)[source]#
Assemble a full property-run main.py from the collected config.
- PAOFLOW.gen.paoflow_driver.build_acbn0_script(cfg)[source]#
Assemble an ACBN0 / eACBN0 main.py from the collected config.
- PAOFLOW.gen.paoflow_driver.build_phonon_script(cfg)[source]#
Assemble a main.phonon.py harmonic-phonon workflow from the config.
The generated script drives the finite-displacement phonon workflow in three phases (generate displaced supercells -> run pw.x -> analyse forces), which can be run together or one at a time (handy on HPC schedulers).
- PAOFLOW.gen.paoflow_driver.ELPHON_TEMPLATE = Multiline-String[source]#
Show Value
"""#!/usr/bin/env python3 """PAOFLOW electron-phonon (Eliashberg) workflow -- PAO route (generated by paoflow_gen.py). Pseudo-atomic-orbital (Agapito & Bernardi, Phys. Rev. B 97, 235146 (2018)) interpolation of Quantum ESPRESSO's DFPT electron-phonon coupling. PAOFLOW reads QE's *full* coarse-grid coupling (no potential reconstruction), rotates it into the PAO gauge and Wigner-Seitz interpolates electrons + vertex to a dense grid for alpha^2F, lambda, omega_log and Tc. Two phases: python main.elphon.py inputs # write the ph.x phonon + AHC input templates python main.elphon.py analyse # PAO interpolation -> alpha^2F, lambda, Tc The ``analyse`` phase parallelises the per-q interpolation over MPI ranks, so on a cluster launch it with mpirun for an (up to nq-fold) speedup:: mpirun -np N python main.elphon.py analyse The dense electron diagonalisation is done redundantly on every rank, so it is usually best to keep N at or below the number of q-points and let BLAS threads (OMP_NUM_THREADS) fill the remaining cores. Between them, run the two QE ph.x steps in the same outdir (typically on HPC): 1. phonon: ph.x < <prefix>.ph.in # full DFPT dvscf on the q-grid 2. ahc: ph.x < <prefix>.ahc.in # electron_phonon='ahc' -> ahc_dir/ ``fildvscf`` and ``fildyn`` MUST match between the two ph.x steps. The AHC path (SOURCE='ahc') is for norm-conserving pseudopotentials; for ultrasoft / PAW use the patched-QE ``el_ph_mat`` dump (SOURCE='elphmat'). """ import argparse import os import sys import numpy as np from mpi4py import MPI from PAOFLOW import PAOFLOW from PAOFLOW.elphon.do_pao_eph import eliashberg_from_qe_coupling from PAOFLOW.elphon.elph_bloch import read_nscf # ----------------------------------------------------------------------- # # Configuration (edit freely -- masses / NELEC / NBND are system-specific) # # ----------------------------------------------------------------------- # HERE = os.path.dirname(os.path.abspath(__file__)) SAVEDIR = os.path.join(HERE, __SAVEDIR__) OUTPUTDIR = __OUTPUTDIR__ PREFIX = __PREFIX__ BASISDIR = os.path.join(HERE, __BASISDIR__) # Coupling source: # 'ahc' -> unpatched QE AHC dumps (ahc_dir/ahc_gkk_iq<iq>.bin); NC pseudos. # 'elphmat' -> patched-QE el_ph_mat dumps (elph_dir/elphmat.<iq>.dat); any pseudo. SOURCE = __SOURCE__ COUPLING_DIR = os.path.join(HERE, __COUPLING_DIR__) KGRID = __KGRID__ # SCF / coupling k-grid (== pw.x K_POINTS) QGRID = __QGRID__ # phonon q-grid (nq1, nq2, nq3) NBND = __NBND__ # bands in the nscf / AHC run (nbnd = ahc_nbnd) MASSES_AMU = __MASSES__ # atomic masses (amu), one per atom in the cell NELEC = __NELEC__ # valence electrons (dense E_F recompute) NK_DENSE = __NK_DENSE__ # dense interpolation grid SIGMA_RY = __SIGMA__ # Fermi-surface smearing (Ry) MU_STAR = __MU_STAR__ # Coulomb pseudopotential for Tc PTHR = __PTHR__ # projectability threshold DYNPREFIX = PREFIX # <DYNPREFIX>.dyn<iq> phonon files # For SOURCE='elphmat' (patched, symmetry-reduced) set the irreducible-q star # weights here; leave empty (or use SOURCE='ahc') to sum the full q-grid with # unit weights. Q_WEIGHTS = __QWEIGHTS__ def _phonon_input(): return '\n'.join([ '&inputph', " prefix='%s'," % PREFIX, " outdir='./',", " fildyn='%s.dyn'," % PREFIX, " fildvscf='dvscf',", ' tr2_ph=1.0d-12,', ' ldisp=.true., nq1=%d, nq2=%d, nq3=%d,' % (QGRID[0], QGRID[1], QGRID[2]), '/', '', ]) def _ahc_input(): return '\n'.join([ '&inputph', " prefix='%s'," % PREFIX, " outdir='./',", " fildyn='%s.dyn'," % PREFIX, " fildvscf='dvscf',", " electron_phonon='ahc',", ' ahc_nbnd=%d, ahc_nbndskip=0, skip_upperfan=.true.,' % NBND, " ahc_dir='ahc_dir/',", ' ldisp=.true., nq1=%d, nq2=%d, nq3=%d,' % (QGRID[0], QGRID[1], QGRID[2]), '/', '', ]) def inputs(): """Phase 1: write the ph.x phonon and AHC input templates.""" ph = os.path.join(HERE, PREFIX + '.ph.in') ahc = os.path.join(HERE, PREFIX + '.ahc.in') with open(ph, 'w') as fh: fh.write(_phonon_input()) with open(ahc, 'w') as fh: fh.write(_ahc_input()) print('Wrote %s' % ph) print('Wrote %s' % ahc) print('Run (same outdir), then `analyse`:') print(' 1) ph.x < %s' % os.path.basename(ph)) print(' 2) ph.x < %s' % os.path.basename(ahc)) def analyse(): """Phase 2: PAO interpolation of the QE coupling -> alpha^2F, lambda, Tc.""" if not os.path.isdir(SAVEDIR): sys.exit('%s not found. Run pw.x (nscf) first.' % SAVEDIR) if not os.path.isdir(COUPLING_DIR): sys.exit('%s not found. Run the QE phonon + AHC steps first (phase: inputs).' % COUPLING_DIR) pf = PAOFLOW.PAOFLOW(workpath=HERE, outputdir=OUTPUTDIR, savedir=SAVEDIR, verbose=False) pf.projections(configuration='standard', basispath=BASISDIR) pf.projectability(pthr=PTHR) # Grab the projection matrices A_k BEFORE pao_hamiltonian (which deletes them). A = pf.data_controller.data_arrays['U'][:, :, :, 0].copy() pf.pao_hamiltonian() HRs = pf.data_controller.data_arrays['HRs'] info = read_nscf(SAVEDIR) if SOURCE == 'ahc' or not Q_WEIGHTS: nq = QGRID[0] * QGRID[1] * QGRID[2] q_weights = [1.0] * nq else: q_weights = list(Q_WEIGHTS) nq = len(q_weights) dyn_paths = [os.path.join(HERE, '%s.dyn%d' % (DYNPREFIX, i + 1)) for i in range(nq)] out = eliashberg_from_qe_coupling( A, HRs, info['kpts_cryst'], info['bg'], info['at'], COUPLING_DIR, q_weights, tuple(KGRID), dyn_paths, source=SOURCE, masses_amu=MASSES_AMU, nk_dense=NK_DENSE, sigmas_ry=[SIGMA_RY], nelec=NELEC, mu_star=MU_STAR, ) # The Eliashberg result is identical on every rank (Allreduce inside the # driver); only rank 0 reports and writes the output files. if MPI.COMM_WORLD.Get_rank() != 0: return kB = 8.617333262e-5 # eV/K print('PAO-route Eliashberg (%s, source=%s, Nk=%d):' % (PREFIX, SOURCE, NK_DENSE)) print(' N(E_F) = %.3f states/spin/Ry' % out['dos_ef'].mean()) print(' lambda = %.4f' % out['lambda']) print(' <w_log> = %.1f K' % (out['omega_log'] / kB)) print(' Tc (McM) = %.2f K (mu* = %.3f)' % (out['Tc_mcmillan'], MU_STAR)) print(' Tc (AD) = %.2f K (mu* = %.3f)' % (out['Tc_allen_dynes'], MU_STAR)) outdir = os.path.join(HERE, OUTPUTDIR) os.makedirs(outdir, exist_ok=True) a2f = os.path.join(outdir, 'alpha2F.dat') np.savetxt(a2f, np.column_stack([out['omega'] * 1.0e3, out['a2F']]), header='omega(meV) alpha^2F(omega) ' '(lambda=%.4f, omega_log=%.2fK, Tc_McM=%.3fK, Tc_AD=%.3fK, mu*=%.3f)' % (out['lambda'], out['omega_log'] / kB, out['Tc_mcmillan'], out['Tc_allen_dynes'], MU_STAR)) npz = os.path.join(outdir, 'eliashberg.npz') np.savez(npz, **out) print(' wrote %s' % a2f) print(' wrote %s' % npz) def main(): parser = argparse.ArgumentParser(description='PAOFLOW electron-phonon (PAO) workflow.') parser.add_argument('phase', nargs='?', choices=['inputs', 'analyse', 'all'], default='all', help='Workflow phase to run (default: all).') args = parser.parse_args() if args.phase in ('inputs', 'all'): inputs() if args.phase in ('analyse', 'all'): analyse() if __name__ == '__main__': main() """
- PAOFLOW.gen.paoflow_driver.build_elphon_script(cfg)[source]#
Assemble a main.elphon.py electron-phonon (PAO route) workflow from the config.
- PAOFLOW.gen.paoflow_driver.ELPHON_PLOT_TEMPLATE = Multiline-String[source]#
Show Value
"""#!/usr/bin/env python3 """Plot the Eliashberg alpha^2F(omega) and cumulative lambda(omega). python plot.elphon.py Reads OUTPUTDIR/eliashberg.npz written by main.elphon.py (analyse). """ import os import numpy as np import matplotlib.pyplot as plt HERE = os.path.dirname(os.path.abspath(__file__)) OUTPUTDIR = __OUTPUTDIR__ NPZ = os.path.join(HERE, OUTPUTDIR, 'eliashberg.npz') def main(): if not os.path.isfile(NPZ): raise SystemExit('%s not found; run main.elphon.py analyse first.' % NPZ) d = np.load(NPZ) omega = d['omega'] * 1e3 # eV -> meV a2F = d['a2F'] lam = float(d['lambda']) tc_ad = float(d['Tc_allen_dynes']) if 'Tc_allen_dynes' in d else None tc_mcm = float(d['Tc_mcmillan']) if 'Tc_mcmillan' in d else None mu = float(d['mu_star']) if 'mu_star' in d else None # Cumulative lambda(omega) = 2 * integral_0^omega a2F(w)/w dw. w = d['omega'] with np.errstate(divide='ignore', invalid='ignore'): integrand = np.where(w > 0, 2.0 * a2F / w, 0.0) lam_cum = np.concatenate([[0.0], np.cumsum(0.5 * (integrand[1:] + integrand[:-1]) * np.diff(w))]) fig, ax1 = plt.subplots(figsize=(6, 4)) ax1.plot(omega, a2F, color='C0', label=r'$\alpha^2F(\omega)$') ax1.set_xlabel(r'$\omega$ (meV)') ax1.set_ylabel(r'$\alpha^2F(\omega)$', color='C0') ax1.set_xlim(left=0.0) ax1.set_ylim(bottom=0.0) ax2 = ax1.twinx() ax2.plot(omega, lam_cum, color='C3', label=r'$\lambda(\omega)$') ax2.set_ylabel(r'$\lambda(\omega)$', color='C3') ax2.set_ylim(bottom=0.0) title = r'Eliashberg spectral function ($\lambda = %.3f$)' % lam if tc_mcm is not None and tc_ad is not None: title += '\n' + r'$T_c^{McM} = %.2f$ K, $T_c^{AD} = %.2f$ K ($\mu^* = %.2f$)' % (tc_mcm, tc_ad, mu) ax1.set_title(title) fig.tight_layout() plt.show() if __name__ == "__main__": main() """
- PAOFLOW.gen.paoflow_driver.build_elphon_plot_script(cfg)[source]#
Assemble a plot.elphon.py for the Eliashberg alpha^2F / lambda output.
- PAOFLOW.gen.paoflow_driver.PLOT_HEADER = Multiline-String[source]#
Show Value
"""#!/usr/bin/env python3 """Plotting helper for the PAOFLOW run (generated by paoflow_gen.py). This script mirrors the property selection made when the PAOFLOW driver was generated. Run it after the PAOFLOW calculation has produced its output files and pick what to plot from the menu: python plot.py The energy window and property y-axis limits can be set on the command line: python plot.py --emin -5 --emax 5 --ymin 0 --ymax 100 Pass ``--all`` to plot every available quantity without any prompting (the menu and axis-limit prompts are skipped and the generated defaults are used): python plot.py --all and are also asked for interactively when the script runs (press Enter to keep the default / command-line value). EMIN/EMAX set the energy axis (vertical for band plots, horizontal for the energy-resolved property plots) and default to the window identified when the driver was generated. YMIN/YMAX set the property y-axis and default to automatic scaling. Optical spectra always start at 0 on the energy axis. The plots use the helpers in PAOFLOW.GPAO. """ import argparse import glob import os import sys from PAOFLOW import GPAO HERE = os.path.dirname(os.path.abspath(__file__)) OUTPUTDIR = os.path.join(HERE, __OUTPUTDIR__) pplt = GPAO.GPAO() # Default energy window (eV, relative to E_F) identified when the driver was # generated. Overridable on the command line via --emin/--emax. YMIN/YMAX # default to None (automatic axis limits) and can be set via --ymin/--ymax. EMIN = __EMIN__ EMAX = __EMAX__ YMIN = None YMAX = None def _ewin(): """Energy-axis window (EMIN, EMAX) used for band / energy-resolved plots.""" return (EMIN, EMAX) def _ewin_optical(): """Energy-axis window for optical spectra (always starts at 0).""" return (0.0, EMAX) def _ylim(): """Property-axis limits (YMIN, YMAX), or None for automatic scaling.""" if YMIN is None and YMAX is None: return None return (YMIN, YMAX) def _dos_mag_lim(fnames): """Magnitude-axis limits for DOS/PDOS plots. When the user sets YMIN/YMAX those win. Otherwise the magnitude axis is rescaled to the data that falls inside the chosen energy window [EMIN, EMAX] so that limiting the energy range actually zooms the curve (important for metals, whose largest DOS peaks may sit far outside the window). """ if YMIN is not None or YMAX is not None: return _ylim() if isinstance(fnames, str): fnames = [fnames] import numpy as np peak = 0.0 for fn in fnames: if not fn: continue try: data = np.loadtxt(fn) except (OSError, ValueError): continue if data.ndim != 2 or data.shape[1] < 2: continue es = data[:, 0] mag = data[:, 1:] mask = (es >= EMIN) & (es <= EMAX) if not mask.any(): continue peak = max(peak, float(np.nanmax(np.abs(mag[mask])))) if peak <= 0.0: return None return (0.0, 1.1 * peak) def _ask_float(prompt, default): """Prompt for a float, accepting Enter to keep *default* (may be None).""" shown = 'auto' if default is None else default try: raw = input('{} [{}]: '.format(prompt, shown)).strip() except EOFError: return default if raw == '': return default if raw.lower() in ('auto', 'none'): return None try: return float(raw) except ValueError: print(' (invalid number, keeping {})'.format(shown)) return default def _one(pattern): """Return the first OUTPUTDIR file matching *pattern* (or None). Patterns are written with an optional '<prefix>.' in front (e.g. '*.bands_0.dat'). PAOFLOW may write the files either with that prefix ('Si.bands_0.dat') or without it ('bands_0.dat'), so a '*.' pattern falls back to the prefix-less form. """ hits = sorted(glob.glob(os.path.join(OUTPUTDIR, pattern))) if not hits and pattern.startswith('*.'): hits = sorted(glob.glob(os.path.join(OUTPUTDIR, pattern[2:]))) return hits[0] if hits else None def _many(pattern): """Return all OUTPUTDIR files matching *pattern* (sorted). As with _one, a '*.' pattern also matches prefix-less files. """ hits = sorted(glob.glob(os.path.join(OUTPUTDIR, pattern))) if not hits and pattern.startswith('*.'): hits = sorted(glob.glob(os.path.join(OUTPUTDIR, pattern[2:]))) return hits def _missing(*names): """Warn about missing output files; return True if anything is missing.""" gone = [n for n in names if n is None] if gone: print(" (output file(s) not found in {}; run PAOFLOW first)".format(OUTPUTDIR)) return True return False def _file_energy_range(fname): """Return (min, max) of the energy column of *fname*, or None.""" if not fname: return None import numpy as np try: data = np.loadtxt(fname) except (OSError, ValueError): return None if data.ndim != 2 or data.shape[0] == 0: return None es = data[:, 0] return (float(np.nanmin(es)), float(np.nanmax(es))) def _default_energy_window(): """Default energy window for the prompts. When a DOS file is present its energy grid is used so that the DOS fills the side-by-side panel (the DOS is usually computed on a narrower grid than the bands). Otherwise the window identified when the driver was generated is kept. """ return _file_energy_range(_one('*.dosdk_0.dat')) _SPIN_COLORS = ['tab:blue', 'tab:red', 'tab:green', 'tab:orange'] def _spin_channels(pattern): """Return ([files], [labels]) for the spin channels of *pattern*. The generated patterns target the first spin channel through a '_0' tag. A spin-polarized (nspin=2) run also writes the '_1' channel; when present both files are returned so the two channels can be overlaid in one figure. """ f0 = _one(pattern) if f0 is None: return [], [] files, labels = [f0], ['spin up'] f1 = _one(pattern.replace('_0.', '_1.')) if f1 is not None: files.append(f1) labels.append('spin down') return files, labels def _overlay_transport(files, labels, title, y_label, scale=1.0, min_zero=False): """Overlay the diagonal-averaged transport tensor of several files. Used for spin-polarized conductivity / Seebeck output, where each spin channel is written to its own file: both channels are drawn on a single figure (one line per spin and temperature). """ import numpy as np import matplotlib.pyplot as plt from PAOFLOW.inputs.read_pao_output import read_transport_PAO fig = plt.figure() fig.suptitle(title) ax = fig.add_subplot(111) ic = 0 ymax = 0.0 for fn, base in zip(files, labels): enes, temps, tensors = read_transport_PAO(fn) for it, temp in enumerate(temps): trace = scale * np.einsum('eii->e', tensors[it]) / 3.0 lbl = base if len(temps) == 1 else '{}, T={:g}'.format(base, temp) ax.plot(enes, trace, color=_SPIN_COLORS[ic % len(_SPIN_COLORS)], label=lbl) ymax = max(ymax, float(np.nanmax(np.abs(trace)))) ic += 1 ax.set_xlim(*_ewin()) yl = _ylim() if yl is not None: ax.set_ylim(*yl) elif min_zero and ymax > 0.0: ax.set_ylim(0.0, 1.1 * ymax) ax.set_xlabel('Energy (eV)') ax.set_ylabel(y_label) ax.legend() plt.show() def _overlay_bands_dos(band_files, dos_files, labels, sym_file, title): """Bands beside DOS with both spin channels overlaid on one figure. Reproduces plot_dos_beside_bands but loops over the spin channels so that the two channels share a single bands panel and a single DOS panel. """ import numpy as np import matplotlib.pyplot as plt from matplotlib import gridspec from PAOFLOW.inputs.read_pao_output import ( read_bands_PAO, read_dos_PAO, read_band_path_PAO, ) sym_points = read_band_path_PAO(sym_file) if sym_file else None y_lim = _ewin() fig = plt.figure() spec = gridspec.GridSpec(ncols=2, nrows=1, width_ratios=[5, 1]) fig.suptitle(title) ax_b = fig.add_subplot(spec[0]) ax_d = fig.add_subplot(spec[1]) nk = 1 dos_peak = 0.0 for i, (fb, fd, lbl) in enumerate(zip(band_files, dos_files, labels)): col = _SPIN_COLORS[i % len(_SPIN_COLORS)] bands = read_bands_PAO(fb) nk = bands.shape[1] for j, b in enumerate(bands): ax_b.plot(b, color=col, label=lbl if j == 0 else None) es, dos = read_dos_PAO(fd) ax_d.plot(dos, es, color=col, label=lbl) mask = (es >= y_lim[0]) & (es <= y_lim[1]) if mask.any(): dos_peak = max(dos_peak, float(np.nanmax(np.abs(dos[mask])))) ax_b.set_xlim(0, nk - 1) ax_b.set_ylim(*y_lim) if sym_points is None: ax_b.xaxis.set_visible(False) else: ax_b.set_xticks(sym_points[0]) ax_b.set_xticklabels(sym_points[1]) ax_b.vlines(sym_points[0], y_lim[0], y_lim[1], color='gray') ax_b.set_ylabel(r'$\epsilon$($\mathbf{k}$) (eV)', fontsize=12) ax_b.legend() xl = _ylim() if xl is not None: ax_d.set_xlim(*xl) elif dos_peak > 0.0: ax_d.set_xlim(0.0, 1.1 * dos_peak) ax_d.set_ylim(*y_lim) ax_d.yaxis.set_visible(False) ax_d.set_xlabel('DOS') plt.tight_layout() plt.show() """
- PAOFLOW.gen.paoflow_driver.build_plot_script(cfg)[source]#
Assemble a plot.py that mimics the property selection in cfg.
- PAOFLOW.gen.paoflow_driver.build_acbn0_plot_script(cfg)[source]#
Assemble a plot.acbn0.py that overlays the ACBN0 / eACBN0 band structures.
The ACBN0 driver writes one band-structure file per converged case (
bands_Ufor the on-site-U solution and, for eACBN0,bands_UVfor the joint U+V solution). This script overlays whichever of those cases are present so they can be compared directly on a single figure.
- PAOFLOW.gen.paoflow_driver.build_phonon_plot_script(cfg)[source]#
Assemble a plot.phonon.py that plots the dispersion, DOS and thermals.
Reads the
phonon_band.dat/phonon_dos.dat/phonon_band.labels/phonon_thermal.datfiles written bymain.phonon.pyand renders them throughPAOFLOW.GPAO.GPAO.
- PAOFLOW.gen.paoflow_driver.build_raman_script(cfg)[source]#
Assemble a main.raman.py non-resonant (Placzek) Raman workflow.
The generated script displaces the primitive cell by
+/-deltaalong every optical zone-centre eigenvector (read from the harmonic-phononFORCE_SETS), writes an SCF-only QE input per displacement, and – in the analyse phase – runs the PAOFLOW internal-projection optical pipeline on each displaced cell to finite-difference the Raman tensor.
- PAOFLOW.gen.paoflow_driver.build_raman_plot_script(cfg)[source]#
Assemble a plot.raman.py that overlays the broadened Raman spectra.
- PAOFLOW.gen.paoflow_driver.collect_common(args, workdir)[source]#
Prompt for configuration shared by both workflows.
- PAOFLOW.gen.paoflow_driver.collect_run(common)[source]#
Prompt for the full property-run configuration.
- PAOFLOW.gen.paoflow_driver.collect_phonon(common)[source]#
Prompt for the harmonic-phonon (finite-displacement) configuration.
- PAOFLOW.gen.paoflow_driver.collect_elphon(common)[source]#
Prompt for the electron-phonon (PAO route) configuration.