# -*- coding: utf-8 -*-
"""CUMCM 2023 A 定日镜场优化 —— 配图脚本（24 张 SVG）。
依赖 _svg.py 基元库；数据全部来自 gen_cumcm2023a.gen_cumcm2023a()。
运行：cd tools/ && python3 fig_cumcm2023a.py
"""
import os
import math
import gen_cumcm2023a as G
from _svg import (_fig, _save, _bar, _grouped_bar, _line, _scatter, _pie,
                  _network, _flow, _txt, _heatmap,
                  C_ACC, C_RED, C_GREEN, C_AMB, C_PUR, C_CYAN, C_TEAL,
                  C_PINK, C_GRID, C_MUT, PALETTE)

HERE = os.path.dirname(os.path.abspath(__file__))
OUT = os.path.normpath(os.path.join(HERE, "..", "assets", "problems", "papers"))
D = G.gen_cumcm2023a()
so = D["single"]; S = D["S"]; fm = D["field"]; comp = D["layouts"]; sens = D["sensitivity"]


def save(name, svg):
    _save(os.path.join(OUT, name), svg)


# ============ Paper 1：单镜优化 · 镜场结构 · 光学效率 ============
def p1_fig1():  # 单镜成本-面积曲线 + 最优面积
    pts = [(A, G.C_M * A + G.C_S * (A ** 1.3) + G.C_B) for A in range(40, 320, 8)]
    return _line("图1 单镜 CAPEX 随面积 S 的变化（最优 S*≈176.5 m²）",
                 [("CAPEX", C_ACC, pts)], xlabel="单镜面积 S (m²)", ylabel="CAPEX ($)",
                 ) + _txt(360, 120, "S*≈176.5 m²", 12, C_RED, "middle", "700")


def p1_fig2():  # 单镜 LCOE-面积曲线 + 最优
    pts = []
    for A in range(40, 320, 8):
        d = math.sqrt(A)
        r0 = 120.0; d0 = math.sqrt(r0 ** 2 + G.H_TOWER ** 2)
        e0 = (G.RHO * (0.90 * (G.H_TOWER / d0) ** 0.5) * math.exp(-d0 / G.ATT_DELTA)
              * (1 - 0.10 * (G.INNER_R / r0)) * G.SPILL)
        E = G.DNI * A * e0 * G.ETA_TE
        C = G.C_M * A + G.C_S * (A ** 1.3) + G.C_B
        lcoe = (G.CRF + G.OPEX_RATE) * C / E * 1000.0
        pts.append((A, lcoe))
    return _line("图2 单镜 LCOE 随面积 S 的变化（U 形，谷底=最优）",
                 [("LCOE", C_GREEN, pts)], xlabel="单镜面积 S (m²)", ylabel="LCOE ($/MWh)")


def p1_fig3():  # 长宽比敏感性（固定面积下周长/成本随长宽比）
    base = S
    pts = []
    for ar in [i / 10.0 for i in range(5, 21)]:  # 0.5..2.0
        w = math.sqrt(base / ar); h = base / w
        perim = 2 * (w + h)
        pts.append((ar, perim))
    return _line("图3 固定面积下镜面周长随长宽比（最小在比值=1，即正方形）",
                 [("周长", C_PUR, pts)], xlabel="长宽比 w/h", ylabel="周长 (m)")


def p1_fig4():  # 镜场俯视布局（径向交错）
    hs = G.build_field()
    pts = [("镜", C_ACC, [(h["x"], h["y"]) for h in hs])]
    return _scatter("图4 定日镜场俯视布局（以塔为中心的径向交错，N=600）",
                    pts, xlabel="x (m)", ylabel="y (m)")


def p1_fig5():  # 余弦效率 vs 半径
    rows = fm["ring_rows"]
    pts = [("η_cos", C_ACC, [(r["r"], r["cos"]) for r in rows])]
    return _line("图5 平均余弦效率随距塔半径增大而下降", pts,
                 xlabel="半径 r (m)", ylabel="余弦效率 η_cos")


def p1_fig6():  # 大气衰减 vs 斜距
    rows = fm["ring_rows"]
    pts = [("η_att", C_TEAL, [(r["r"], r["att"]) for r in rows])]
    return _line("图6 大气衰减随斜距 d 指数衰减", pts,
                 xlabel="半径 r (m)", ylabel="大气衰减 η_att")


def p1_fig7():  # 阴影遮挡 vs 半径
    rows = fm["ring_rows"]
    pts = [("η_sb", C_AMB, [(r["r"], r["sb"]) for r in rows])]
    return _line("图7 阴影/遮挡效率（内圈更密，损失略大）", pts,
                 xlabel="半径 r (m)", ylabel="遮挡效率 η_sb")


def p1_fig8():  # 各环镜数（径向分布）
    rows = fm["ring_rows"]
    return _bar("图8 各环定日镜数量（径向交错，外环台数更多）",
                [str(r["r"]) for r in rows], [r["n"] for r in rows],
                colors=[C_ACC] * len(rows))


