mirror of
https://github.com/vale981/master-thesis
synced 2025-03-06 10:31:37 -05:00
350 lines
8.7 KiB
Python
350 lines
8.7 KiB
Python
import os
|
|
from pathlib import Path
|
|
import matplotlib
|
|
import matplotlib.pyplot as plt
|
|
import numpy as np
|
|
from contextlib import contextmanager
|
|
|
|
|
|
fig_path = Path(os.getcwd()) / "figures"
|
|
val_path = Path(os.getcwd()) / "values"
|
|
|
|
|
|
def cm2inch(*tupl):
|
|
inch = 2.54
|
|
if isinstance(tupl[0], tuple):
|
|
return tuple(i / inch for i in tupl[0])
|
|
else:
|
|
return tuple(i / inch for i in tupl)
|
|
|
|
|
|
def export_fig(name, fig=None, style=None):
|
|
fig_path.mkdir(parents=True, exist_ok=True)
|
|
if fig is None:
|
|
fig = plt.gcf()
|
|
|
|
with hiro_style(style):
|
|
fig.tight_layout()
|
|
fig.canvas.draw()
|
|
|
|
fig.savefig(fig_path / f"{name}.pdf")
|
|
fig.savefig(fig_path / f"{name}.svg")
|
|
fig.savefig(fig_path / f"{name}.pgf")
|
|
|
|
return fig
|
|
|
|
|
|
def scientific_round(val, *err, retprec=False):
|
|
"""Scientifically rounds the values to the given errors."""
|
|
val, err = np.asarray(val), np.asarray(err)
|
|
if len(err.shape) == 1:
|
|
err = np.array([err])
|
|
err = err.T
|
|
err = err.T
|
|
|
|
if err.size == 1 and val.size > 1:
|
|
err = np.ones_like(val) * err
|
|
|
|
if len(err.shape) == 0:
|
|
err = np.array([err])
|
|
|
|
if val.size == 1 and err.shape[0] > 1:
|
|
val = np.ones_like(err) * val
|
|
|
|
i = np.floor(np.log10(err))
|
|
first_digit = (err // 10 ** i).astype(int)
|
|
prec = (-i + np.ones_like(err) * (first_digit <= 3)).astype(int)
|
|
prec = np.max(prec, axis=1)
|
|
|
|
def smart_round(value, precision):
|
|
value = np.round(value, precision)
|
|
if precision <= 0:
|
|
value = value.astype(int)
|
|
return value
|
|
|
|
if val.size > 1:
|
|
rounded = np.empty_like(val)
|
|
rounded_err = np.empty_like(err)
|
|
for n, (value, error, precision) in enumerate(zip(val, err, prec)):
|
|
rounded[n] = smart_round(value, precision)
|
|
rounded_err[n] = smart_round(error, precision)
|
|
|
|
if retprec:
|
|
return rounded, rounded_err, prec
|
|
else:
|
|
return rounded, rounded_err
|
|
|
|
else:
|
|
prec = prec[0]
|
|
if retprec:
|
|
return (smart_round(val, prec), *smart_round(err, prec)[0], prec)
|
|
else:
|
|
return (smart_round(val, prec), *smart_round(err, prec)[0])
|
|
|
|
|
|
def lighten_color(color, amount=0.5):
|
|
"""
|
|
Lightens the given color by multiplying (1-luminosity) by the given amount.
|
|
Input can be matplotlib color string, hex string, or RGB tuple.
|
|
|
|
Examples:
|
|
>> lighten_color('g', 0.3)
|
|
>> lighten_color('#F034A3', 0.6)
|
|
>> lighten_color((.3,.55,.1), 0.5)
|
|
"""
|
|
import matplotlib.colors as mc
|
|
import colorsys
|
|
|
|
try:
|
|
c = mc.cnames[color]
|
|
except:
|
|
c = color
|
|
c = colorsys.rgb_to_hls(*mc.to_rgb(c))
|
|
return colorsys.hls_to_rgb(c[0], 1 - amount * (1 - c[1]), c[2])
|
|
|
|
|
|
def tex_value(val, err=None, unit=None, prefix="", suffix="", prec=0, save=None):
|
|
"""Generates LaTeX output of a value with units and error."""
|
|
|
|
if err:
|
|
val, err, prec = scientific_round(val, err, retprec=True)
|
|
else:
|
|
val = np.round(val, prec)
|
|
|
|
if prec == 0:
|
|
val = int(val)
|
|
if err:
|
|
err = int(err)
|
|
|
|
val_string = fr"{val:.{prec}f}" if prec > 0 else str(val)
|
|
if err:
|
|
val_string += fr"\pm {err:.{prec}f}" if prec > 0 else str(err)
|
|
|
|
ret_string = r"\(" + prefix
|
|
|
|
if unit is None:
|
|
ret_string += val_string
|
|
else:
|
|
ret_string += fr"\SI{{{val_string}}}{{{unit}}}"
|
|
|
|
ret_string += suffix + r"\)"
|
|
|
|
if save is not None:
|
|
val_path.mkdir(parents=True, exist_ok=True)
|
|
|
|
with open(val_path / f"{save}.tex", "w") as f:
|
|
f.write(ret_string)
|
|
|
|
return ret_string
|
|
|
|
|
|
###############################################################################
|
|
# Plot Porn #
|
|
###############################################################################
|
|
|
|
|
|
def wrap_plot(f):
|
|
def wrapped(*args, ax=None, setup_function=plt.subplots, **kwargs):
|
|
fig = None
|
|
if not ax:
|
|
fig, ax = setup_function()
|
|
|
|
ret_val = f(*args, ax=ax, **kwargs)
|
|
return (fig, ax, ret_val) if ret_val else (fig, ax)
|
|
|
|
return wrapped
|
|
|
|
|
|
def get_figsize(width="thesis", fraction=1, subplots=(1, 1)):
|
|
"""Set figure dimensions to avoid scaling in LaTeX.
|
|
|
|
Parameters
|
|
----------
|
|
width: float or string
|
|
Document width in points, or string of predined document type
|
|
fraction: float, optional
|
|
Fraction of the width which you wish the figure to occupy
|
|
subplots: array-like, optional
|
|
The number of rows and columns of subplots.
|
|
Returns
|
|
-------
|
|
fig_dim: tuple
|
|
Dimensions of figure in inches
|
|
"""
|
|
if width == "thesis":
|
|
width_pt = 330.62111
|
|
elif width == "poster":
|
|
width_pt = 957.13617
|
|
elif width == "beamer":
|
|
width_pt = 307.28987
|
|
else:
|
|
width_pt = width
|
|
|
|
# Width of figure (in pts)
|
|
fig_width_pt = width_pt * fraction
|
|
# Convert from pt to inches
|
|
inches_per_pt = 1 / 72.27
|
|
|
|
# Golden ratio to set aesthetic figure height
|
|
# https://disq.us/p/2940ij3
|
|
golden_ratio = (5 ** 0.5 - 1) / 2
|
|
|
|
# Figure width in inches
|
|
fig_width_in = fig_width_pt * inches_per_pt
|
|
# Figure height in inches
|
|
fig_height_in = fig_width_in * golden_ratio * (subplots[0] / subplots[1])
|
|
|
|
return (fig_width_in, fig_height_in)
|
|
|
|
|
|
MPL_RC = {
|
|
"font.family": "serif",
|
|
"text.usetex": False,
|
|
"pgf.rcfonts": False,
|
|
"lines.linewidth": 1,
|
|
"font.size": 10,
|
|
"axes.labelsize": 10,
|
|
"axes.titlesize": 8,
|
|
"legend.fontsize": 8,
|
|
"xtick.labelsize": 8,
|
|
"ytick.labelsize": 8,
|
|
}
|
|
|
|
MPL_RC_POSTER = {
|
|
"font.family": "sans",
|
|
"text.usetex": False,
|
|
"pgf.rcfonts": False,
|
|
"lines.linewidth": 2,
|
|
"font.size": 17.28,
|
|
"axes.labelsize": 17.28,
|
|
"axes.titlesize": 17.28,
|
|
"legend.fontsize": 17.28,
|
|
"xtick.labelsize": 14.4,
|
|
"ytick.labelsize": 14.4,
|
|
}
|
|
|
|
|
|
@contextmanager
|
|
def hiro_style(style=None):
|
|
with plt.style.context("ggplot"):
|
|
with matplotlib.rc_context(MPL_RC_POSTER if style == "poster" else MPL_RC):
|
|
yield True
|
|
|
|
|
|
plt.style.use("ggplot")
|
|
matplotlib.rcParams.update(MPL_RC)
|
|
|
|
|
|
@wrap_plot
|
|
def plot_complex(x, y, *args, ax=None, label="", **kwargs):
|
|
label = label + ", " if (len(label) > 0) else ""
|
|
ax.plot(x, y.real, *args, label=f"{label}real part", **kwargs)
|
|
ax.plot(x, y.imag, *args, label=f"{label}imag part", **kwargs)
|
|
ax.legend()
|
|
|
|
|
|
def fancy_error(x, y, err, ax, **kwargs):
|
|
line = ax.plot(
|
|
x,
|
|
y,
|
|
**kwargs,
|
|
)
|
|
|
|
err = ax.fill_between(
|
|
x,
|
|
y + err,
|
|
y - err,
|
|
color=lighten_color(line[0].get_color(), 0.5),
|
|
alpha=0.5,
|
|
)
|
|
|
|
return line, err
|
|
|
|
|
|
@wrap_plot
|
|
def plot_convergence(
|
|
x,
|
|
y,
|
|
ax=None,
|
|
label="",
|
|
transform=lambda y: y,
|
|
slice=None,
|
|
linestyle="-",
|
|
bath=None,
|
|
color=None,
|
|
**kwargs,
|
|
):
|
|
slice = (0, -1) if not slice else slice
|
|
ys = y[slice[0] : slice[1]]
|
|
label = label + (", " if (len(label) > 0 and len(ys) > 0) else "")
|
|
for n, val, _ in ys:
|
|
plt.plot(
|
|
x,
|
|
transform(val[bath] if bath is not None else val),
|
|
label=f"{label}$N={n}$",
|
|
alpha=n / y[-1][0],
|
|
linestyle=":",
|
|
)
|
|
|
|
fancy_error(
|
|
x,
|
|
transform(y[-1][1][bath] if bath is not None else y[-1][1]),
|
|
(y[-1][2][bath] if bath is not None else y[-1][2]).real,
|
|
label=f"{label}" + (fr"$N={y[-1][0]}$" if len(ys) > 1 else ""),
|
|
linestyle=linestyle,
|
|
color=color,
|
|
ax=ax,
|
|
**kwargs,
|
|
)
|
|
|
|
return None
|
|
|
|
|
|
@wrap_plot
|
|
def plot_diff_vs_sigma(
|
|
x,
|
|
y,
|
|
reference,
|
|
ax=None,
|
|
label="",
|
|
transform=lambda y: y,
|
|
ecolor="yellow",
|
|
ylabel=None,
|
|
bath=None,
|
|
):
|
|
label = label + ", " if (len(label) > 0) else ""
|
|
y = y if bath is None else [(n, val[bath], err[bath]) for n, val, err in y]
|
|
|
|
ref_traj = transform(reference)
|
|
ref_err = np.abs(y[-1][2].real)
|
|
|
|
ax.fill_between(
|
|
x,
|
|
0,
|
|
ref_err,
|
|
color=ecolor,
|
|
label=fr"{label}$\sigma\, (N={y[-1][0]})$",
|
|
)
|
|
|
|
for n, val, err in y:
|
|
diff = np.abs(transform(val) - ref_traj)
|
|
within = (diff < ref_err).sum() / y[-1][2].size
|
|
|
|
not_last = n < y[-1][0]
|
|
|
|
ax.plot(
|
|
x,
|
|
diff,
|
|
label=fr"{label}$N={n}$ $\Delta<\sigma = {within * 100:.1f}\%$",
|
|
alpha=within if not_last else 1,
|
|
linestyle=":" if not_last else "-",
|
|
color=None if not_last else "red",
|
|
)
|
|
|
|
if ylabel is not None:
|
|
if ylabel[0] == "$":
|
|
ylabel = ylabel[1:-1]
|
|
else:
|
|
ylabel = fr"\text{{ {ylabel} }}"
|
|
|
|
ax.set_ylabel(fr"$|{{{ylabel}}}_{{\mathrm{{ref}}}}-{{{ylabel}}}_{{N_i}}|$")
|