Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ Source = "https://github.com/aiidaplugins/aiida-epw"
'epw.base' = 'aiida_epw.workflows.base:EpwBaseWorkChain'
'epw.epw_prep' = 'aiida_epw.workflows.prep:EpwPrepWorkChain'
'epw.supercon' = 'aiida_epw.workflows.supercon:SuperConWorkChain'
'epw.mobility' = 'aiida_epw.workflows.mobility:MobilityWorkChain'


[project.optional-dependencies]
docs = [
Expand Down
32 changes: 31 additions & 1 deletion src/aiida_epw/calculations/epw.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class EpwCalculation(CalcJob):
_output_phbands_file = "phband.freq"
_FOLDER_SAVE = "save"
_FOLDER_DYNAMICAL_MATRIX = "DYN_MAT"
_MAX_NSTEMP = 1000

# Not using symlink in pw to allow multiple nscf to run on top of the same scf
_default_symlink_usage = False
Expand Down Expand Up @@ -103,6 +104,12 @@ def define(cls, spec):
required=False,
help=("The script to convert the chk file to a ukk file"),
)
spec.input(
"quadrupole_dir",
valid_type=(orm.Str, orm.RemoteData),
required=False,
help=("RemoteData containing the quadrupole.fmt file, or its absolute path string."),
)

spec.inputs["metadata"]["options"]["parser_name"].default = "epw.epw"

Expand Down Expand Up @@ -299,6 +306,22 @@ def test_offset(offset):
)
)

if 'quadrupole_dir' in self.inputs:
source_path = self.inputs.quadrupole_dir.value

code = self.inputs.epw.code

remote_list.append(
(
code.computer.uuid,
Path(
source_path, ".quadrupole.fmt"
).as_posix(),
"quadrupole.fmt",
)
)


if "parent_folder_epw" in self.inputs:
parent_folder_epw = self.inputs.parent_folder_epw
if isinstance(parent_folder_epw, orm.RemoteStashFolderData):
Expand All @@ -314,7 +337,14 @@ def test_offset(offset):
"selecq.fmt",
"crystal.fmt",
"epwdata.fmt",
vme_fmt_dict[parameters["INPUTEPW"]["vme"]],
"dmedata.fmt",
"vmedata.fmt",
"quadrupole.fmt",
"decay.H",
"decay.dynmat",
"decay.epmate",
"decay.epmatp",
# vme_fmt_dict[parameters["INPUTEPW"]["vme"]],
f"{self._PREFIX}.kgmap",
f"{self._PREFIX}.kmap",
f"{self._PREFIX}.ukk",
Expand Down
189 changes: 187 additions & 2 deletions src/aiida_epw/tools/parsers.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,87 @@
"""Manual parsing functions for post-processing."""
"""Manual parsing functions for post-processing. These functions can either be used independently or as helper functions of the `EpwParser` class.
It would be good to always have a `parsed_data` dictionary as an output."""

import numpy
import re
import io

Ry2eV = 13.605662285137


### This parse function is used for parsing the `band.eig` and `phband.freq` files.
def parse_epw_bands(file_content):
"""Parse the contents of a band structure file generated by EPW software."""
parsed_data = {}

nbnd, nks = (
int(v)
for v in re.search(r"&plot nbnd=\s+(\d+), nks=\s+(\d+)", file_content).groups()
)
kpt_pattern = re.compile(r"\s([\s-][\d\.]+)" * 3)
band_pattern = re.compile(r"\s+([-\d\.]+)" * nbnd)

kpoints = []
bands = []

for number, line in enumerate(file_content.splitlines()):
match_kpt = re.search(kpt_pattern, line)
if match_kpt and number % 2 == 1:
kpoints.append(list(match_kpt.groups()))

match_band = re.search(band_pattern, line)
if match_band and number % 2 == 0:
bands.append(list(match_band.groups()))

parsed_data["kpoints"] = numpy.array(kpoints, dtype=float)
parsed_data["bands"] = numpy.array(bands, dtype=float)

return parsed_data


def parse_epw_eldos(file_content):
"""Parse the contents of the `.dos` file.
parameters:
file_content: the content of the `.dos` file as a string.
returns:
parsed_data: a dictionary containing the energy, the electronic density of states and the integrated density of states.
"""
parsed_data = {}

dos = numpy.loadtxt(io.StringIO(file_content), dtype=float, comments="#")

parsed_data["energy"] = dos[:, 0]
parsed_data["edos"] = dos[:, 1]
parsed_data["integrated_dos"] = dos[:, 2]
return parsed_data


def parse_epw_phdos(file_content):
"""Parse the contents of the `.phdos` file.
parameters:
file_content: the content of the `.phdos` file as a string.
returns:
parsed_data: a dictionary containing the frequency, the phonon density of states. Note that there are multiple phonon density of states for different smearing.
"""
parsed_data = {}

phdos = numpy.loadtxt(io.StringIO(file_content), dtype=float, skiprows=1)
parsed_data["frequency"] = phdos[:, 0]
parsed_data["phdos"] = phdos[:, 1:]
return parsed_data


