128 lines
4.7 KiB
Python
128 lines
4.7 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
plot_comparison.py — Figura comparativa entre dos dias de campo
|
|
(14 Sep vs 21 Sep 2025, Aguascalientes, UTC-6).
|
|
|
|
Cada dia se evalua contra su PROPIO techo de referencia P_ref(t)
|
|
(alineacion focal ideal, Ecuacion del techo de potencia), de modo que la
|
|
comparativa muestra la reproducion entre sesiones y no depende de un
|
|
dataset externo de control.
|
|
|
|
Lee los salidas ya generadas por parse_log.py:
|
|
articulo/resultados_<tag>/samples.csv (t, G_POA, P_CPV, P_ref, ...)
|
|
articulo/resultados_<tag>/metrics.json (E, H, E/H, eta_tr, MAE_P, ...)
|
|
y produce:
|
|
articulo/figs/fig_comparison_14sep_21sep.{pdf,png}
|
|
|
|
Uso: py -3.12 plot_comparison.py
|
|
(Requiere haber corrido antes: py -3.12 parse_log.py gyuggyhoy.txt
|
|
y py -3.12 parse_log.py 21092025.txt )
|
|
"""
|
|
import io
|
|
import os
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import numpy as np
|
|
import pandas as pd
|
|
import matplotlib
|
|
matplotlib.use("Agg")
|
|
import matplotlib.pyplot as plt
|
|
import matplotlib.dates as mdates
|
|
|
|
BASE = Path(__file__).resolve().parent
|
|
FIGS = BASE / "articulo" / "figs"
|
|
|
|
# (tag del parser = stem del log, etiqueta para la figura)
|
|
# 14 Sep esta representado por su captura B (gyuggyhoy); 21 Sep por la
|
|
# sesion completa que es la sesion "cabecera" del articulo.
|
|
DAYS = [
|
|
("gyuggyhoy", "14 September 2025 (capture B)"),
|
|
("21092025", "21 September 2025 (headline session)"),
|
|
]
|
|
|
|
|
|
def load(tag):
|
|
d = BASE / "articulo" / f"resultados_{tag}"
|
|
csv = d / "samples.csv"
|
|
if not csv.exists():
|
|
raise SystemExit(
|
|
f"Falta {csv}. Ejecuta antes: py -3.12 parse_log.py {tag}.txt")
|
|
df = pd.read_csv(csv, parse_dates=["timestamp"])
|
|
with open(d / "metrics.json", encoding="utf-8") as f:
|
|
m = json.load(f)
|
|
return df, m
|
|
|
|
|
|
def savefig_safe(fig, stem):
|
|
"""Renderiza en memoria y escribe en disco (delete-then-create).
|
|
Evita el bloqueo de archivo que OneDrive causa con savefig directo."""
|
|
FIGS.mkdir(parents=True, exist_ok=True)
|
|
for ext, kw in (("pdf", {}), ("png", {"dpi": 200})):
|
|
path = FIGS / f"{stem}.{ext}"
|
|
if path.exists():
|
|
os.remove(str(path))
|
|
buf = io.BytesIO()
|
|
fig.savefig(buf, format=ext, **kw)
|
|
buf.seek(0)
|
|
with open(str(path), "wb") as f:
|
|
f.write(buf.read())
|
|
print(" saved", path)
|
|
|
|
|
|
def main():
|
|
fig, axes = plt.subplots(len(DAYS), 1, figsize=(9.2, 6.6), dpi=200)
|
|
axes = np.atleast_1d(axes)
|
|
for ax, (tag, label) in zip(axes, DAYS):
|
|
df, m = load(tag)
|
|
fs = m.get("full_session") or {}
|
|
t = df["timestamp"].values
|
|
lit = (df["G_POA"].values > 50.0)
|
|
|
|
ax.plot(t, df["P_CPV"].values * 1000, color="tab:red", lw=0.9,
|
|
label="$P_{\\mathrm{CPV}}$ (tracked)")
|
|
ax.plot(t, df["P_ref"].values * 1000, color="k", ls="--", lw=1.0,
|
|
label="$P_{\\mathrm{ref}}$ (reference ceiling)")
|
|
ax.fill_between(t, df["P_CPV"].values * 1000,
|
|
df["P_ref"].values * 1000,
|
|
where=lit, color="tab:red", alpha=0.10,
|
|
label="Loss vs. $P_{\\mathrm{ref}}$")
|
|
ax2 = ax.twinx()
|
|
ax2.plot(t, df["G_POA"].values, color="tab:orange", lw=0.6,
|
|
alpha=0.6, label="$G_{\\mathrm{POA}}$")
|
|
ax2.set_ylabel("Irradiance $G_{\\mathrm{POA}}$ (W/m$^2$)",
|
|
color="tab:orange")
|
|
ax2.tick_params(axis="y", labelcolor="tab:orange")
|
|
|
|
ax.set_ylabel("Power (mW)", color="tab:red")
|
|
ax.tick_params(axis="y", labelcolor="tab:red")
|
|
ax.set_ylim(bottom=0)
|
|
ax.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M"))
|
|
ax.tick_params(axis="x", labelsize=8)
|
|
|
|
h1, l1 = ax.get_legend_handles_labels()
|
|
h2, l2 = ax2.get_legend_handles_labels()
|
|
ax.legend(h1 + h2, l1 + l2, loc="upper right", fontsize=7,
|
|
framealpha=0.6)
|
|
ax.grid(alpha=0.3)
|
|
|
|
ax.set_title(
|
|
f"{label} — $\\eta_{{tr}}$ = {fs.get('eta_tr_pct', 'n/a')}% "
|
|
f"MAE$_P$ = {fs.get('MAE_P_mW', 'n/a')} mW "
|
|
f"$E$ = {fs.get('E_Wh', 'n/a')} Wh "
|
|
f"$H$ = {fs.get('H_kWh_m2', 'n/a')} kWh/m$^2$",
|
|
fontsize=9.5)
|
|
|
|
# No global suptitle: it overflowed the figure width (clipped at both
|
|
# edges) and duplicated the LaTeX \caption. The per-panel titles below
|
|
# carry the per-day metrics; the article caption carries the description.
|
|
fig.tight_layout()
|
|
savefig_safe(fig, "fig_comparison_14sep_21sep")
|
|
plt.close(fig)
|
|
print("DONE ->", FIGS)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|