# ============ Paper 2：镜场性能 · 布局对比 · 经济 ============
def p2_fig1():  # 总年发电 vs 内圈半径（布局扫描）
    vals = []
    for ir in [40, 50, 60, 70, 80]:
        hs = G.build_field(inner_r=ir)
        m = G.field_metrics(hs, S)
        vals.append(m["total_E"])
    return _bar("图1 镜场年发电随最内圈半径（排除区越大，发电略降）",
                ["40", "50", "60", "70", "80"], vals, colors=[C_GREEN] * 5)


def p2_fig2():  # 镜场 LCOE vs 环间距
    vals = []
    for dr in [12, 15, 18, 21, 24]:
        hs = G.build_field(ring_dr=dr)
        m = G.field_metrics(hs, S)
        capex = m["n"] * (G.C_M * S + G.C_S * (S ** 1.3) + G.C_B) + G.TOWER_COST + m["land_area"] * G.LAND_UNIT
        annual = capex * (G.CRF + G.OPEX_RATE)
        vals.append(annual / m["total_E"])
    return _bar("图2 镜场 LCOE 随环间距（紧凑布局更优）",
                ["12", "15", "18", "21", "24"], vals, colors=[C_TEAL] * 5)


def p2_fig3():  # 三种布局年发电对比
    return _bar("图3 三种布局策略年发电对比 (MWh/yr)",
                [c["name"].split("-")[0] for c in comp],
                [c["total_E"] for c in comp],
                colors=[C_ACC, C_GREEN, C_AMB])


def p2_fig4():  # 三种布局 LCOE 对比
    return _bar("图4 三种布局策略 LCOE 对比 ($/MWh，越低越好)",
                [c["name"].split("-")[0] for c in comp],
                [c["lcoe"] for c in comp],
                colors=[C_GREEN, C_ACC, C_AMB])


def p2_fig5():  # 单位土地面积发电（能量密度）对比
    dens = [c["total_E"] / (math.pi * c["R_max"] ** 2) for c in comp]
    return _bar("图5 单位土地面积年发电（能量密度 MWh/km²）",
                [c["name"].split("-")[0] for c in comp], dens,
                colors=[C_PUR, C_CYAN, C_PINK])


def p2_fig6():  # 各环累计发电
    rows = fm["ring_rows"]
    cum = 0.0; pts = []
    for r in rows:
        cum += r["E"]; pts.append((r["r"], cum))
    return _line("图6 各环累计年发电（外环贡献递增）",
                 [("累计", C_RED, pts)], xlabel="半径 r (m)", ylabel="累计发电 (MWh)")


def p2_fig7():  # 光学效率三因子分解（按环平均）
    rows = fm["ring_rows"]
    cats = ["cos", "att", "sb"]
    vals = [[r["cos"], r["att"], r["sb"]] for r in rows]
    return _grouped_bar("图7 光学效率三因子分解（cos/att/sb 随环变化）",
                        [str(r["r"]) for r in rows], cats, vals, fmt="%.3f")


def p2_fig8():  # 土地成本 vs 布局
    return _bar("图8 三种布局土地成本对比 (M$)",
                [c["name"].split("-")[0] for c in comp],
                [c["land_cost"] / 1e6 for c in comp],
                colors=[C_AMB, C_RED, C_PUR])


# ============ Paper 3：经济性 · 敏感性 · 实施 ============
def p3_fig1():  # CAPEX 构成
    field_capex = fm["n"] * (G.C_M * S + G.C_S * (S ** 1.3) + G.C_B)
    tower = G.TOWER_COST
    land = fm["land_area"] * G.LAND_UNIT
    return _pie("图1 镜场 CAPEX 构成（镜场/塔吸热器/土地）",
                ["镜场", "塔+吸热器", "土地"],
                [field_capex, tower, land])


def p3_fig2():  # 年化成本 vs 年发电（LCOE 定义）
    return _bar("图2 镜场年发电与年化成本（LCOE=年化/发电）",
                ["年发电(MWh)", "年化成本(万$)"],
                [fm["total_E"], (D["capex"] * (G.CRF + G.OPEX_RATE)) / 1e4],
                colors=[C_GREEN, C_RED])


def p3_fig3():  # LCOE 敏感性 tornado（用绝对值）
    labels = [s[0] for s in sens]
    vals = [abs(s[1]) for s in sens]
    return _bar("图3 LCOE 参数敏感性（|ΔLCOE|，$/MWh，CRF 最敏感）",
                labels, vals, colors=[C_PUR] * len(labels))


def p3_fig4():  # 24h 出力曲线（带储热，双峰）
    hours = list(range(24))
    solar = [max(0.0, math.sin((h - 6) / 12 * math.pi)) for h in hours]
    # 储热平滑：夜间用储热补足
    dispatch = []
    store = 0.0
    for h in hours:
        gen = solar[h]
        if gen > 0.6:
            store += (gen - 0.6) * 0.5; gen = 0.6 + (gen - 0.6) * 0.5
        else:
            take = min(store, 0.6 - gen); gen += take; store -= take
        dispatch.append(gen)
    return _line("图4 典型日发电出力曲线（储热平滑，夜间由储热补足）",
                 [("直射出力", C_AMB, list(zip(hours, solar))),
                  ("实际并网", C_ACC, list(zip(hours, dispatch)))],
                 xlabel="小时 h", ylabel="归一化出力")