def parse_epw_a2f(file_content):
"""Parse the contents of the `.a2f` file.
parameters:
file_content: the content of the `.a2f` file as a string.
returns:
parsed_data: a dictionary containing the frequency, the spectral function for different smearing, and metadata such as electron smearing, Fermi window and summed el-ph coupling strength.
"""
parsed_data = {}

a2f, footer = file_content.split("\n Integrated el-ph coupling")

a2f_array = numpy.array([line.split() for line in a2f.split("\n")], dtype=float)
a2f_array = numpy.array([line.split() for line in a2f.split("\n")[1:]], dtype=float)
parsed_data["frequency"] = a2f_array[:, 0]
parsed_data["a2f"] = a2f_array[:, 1:]

Expand All @@ -31,3 +102,117 @@ def parse_epw_a2f(file_content):
parsed_data[property] = float(line.split()[-1])

return parsed_data


def parse_epw_max_eigenvalue(file_content):
"""Parse the max_eigenvalue part of the `stdout` file when solving the linearized Eliashberg equation."""
parsed_data = {}
re_pattern = re.compile(r"\s+([\d\.]+)\s+([\d\.-]+)\s+\d+\s+[\d\.]+\s+\d+\n")
parsing_block = file_content.split(
"Finish: Solving (isotropic) linearized Eliashberg"
)[0]

parsed_data["max_eigenvalue"] = numpy.array(
re_pattern.findall(parsing_block), dtype=float
)
return parsed_data


def parse_epw_imag_iso(file_contents, prefix="aiida"):
"""Parse the isotropic gap functions from EPW isotropic Eliashberg equation calculation.
parameters:
folder: the folder containing the `imag_iso` files. When serving as a helper function, it can take a `Retrieved` folder from aiida .
When used independently, it can take a local folder.
prefix: the prefix of the `imag_iso` files.
returns:
parsed_data: a dictionary containing the isotropic gap functions of numpy array type and the corresponding temperatures as keys.
"""
parsed_data = {}
pattern_iso = re.compile(rf"^{prefix}\.imag_iso_(\d{{3}}\.\d{{2}})$")

for filename, file_content in file_contents.items():
match = pattern_iso.match(filename)
if match:
T = float(match.group(1))
gap_function = numpy.loadtxt(
io.StringIO(file_content), dtype=float, comments="#", skiprows=1
)
parsed_data[T] = gap_function
return parsed_data


def parse_epw_imag_aniso_gap0(file_contents, prefix="aiida"):
"""Parse the anisotropic gap functions from EPW anisotropic Eliashberg equation calculation.
parameters:
file_contents: a dictionary containing the file contents with filename as keys.
prefix: the prefix of the `imag_aniso_gap0` files.
returns:
parsed_data: a sorted dictionary containing the anisotropic gap functions of numpy array type and the corresponding temperatures as keys.
"""
parsed_data = {}
pattern_aniso_gap0 = re.compile(rf"^{prefix}\.imag_aniso_gap0_(\d{{3}}\.\d{{2}})$")

for filename, file_content in file_contents.items():
match = pattern_aniso_gap0.match(filename)
if match:
T = float(match.group(1))
gap_function = numpy.loadtxt(
io.StringIO(file_content), dtype=float, comments="#", skiprows=1
)
parsed_data[T] = gap_function
return parsed_data


@staticmethod
def parse_stdout(stdout, logs):
"""Parse the ``stdout``."""

def parse_max_eigenvalue(stdout_block):
re_pattern = re.compile(
r"\s+([\d\.]+)\s+([\d\.-]+)\s+\d+\s+[\d\.]+\s+\d+\n"
)
parsing_block = stdout_block.split(
"Finish: Solving (isotropic) linearized Eliashberg"
)[0]
max_eigenvalue_array = orm.XyData()
max_eigenvalue_array.set_array(
"max_eigenvalue",
numpy.array(re_pattern.findall(parsing_block), dtype=float),
)
return max_eigenvalue_array

data_type_regex = (
(
"allen_dynes",
float,
re.compile(r"\s+Estimated Allen-Dynes Tc =\s+([\d\.]+) K"),
),
(
"fermi_energy_coarse",
float,
re.compile(r"\s+Fermi energy coarse grid =\s+([\d\.-]+)\seV"),
),
)
data_block_marker_parser = (
(
"max_eigenvalue",
"Superconducting transition temp. Tc",
parse_max_eigenvalue,
),
)
parsed_data = {}
stdout_lines = stdout.split("\n")

for line_number, line in enumerate(stdout_lines):
for data_key, type, re_pattern in data_type_regex:
match = re.search(re_pattern, line)
if match:
parsed_data[data_key] = type(match.group(1))

for data_key, data_marker, block_parser in data_block_marker_parser:
if data_marker in line:
parsed_data[data_key] = block_parser(stdout[line_number:])

return parsed_data, logs


Loading