Source code for aim2dat.io.xmgrace
"""Functions to read plot-files of xmgrace."""
import re
import numpy as np
[docs]
def read_xmgrace_file(file_path):
"""
Read xmgrace plot filies.
Up to now the functionality is very limited. xy-data is read and grouped by the line-color.
Notes
-----
TODO: Increase functionality and generality.
Parameters
----------
file_path : str
Path to the xmgrace file.
Returns
-------
x_values : list
Nested list of the x-values.
y_values : list
Nested list of the y-values.
tick_labels : tuple
Tuple of dictionaries containing the position on the x-axis ("pos") and the label
("label").
"""
def parse_header(fobj):
"""Parse header information of the xmgrace file."""
plot_categories = {}
tick_labels = {}
pattern_tick_major = re.compile(
r"^@\s*xaxis\s*tick\s*major\s*(?P<tick_nr>\d+),"
r"\s*(?P<tick_pos>([-+]?[0-9]*\.?[0-9]*))$"
)
pattern_tick_label = re.compile(
r'^@\s*xaxis\s*ticklabel\s*(?P<tick_nr>\d+),\s*"(?P<tick_label>.*)"$'
)
pattern_line_color = re.compile(
r"^@\s*s(?P<plot_nr>\d+)\s*line\s*color\s*(?P<color>\d+)?$"
)
str_stop = "@target"
with open(file_path, "r") as fobj:
for line_idx, line in enumerate(fobj):
if pattern_line_color.match(line):
match = pattern_line_color.match(line)
group_dict = match.groupdict()
plot_categories[int(group_dict["plot_nr"])] = int(group_dict["color"])
elif pattern_tick_label.match(line):
match = pattern_tick_label.match(line)
group_dict = match.groupdict()
tick_nr = int(group_dict["tick_nr"])
if tick_nr not in tick_labels:
tick_labels[tick_nr] = {"label": group_dict["tick_label"]}
else:
tick_labels[tick_nr]["label"] = group_dict["tick_label"]
elif pattern_tick_major.match(line):
match = pattern_tick_major.match(line)
group_dict = match.groupdict()
tick_nr = int(group_dict["tick_nr"])
if tick_nr not in tick_labels:
tick_labels[tick_nr] = {"pos": float(group_dict["tick_pos"])}
else:
tick_labels[tick_nr]["pos"] = float(group_dict["tick_pos"])
if str_stop in line:
break
nr_of_categories = len(set(plot_categories.values()))
# Sort tick labels:
tick_labels = list(tick_labels.values())
zipped = list(zip([tick_label["pos"] for tick_label in tick_labels], tick_labels))
zipped.sort(key=lambda point: point[0])
_, tick_labels = zip(*zipped)
return nr_of_categories, plot_categories, line_idx, tick_labels
str_start_set = "@type xy"
start_pattern = re.compile(r"^@target\s*G\d+.S(\d+)?$")
str_end_set = "&"
in_set = False
nr_of_categories, plot_categories, start_idx, tick_labels = parse_header(file_path)
with open(file_path, "r") as fobj:
x_values = [[] for idx in range(nr_of_categories)]
y_values = [[] for idx in range(nr_of_categories)]
lines = fobj.readlines()[start_idx:]
for line in lines:
if not in_set:
match = start_pattern.match(line)
if match:
category_idx = plot_categories[int(match.group(1))] - nr_of_categories + 1
x_values[category_idx].append([])
y_values[category_idx].append([])
elif str_start_set in line:
in_set = True
elif str_end_set in line:
in_set = False
elif in_set:
x_values[category_idx][-1].append(float(line.split()[0]))
y_values[category_idx][-1].append(float(line.split()[1]))
return x_values, y_values, tick_labels
[docs]
def read_band_structure(file_path, kpoints):
"""
Read xmgrace band structure file.
Parameters
----------
file_path : str
Path to the xmgrace file.
kpoints : list
List of tuples containing the label and the k-point.
Returns
-------
band_structures : list
List of band structures.
"""
band_structures = []
x_values, y_values, tick_labels = read_xmgrace_file(file_path)
x_values = x_values[0][0]
path = []
# Construct segments and reverse-engineer k-path:
labels = []
x_values_indices = [0, 0]
for segment_idx in range(len(tick_labels) - 1):
# Determine number of points:
for idx, value in enumerate(x_values[x_values_indices[0] :]):
if abs(value - tick_labels[segment_idx + 1]["pos"]) < 1e-4:
x_values_indices[1] = x_values_indices[0] + idx
break
# Check labels and find k-points:
segment_labels = (tick_labels[segment_idx]["label"], tick_labels[segment_idx + 1]["label"])
segment_kpoints = []
for x_values_idx, segment_label in zip(x_values_indices, segment_labels):
if segment_label == "\\xG\\f{}":
segment_kpoints.append(kpoints["Gamma"])
labels.append((x_values_idx, "Gamma"))
else:
segment_kpoints.append(kpoints[segment_label])
labels.append((x_values_idx, segment_label))
# Calculate directional vector
vector = np.subtract(segment_kpoints[1], segment_kpoints[0])
# Crate k-path:
length = x_values_indices[1] - x_values_indices[0]
for point_nr in range(length + 1):
path.append(np.add(segment_kpoints[0], point_nr * vector / (length)).tolist())
x_values_indices[0] = x_values_indices[1] + 1
for bands_cat in y_values:
band_structures.append(
{
"kpoints": path,
"bands": np.asarray(bands_cat).transpose().tolist(),
"path_labels": labels,
}
)
return band_structures