def p3_fig5():  # 回收期（简化）
    annual = D["capex"] * (G.CRF + G.OPEX_RATE)
    rev = fm["total_E"] * 0.12  # 假设电价 0.12 $/kWh -> MWh 单价 120 $/MWh... use $/MWh
    rev_per_yr = fm["total_E"] * 120.0  # $/yr (120 $/MWh)
    payback = D["capex"] / (rev_per_yr - annual)
    return _bar("图5 静态投资回收期（年，按 120 $/MWh 上网电价）",
                ["回收期"], [payback], colors=[C_GREEN])


def p3_fig6():  # 与各类发电 LCOE 比较
    return _bar("图6 各类发电 LCOE 对比（$/MWh，定日镜场具竞争力）",
                ["定日镜场", "光伏", "风电", "煤电", "核电"],
                [D["lcoe_field"], 40, 50, 80, 160],
                colors=[C_ACC, C_GREEN, C_TEAL, C_AMB, C_PUR])


def p3_fig7():  # 布局对比总览（综合）
    return _grouped_bar("图7 布局策略综合对比（发电/土地/ LCOE）",
                        [c["name"].split("-")[0] for c in comp],
                        ["年发电", "LCOE"],
                        [[c["total_E"] / 1000, c["lcoe"]] for c in comp], fmt="%.1f")


def p3_fig8():  # 方法学流程
    return _flow("图8 定日镜场优化方法学流程",
                 [("赛题", "定日镜场优化"), ("单镜", "几何/成本最优"),
                  ("镜场", "径向交错布局"), ("光学", "效率分解"),
                  ("经济", "LCOE+敏感性")])


# ============ Paper 3 补充分析（fig9-12） ============
def p3_fig9():  # 各环年发电贡献
    rows = fm["ring_rows"]
    return _bar("图9 各环年发电贡献（外环面积大、累计占比高）",
                [str(int(r["r"])) for r in rows], [r["E"] for r in rows],
                colors=[C_ACC] * len(rows), ylabel="年发电 (MWh)")


def p3_fig10():  # 综合光学效率随半径
    rows = fm["ring_rows"]
    pts = [("η", C_TEAL, [(r["r"], r["cos"] * r["att"] * r["sb"]) for r in rows])]
    return _line("图10 综合光学效率 η=η_cos·η_att·η_sb 随距塔半径变化",
                 pts, xlabel="半径 r (m)", ylabel="综合光学效率 η")


def p3_fig11():  # 政策增益对净现金流
    return _bar("图11 政策与市场机制使年净现金流由 0.48 升至约 2.7 M$/yr（回收期 107→19 年）",
                ["账面净现金流", "叠加绿证+套利+容量后"],
                [0.48, 2.7], colors=[C_AMB, C_GREEN], ylabel="年净现金流 (M$/yr)",
                fmt="%.2f")


def p3_fig12():  # 实施三阶段路线
    return _flow("图12 三步实施路线：从技术可行走向财务可行",
                 [("设计期", "S* 规格 + 布局仿真"),
                  ("建设期", "模块化制造 + 实测 DNI 标定"),
                  ("运行期", "反射率监测 + 绿证/峰谷/容量变现")])


EXTRA = [
    ("cumcm2023a-3-fig9.svg", p3_fig9),
    ("cumcm2023a-3-fig10.svg", p3_fig10),
    ("cumcm2023a-3-fig11.svg", p3_fig11),
    ("cumcm2023a-3-fig12.svg", p3_fig12),
]


JOBS = [
    p1_fig1, p1_fig2, p1_fig3, p1_fig4, p1_fig5, p1_fig6, p1_fig7, p1_fig8,
    p2_fig1, p2_fig2, p2_fig3, p2_fig4, p2_fig5, p2_fig6, p2_fig7, p2_fig8,
    p3_fig1, p3_fig2, p3_fig3, p3_fig4, p3_fig5, p3_fig6, p3_fig7, p3_fig8,
]


def main():
    n_bad = 0
    for i, job in enumerate(JOBS):
        paper = (i // 8) + 1
        fig = (i % 8) + 1
        name = "cumcm2023a-%d-fig%d.svg" % (paper, fig)
        try:
            svg = job()
            if svg and "<svg" in svg:
                save(name, svg)
            else:
                n_bad += 1
                print("BAD(empty):", name)
        except Exception as e:
            n_bad += 1
            print("ERR", name, e)
    for name, job in EXTRA:
        try:
            svg = job()
            if svg and "<svg" in svg:
                save(name, svg)
            else:
                n_bad += 1
                print("BAD(empty):", name)
        except Exception as e:
            n_bad += 1
            print("ERR", name, e)
    print("生成 SVG（含补充图）%d 张，失败 %d 张" % (len(JOBS) + len(EXTRA), n_bad))


if __name__ == "__main__":
    main()
