电气研究 matplotlib 图表全集(20 类 × 5 例 = 100 张)
生成日期 2026-11-06 运行环境 Python 3.13.3 + matplotlib 3.10.9
版式遵循本机
matplotlib-plot-cn规范:中文宋体、西文 Times New Roman、数学符号 STIX 斜体;字号 10 磅;坐标轴线宽 0.5、数据线宽 1.0;刻度朝内且四边显示;图内不放标题;每张导出后做四边留白 ≥ 8 px 像素自检。
一、使用说明
所有图片由 code/ 下的四个脚本批量生成,每个文件对应一个坐标系、一个 PNG,不使用 subplots 拼板,便于单独插入论文或报告。
cd 电气图表全集_261106/code
python 一键重出261106.py # 全量重出 100 张并重建本文档
python 第三批_面积与场261106.py # 只重出某一批
数据全部为贴近广东实际量级的仿真算例(10 kV 配网、380 V 低压、50 Hz),随机数固定种子,重跑结果完全一致。
二、目录结构
电气图表全集_261106/
├── code/ 5 个脚本(common 公共配置 + p1~p4 四批)
├── docs/ 本文档
└── figures/ 20 个图型子目录,共 100 张 PNG(300 dpi,1181×944 px)
三、索引
| 目录 | 图型 | 5 个场景 |
|---|---|---|
| 01_折线 | 折线图 plot | 母线电压日曲线、馈线短路故障三相电流录波、典型日负荷曲线、光伏电站日出力、储能电站日充放电功率 |
| 02_散点 | 散点图 scatter | 配电变压器负载率与效率、光伏逆变器直流侧电压与输出功率、异步电机转速—电磁转矩特性散点、负荷有功—无功散点与功率因数参考线、锂电池荷电状态与开路电压 |
| 03_柱状 | 柱状图 bar | 母线电压各次谐波含有率、分时段用电量统计、各支路电流有效值对比、全年各月发电量、三种降损方案的年线损对比 |
| 04_条形 | 条形图 barh | 各馈线最大负载率、各次谐波电流幅值、各节点电压偏差百分数、主要电气设备额定容量、四种储能配置方案的年化费用构成对比 |
| 05_饼图 | 饼图 pie | 园区用电量构成、配电网线损构成、区域电源装机容量结构、谐波源贡献占比、变电站负荷类型占比 |
| 06_直方 | 直方图 hist | 日负荷功率分布直方图、母线电压偏差分布、风电场风速 Weibull 分布与实测频次对比、光伏功率短期预测误差分布、谐波电流幅值累积分布 |
| 07_箱线 | 箱线图 boxplot | 四季日负荷率箱线图、三条母线电压波动箱线图、四种降损方案的年线损率分布箱线图、各时段功率因数分布、不同负载率下变压器顶层油温升分布 |
| 08_小提琴 | 小提琴图 violinplot | 各月光伏出力分布小提琴图、各馈线电流分布小提琴图、四种储能调度策略的效率分布、四季风速分布小提琴图、三相电压不平衡度分布对比 |
| 09_误差棒 | 误差棒图 errorbar | 实测与仿真节点电压对比、变压器不同负载率下的效率测量值与标准差、各次谐波电流测量值与不确定度、电缆接头温度测量值随环境温度变化、短期负荷预测各时段误差 |
| 10_填充带 | 填充带 fill_between | 光伏出力预测区间、母线负荷预测置信带、节点电压运行范围与实测曲线、储能荷电状态运行区间与上下限、系统频率偏差与一次调频死区 |
| 11_阶梯 | 阶梯图 step | 分时电价阶梯曲线、变压器分接头档位日调节过程、储能功率指令阶梯化与荷电状态响应、无功补偿装置投切档位、负荷分级投切 |
| 12_堆叠面积 | 堆叠面积 stackplot | 日电源出力构成、工业用户日负荷构成、配电网线损构成随负荷变化、谐波电流各次分量随时间的叠加、多能互补系统供能构成 |
| 13_热图 | 热图 imshow | 月—时负荷矩阵热图、谐波时频图、各节点各时段电压偏差矩阵、电气量相关系数矩阵热图、日负荷灰度图 |
| 14_等值线 | 等值线 contour / contourf | 电机效率 MAP 等值线、开关柜内部温度场分布、接地网地表电位分布、电缆终端电场强度分布、变压器铁心损耗分布等值线 |
| 15_网格图 | 网格图 pcolormesh | 非均匀网格下母线室温度场、时频能量分布、供电分区负荷密度网格图、风电场风功率密度分布、绝缘子表面电位分布 |
| 16_频谱 | 频谱 psd / magnitude_spectrum | 母线电压谐波功率谱、变频器输出电流谐波频谱、异步电机振动加速度频谱、风电功率波动的功率谱密度、并网逆变器开关频率附近噪声频谱 |
| 17_向量场 | 向量场 quiver | 双极板间电场强度矢量场、单根载流导线周围磁场矢量场、配电网潮流方向与有功流动、风电场轮毂高度风速矢量场、接地网附近地表电流密度矢量 |
| 18_极坐标 | 极坐标 polar | 三相电压相量图、功率因数随负荷变化的极坐标圆图、风向玫瑰图、变电站局部放电超声阵列方向响应、谐波电流极坐标分布 |
| 19_三维曲面 | 三维曲面 plot_surface | 电机效率 MAP 三维曲面、光伏组件 P-V 特性曲面、母线室温度分布三维曲面、LC 滤波器幅频响应曲面、锂电池放电容量特性 |
| 20_三维柱状 | 三维柱状 bar3d | 各月各时段负荷三维柱状图、各节点各次谐波幅值三维柱状图、三种方案三项指标对比三维柱状图、各馈线四季负载率三维柱状图、各设备容量五年增长三维柱状图 |
四、逐图说明
01 折线 折线图 plot
母线电压日曲线:三条馈线 24 h 电压

馈线短路故障三相电流录波

典型日负荷曲线:工作日、周末、节假日

光伏电站日出力:晴、多云、阴三种天气

储能电站日充放电功率(正为放电、负为充电)

02 散点 散点图 scatter
配电变压器负载率与效率

光伏逆变器直流侧电压与输出功率

异步电机转速—电磁转矩特性散点

负荷有功—无功散点与功率因数参考线

锂电池荷电状态与开路电压

03 柱状 柱状图 bar
母线电压各次谐波含有率

分时段用电量统计(纵向柱,峰值时段高亮)

各支路电流有效值对比

全年各月发电量

三种降损方案的年线损对比

04 条形 条形图 barh
各馈线最大负载率

各次谐波电流幅值(对数横轴)

各节点电压偏差百分数

主要电气设备额定容量

四种储能配置方案的年化费用构成对比(堆叠条形)

05 饼图 饼图 pie
园区用电量构成

配电网线损构成

区域电源装机容量结构

谐波源贡献占比

变电站负荷类型占比

06 直方 直方图 hist
日负荷功率分布直方图(双峰:白天与晚高峰)

母线电压偏差分布(正态,含合格区间)

风电场风速 Weibull 分布与实测频次对比

光伏功率短期预测误差分布(叠加零均值参考线)

谐波电流幅值累积分布(按不同监测点分层)

07 箱线 箱线图 boxplot
四季日负荷率箱线图

三条母线电压波动箱线图(含离群点)

四种降损方案的年线损率分布箱线图

各时段功率因数分布

不同负载率下变压器顶层油温升分布

08 小提琴 小提琴图 violinplot
各月光伏出力分布小提琴图

各馈线电流分布小提琴图(含中位数)

四种储能调度策略的效率分布

四季风速分布小提琴图

三相电压不平衡度分布对比

09 误差棒 误差棒图 errorbar
实测与仿真节点电压对比(含测量误差棒)

变压器不同负载率下的效率测量值与标准差

各次谐波电流测量值与不确定度(对数纵轴)

电缆接头温度测量值随环境温度变化(三测点)

短期负荷预测各时段误差(MAPE 与置信区间)

10 填充带 填充带 fill_between
光伏出力预测区间(P10~P90 带 + 预测中值)

母线负荷预测置信带

节点电压运行范围与实测曲线(GB/T 12325 允许偏差带)

储能荷电状态运行区间与上下限

系统频率偏差与一次调频死区

11 阶梯 阶梯图 step
分时电价阶梯曲线

变压器分接头档位日调节过程

储能功率指令阶梯化与荷电状态响应

无功补偿装置投切档位(累计投入容量)

负荷分级投切:可中断负荷容量阶梯变化

12 堆叠面积 堆叠面积 stackplot
日电源出力构成(火电、光伏、风电、储能)

工业用户日负荷构成

配电网线损构成随负荷变化

谐波电流各次分量随时间的叠加

多能互补系统供能构成(电、热、冷)

13 热图 热图 imshow
月—时负荷矩阵热图

谐波时频图(次数—时间)

各节点各时段电压偏差矩阵

电气量相关系数矩阵热图

日负荷灰度图(一周 7 天)

14 等值线 等值线 contour / contourf
电机效率 MAP 等值线

开关柜内部温度场分布

接地网地表电位分布

电缆终端电场强度分布

变压器铁心损耗分布等值线

15 网格图 网格图 pcolormesh
非均匀网格下母线室温度场

时频能量分布(非均匀频率轴)

供电分区负荷密度网格图

风电场风功率密度分布

绝缘子表面电位分布(不规则极坐标网格映射)

16 频谱 频谱 psd / magnitude_spectrum
母线电压谐波功率谱(含 3/5/7 次谐波)

变频器输出电流谐波频谱(柱状对数纵轴)

异步电机振动加速度频谱(含转频与轴承故障频率)

风电功率波动的功率谱密度

并网逆变器开关频率附近噪声频谱

17 向量场 向量场 quiver
双极板间电场强度矢量场

单根载流导线周围磁场矢量场

配电网潮流方向与有功流动

风电场轮毂高度风速矢量场

接地网附近地表电流密度矢量

18 极坐标 极坐标 polar
三相电压相量图

功率因数随负荷变化的极坐标圆图

风向玫瑰图(16 方位风速频率)

变电站局部放电超声阵列方向响应

谐波电流极坐标分布(次数—幅值)

19 三维曲面 三维曲面 plot_surface
电机效率 MAP 三维曲面

光伏组件 P-V 特性曲面(不同辐照度与温度)

母线室温度分布三维曲面

LC 滤波器幅频响应曲面

锂电池放电容量特性:倍率与温度的影响

20 三维柱状 三维柱状 bar3d
各月各时段负荷三维柱状图

各节点各次谐波幅值三维柱状图

三种方案三项指标对比三维柱状图

各馈线四季负载率三维柱状图

各设备容量五年增长三维柱状图

五、完整代码
5.1 公共配置261106.py(样式与导出工具)
统一引入 plot_cn 规范库、定义路径与命名规则、封装导出与双自检、补齐 IT 字符表的大写斜体字母与常用 Unicode 下标。
# -*- coding: utf-8 -*-
"""电气图表全集 —— 公共配置(2026-11-06)
职责:
1) 引入本机绘图规范库 plot_cn(宋体+Times+STIX、10 磅、刻度朝内四边、图内无标题);
2) 统一路径常量与文件命名(<序号>_<图型>_<场景>261106.png);
3) 统一导出 + 双重自检:四边留白 >=8px(save_checked)+ 缺字形告警拦截。
运行要求:本文件所在目录即 code/,data/figures 在其上一级,脚本必须在 code/ 下执行。
"""
from __future__ import annotations
import inspect
import sys
import warnings
from pathlib import Path
import numpy as np
HERE = Path(__file__).resolve().parent
ROOT = HERE.parent
FIG = ROOT / "figures"
DATE = "261106"
SKILL_SCRIPTS = Path.home() / ".workbuddy" / "skills" / "matplotlib-plot-cn" / "scripts"
sys.path.insert(0, str(SKILL_SCRIPTS))
import matplotlib.pyplot as plt # noqa: E402
from plot_cn import ( # noqa: E402
FS,
IT,
new_fig,
save_checked,
setup_cn_style,
style_legend,
)
setup_cn_style()
# ---------- 补齐 plot_cn.IT 未收录的大写斜体字母 ----------
# IT 原表只给到 V/I/P/Q/R/T/U,电气符号还常用 E(电量)、S(视在功率)、A/B/C、W(能量)。
# 数学斜体大写区 U+1D434(A) ~ U+1D44D(Z) 连续无空缺,STIXGeneral 提供字形。
IT.update({chr(ord("A") + i): chr(0x1D434 + i) for i in range(26)})
# 常用 Unicode 下标(供 h 次谐波、dc 等写法使用)
SUB = {"h": "ₕ", "0": "₀", "1": "₁", "2": "₂", "3": "₃", "4": "₄",
"5": "₅", "6": "₆", "7": "₇", "8": "₈", "9": "₉",
"d": "ᵈ", "c": "ᶜ", "max": "ₘₐₓ", "min": "ₘᵢₙ", "ref": "ᵣₑf"}
# ---------- 电气绘图色板(规避红绿同现,蓝/红/橙/灰) ----------
C1 = "#2E5FA3" # 主色 深蓝
C2 = "#C0392B" # 次色 砖红
C3 = "#E08A1E" # 第三 橙
C4 = "#555555" # 第四 深灰
C5 = "#7FA7D9" # 浅蓝(填充)
C6 = "#9E9E9E" # 浅灰
COLORS = [C1, C2, C3, C4]
MARKERS = ["o", "s", "^", "D"]
LINES = ["-", "--", "-.", ":"]
# 5 个场景共用的柔和色(饼图/堆叠面积用)
PALETTE5 = ["#2E5FA3", "#7FA7D9", "#E08A1E", "#C0392B", "#9E9E9E"]
def mkhash(*args) -> np.random.Generator:
"""固定种子的随机数发生器,保证每次重跑图形完全一致。"""
return np.random.default_rng(abs(hash(args)) % (2 ** 32))
# 导出登记表:供 一键重出261106.py 自动生成文档(folder/文件基名/一句话说明)
RECORD: list[dict] = []
def save(fig, folder: str, filename: str) -> Path:
"""导出到 figures/<folder>/<filename>261106.png,并做留白 + 缺字形双自检。"""
path = FIG / folder / f"{filename}{DATE}.png"
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
save_checked(fig, path)
bad = [str(w.message) for w in caught if "glyph" in str(w.message).lower()]
if bad:
raise RuntimeError(f"[缺字形] {path.name} -> {bad[:3]}")
stack = inspect.stack()
caller = stack[1]
func = caller.frame.f_globals.get(caller.function)
doc = (func.__doc__ or "").strip().splitlines()[0] if func else ""
src = inspect.getsource(func).strip() if func else ""
del stack, caller
RECORD.append({"folder": folder, "base": f"{filename}{DATE}",
"desc": doc, "src": src})
return path
def new_fig3d(w_mm: float = 100, h_mm: float = 95, right: float = 0.94,
left: float = 0.06):
"""三维坐标区画布(plot_cn 的 new_fig 不支持 projection)。
带色条时把 right 调到 0.86;z 轴标签落到左侧时把 left 调到 0.13。
"""
fig = plt.figure(figsize=(w_mm / 25.4, h_mm / 25.4))
ax = fig.add_subplot(111, projection="3d")
fig.subplots_adjust(left=left, bottom=0.10, right=right, top=0.98)
ax.tick_params(labelsize=FS - 1, pad=1, direction="in")
for axis in (ax.xaxis, ax.yaxis, ax.zaxis):
axis.pane.set_facecolor("white")
axis.pane.set_edgecolor("0.55")
axis.pane.set_alpha(1.0)
axis.line.set_linewidth(0.5)
axis._axinfo["grid"]["linewidth"] = 0.3
axis._axinfo["grid"]["color"] = (0.75, 0.75, 0.75, 1.0)
return fig, ax
def legend(ax, loc="upper right", ncol=1, fontsize=FS - 1):
"""统一图例:半透明、细边框、置于坐标区内。"""
lg = ax.legend(loc=loc, ncol=ncol, fontsize=fontsize, framealpha=0.60,
edgecolor="0.6", borderpad=0.3, handlelength=2.2,
labelspacing=0.3, handletextpad=0.5)
return style_legend(lg)
5.2 第一批_基础图型261106.py
# -*- coding: utf-8 -*-
"""第一批:折线、散点、柱状、条形、饼图(各 5 个电气场景,共 25 张)
执行方式:在本 code/ 目录下运行
python 第一批_基础图型261106.py
"""
from 公共配置261106 import (
C1, C2, C3, C4, C5, C6, COLORS, MARKERS, LINES, IT, SUB,
FS, np, new_fig, legend, save, PALETTE5,
)
import matplotlib.pyplot as plt
# ==================== 1. 折线图 plot ====================
def line_01():
"""母线电压日曲线:三条馈线 24 h 电压。"""
fig, ax = new_fig()
t = np.arange(0, 24.01, 0.5)
u1 = 10.15 + 0.16 * np.sin((t - 6) / 24 * 2 * np.pi) + 0.02 * np.cos(t)
u2 = 10.02 + 0.11 * np.sin((t - 5) / 24 * 2 * np.pi)
u3 = 9.93 + 0.09 * np.sin((t - 7) / 24 * 2 * np.pi) - 0.02 * np.cos(t / 2)
for i, (y, nm) in enumerate([(u1, "Ⅰ 段母线"), (u2, "Ⅱ 段母线"), (u3, "Ⅲ 段母线")]):
ax.plot(t, y, color=COLORS[i], ls=LINES[i], marker=MARKERS[i],
markevery=6, ms=3, lw=1.0, label=nm)
ax.set_xlabel("时间 " + IT["t"] + " / h")
ax.set_ylabel("母线电压 " + IT["U"] + " / kV")
ax.set_xlim(0, 24)
ax.set_ylim(9.74, 10.66)
legend(ax, "upper center", ncol=3)
save(fig, "01_折线", "01_折线_母线电压日曲线")
def line_02():
"""馈线短路故障三相电流录波。"""
fig, ax = new_fig()
t = np.linspace(0, 100, 600)
base = 380 * np.sin(2 * np.pi * 50 * t / 1000)
for i in range(3):
sig = base.copy()
mask = t > 40
sig[mask] = 380 * 2.8 * np.sin(2 * np.pi * 50 * t[mask] / 1000 - i * 2.09) \
* np.exp(-(t[mask] - 40) / 55)
ax.plot(t, sig, color=COLORS[i], ls=LINES[i], lw=1.0, label=f"{'ABC'[i]} 相")
ax.axvline(40, color=C4, lw=0.5, ls=":", label="故障时刻")
ax.set_xlabel("时间 " + IT["t"] + " / ms")
ax.set_ylabel("相电流 " + IT["I"] + " / A")
ax.set_xlim(0, 100)
ax.set_ylim(-1450, 2100)
legend(ax, "upper center", ncol=2)
save(fig, "01_折线", "01_折线_馈线故障电流录波")
def line_03():
"""典型日负荷曲线:工作日、周末、节假日。"""
fig, ax = new_fig()
t = np.arange(0, 24.01, 1.0)
work = 520 + 190 * np.exp(-((t - 10) / 3.2) ** 2) + 240 * np.exp(-((t - 15) / 2.6) ** 2)
week = 430 + 110 * np.exp(-((t - 11) / 3.6) ** 2) + 130 * np.exp(-((t - 16) / 3.0) ** 2)
holi = 360 + 70 * np.exp(-((t - 12) / 4.2) ** 2) + 60 * np.exp(-((t - 18) / 3.4) ** 2)
for i, (y, nm) in enumerate([(work, "工作日"), (week, "周末"), (holi, "节假日")]):
ax.plot(t, y, color=COLORS[i], ls=LINES[i], marker=MARKERS[i],
markevery=3, ms=3, lw=1.0, label=nm)
ax.set_xlabel("时间 " + IT["t"] + " / h")
ax.set_ylabel("有功负荷 " + IT["P"] + " / kW")
ax.set_xlim(0, 24)
ax.set_ylim(300, 900)
legend(ax, "upper center", ncol=3)
save(fig, "01_折线", "01_折线_典型日负荷曲线")
def line_04():
"""光伏电站日出力:晴、多云、阴三种天气。"""
fig, ax = new_fig()
t = np.arange(5, 20.01, 0.5)
clear = 1000 * np.sin((t - 5) / 15 * np.pi) ** 1.35
cloud = clear * (0.55 + 0.35 * np.sin(t * 1.3))
over = clear * 0.22
for i, (y, nm) in enumerate([(clear, "晴天"), (cloud, "多云"), (over, "阴天")]):
ax.plot(t, y, color=COLORS[i], ls=LINES[i], marker=MARKERS[i],
markevery=4, ms=3, lw=1.0, label=nm)
ax.set_xlabel("时间 " + IT["t"] + " / h")
ax.set_ylabel("有功出力 " + IT["P"] + " / kW")
ax.set_xlim(5, 20)
ax.set_ylim(-60, 1300)
legend(ax, "upper center", ncol=3)
save(fig, "01_折线", "01_折线_光伏电站日出力")
def line_05():
"""储能电站日充放电功率(正为放电、负为充电)。"""
fig, ax = new_fig()
t = np.arange(0, 24.01, 0.5)
p = np.where((t >= 1) & (t < 7), -180, 0) + np.where((t >= 18) & (t < 22), 150, 0)
p = p + 12 * np.sin(t / 2)
ax.plot(t, p, color=C1, lw=1.0, marker="o", markevery=6, ms=3, label="储能功率")
ax.axhline(0, color=C4, lw=0.5)
ax.fill_between(t, p, 0, where=p > 0, color=C3, alpha=0.35, linewidth=0, label="放电")
ax.fill_between(t, p, 0, where=p < 0, color=C5, alpha=0.45, linewidth=0, label="充电")
ax.set_xlabel("时间 " + IT["t"] + " / h")
ax.set_ylabel("有功功率 " + IT["P"] + " / kW")
ax.set_xlim(0, 24)
ax.set_ylim(-250, 290)
legend(ax, "upper center", ncol=3)
save(fig, "01_折线", "01_折线_储能日充放电功率")
# ==================== 2. 散点图 scatter ====================
def scatter_01():
"""配电变压器负载率与效率。"""
fig, ax = new_fig()
rng = np.random.default_rng(11)
beta = np.concatenate([np.linspace(0.05, 1.0, 26), np.linspace(0.05, 1.0, 26)])
eff = 98.72 - 0.62 * (1 / (0.32 + beta) - 0.75) - 0.42 * beta ** 2 + rng.normal(0, 0.035, 52)
ax.scatter(beta, eff, s=16, color=C1, edgecolors="white", linewidths=0.3, label="实测样本")
xx = np.linspace(0.05, 1.0, 200)
yy = 98.72 - 0.62 * (1 / (0.32 + xx) - 0.75) - 0.42 * xx ** 2
ax.plot(xx, yy, color=C2, ls="--", lw=1.0, label="拟合曲线")
ax.set_xlabel("负载率 " + IT["beta"])
ax.set_ylabel("效率 " + IT["eta"] + " / %")
ax.set_xlim(0, 1.05)
ax.margins(y=0.20)
legend(ax, "lower right", ncol=1)
save(fig, "02_散点", "02_散点_变压器负载率与效率")
def scatter_02():
"""光伏逆变器直流侧电压与输出功率。"""
fig, ax = new_fig()
rng = np.random.default_rng(22)
u = rng.uniform(560, 820, 90)
p = 500 * np.clip(1 - ((u - 700) / 130) ** 2, 0.15, 1) + rng.normal(0, 18, 90)
sc = ax.scatter(u, p, c=p, cmap="Blues", s=22, edgecolors="0.4", linewidths=0.3)
cb = fig.colorbar(sc, ax=ax, pad=0.02, fraction=0.05)
cb.set_label("输出功率 " + IT["P"] + " / kW")
cb.ax.tick_params(direction="in", labelsize=FS - 2)
cb.outline.set_linewidth(0.5)
ax.set_xlabel("直流母线电压 " + IT["U"] + "dc / V")
ax.set_ylabel("交流输出功率 " + IT["P"] + " / kW")
ax.set_xlim(540, 840)
ax.margins(y=0.16)
fig.subplots_adjust(right=0.90)
save(fig, "02_散点", "02_散点_逆变器电压与功率")
def scatter_03():
"""异步电机转速—电磁转矩特性散点。"""
fig, ax = new_fig()
rng = np.random.default_rng(33)
n = np.linspace(400, 1480, 40)
tq = 2.1 * (1 - ((n - 1450) / 700) ** 2)
for i, (off, nm) in enumerate([(0, "额定电压"), (-0.18, "0.9 倍电压"), (-0.34, "0.8 倍电压")]):
ax.scatter(n, tq + off + rng.normal(0, 0.03, 40), s=14, marker=MARKERS[i],
color=COLORS[i], label=nm, linewidths=0.3, edgecolors="white")
ax.set_xlabel("转速 " + IT["n"] + " / (r/min)")
ax.set_ylabel("电磁转矩 " + IT["T"] + " / (N·m)")
ax.set_xlim(350, 1550)
ax.margins(y=0.24)
legend(ax, "lower left", ncol=1)
save(fig, "02_散点", "02_散点_电机转速转矩特性")
def scatter_04():
"""负荷有功—无功散点与功率因数参考线。"""
fig, ax = new_fig()
rng = np.random.default_rng(44)
p = rng.uniform(120, 900, 80)
q = p * rng.uniform(0.25, 0.55, 80) + rng.normal(0, 22, 80)
ax.scatter(p, q, s=16, color=C1, alpha=0.85, edgecolors="white", linewidths=0.3,
label="采样点")
xx = np.linspace(100, 950, 50)
for i, (npf, ls) in enumerate([(0.90, "-"), (0.95, "--")]):
ax.plot(xx, xx * np.tan(np.arccos(npf)), color=[C2, C3][i], ls=ls, lw=0.8,
label="功率因数 " + f"{npf:.2f}")
ax.set_xlabel("有功功率 " + IT["P"] + " / kW")
ax.set_ylabel("无功功率 " + IT["Q"] + " / kvar")
ax.set_xlim(100, 950)
ax.margins(y=0.16)
legend(ax, "upper left", ncol=1)
save(fig, "02_散点", "02_散点_有功无功与功率因数")
def scatter_05():
"""锂电池荷电状态与开路电压。"""
fig, ax = new_fig()
soc = np.linspace(0, 100, 60)
ocv = 3.05 + 0.22 * (soc / 100) + 0.42 / (1 + np.exp(-(soc - 62) / 8))
rng = np.random.default_rng(55)
for i, (cur, nm) in enumerate([(0.0, "静置"), (0.5, "充电 0.5C"), (-0.5, "放电 0.5C")]):
ax.scatter(soc, ocv + cur * 0.09 + rng.normal(0, 0.006, 60), s=13,
marker=MARKERS[i], color=COLORS[i], label=nm,
linewidths=0.3, edgecolors="white")
ax.set_xlabel("荷电状态 SOC / %")
ax.set_ylabel("开路电压 " + IT["U"] + "oc / V")
ax.set_xlim(0, 100)
ax.margins(y=0.22)
legend(ax, "upper left", ncol=1)
save(fig, "02_散点", "02_散点_锂电池SOC与开路电压")
# ==================== 3. 柱状图 bar ====================
def bar_01():
"""母线电压各次谐波含有率。"""
fig, ax = new_fig()
h = [1, 3, 5, 7, 9, 11, 13, 15]
u = [100, 3.8, 5.2, 3.1, 1.6, 1.1, 0.7, 0.4]
bars = ax.bar(range(len(h)), u, width=0.62, color=C1, edgecolor="0.35", linewidth=0.5)
for b, v in zip(bars, u):
ax.text(b.get_x() + b.get_width() / 2, v + 1.6, f"{v:g}", ha="center",
va="bottom", fontsize=FS - 2)
ax.set_xticks(range(len(h)))
ax.set_xticklabels([f"{i}" for i in h])
ax.set_xlabel("谐波次数 " + IT["h"])
ax.set_ylabel("电压含有率 / %")
ax.set_ylim(0, 118)
save(fig, "03_柱状", "03_柱状_电压谐波含有率")
def bar_02():
"""分时段用电量统计(纵向柱,峰值时段高亮)。"""
fig, ax = new_fig()
seg = ["0-4", "4-8", "8-12", "12-16", "16-20", "20-24"]
e = [86, 152, 268, 234, 291, 140]
cols = [C5] * 6
cols[4] = C2
bars = ax.bar(range(len(seg)), e, width=0.62, color=cols, edgecolor="0.35",
linewidth=0.5)
for b, v in zip(bars, e):
ax.text(b.get_x() + b.get_width() / 2, v + 6, f"{v}", ha="center",
va="bottom", fontsize=FS - 2)
ax.set_xticks(range(len(seg)))
ax.set_xticklabels([s + " 时" for s in seg])
ax.set_xlabel("时段")
ax.set_ylabel("用电量 " + IT["E"] + " / (MW·h)")
ax.set_ylim(0, 350)
save(fig, "03_柱状", "03_柱状_分时段用电量")
def bar_03():
"""各支路电流有效值对比。"""
fig, ax = new_fig()
br = ["L1", "L2", "L3", "L4", "L5", "L6"]
a = [312, 268, 401, 175, 356, 224]
b = [298, 251, 388, 180, 340, 231]
x = np.arange(len(br))
ax.bar(x - 0.19, a, width=0.36, color=C1, edgecolor="0.35", linewidth=0.5,
label="改造前")
ax.bar(x + 0.19, b, width=0.36, color=C3, edgecolor="0.35", linewidth=0.5,
label="改造后")
ax.set_xticks(x)
ax.set_xticklabels(br)
ax.set_xlabel("支路")
ax.set_ylabel("电流有效值 " + IT["I"] + " / A")
ax.set_ylim(0, 470)
legend(ax, "upper right", ncol=2)
save(fig, "03_柱状", "03_柱状_各支路电流对比")
def bar_04():
"""全年各月发电量。"""
fig, ax = new_fig()
m = np.arange(1, 13)
gen = [182, 196, 264, 318, 402, 438, 486, 472, 388, 296, 214, 178]
cols = [C5 if v < 300 else C1 for v in gen]
bars = ax.bar(m, gen, width=0.66, color=cols, edgecolor="0.35", linewidth=0.5)
ax.plot(m, np.full_like(m, np.mean(gen), dtype=float), color=C2, ls="--", lw=1.0,
label="年平均 " + f"{np.mean(gen):.0f}")
for b, v in zip(bars, gen):
if v == max(gen):
ax.text(b.get_x() + b.get_width() / 2, v + 8, f"{v}", ha="center",
va="bottom", fontsize=FS - 2)
ax.set_xticks(m)
ax.set_xticklabels([f"{i}" for i in m])
ax.set_xlabel("月份")
ax.set_ylabel("发电量 " + IT["E"] + " / (GW·h)")
ax.set_ylim(120, 560)
legend(ax, "upper left", ncol=1)
save(fig, "03_柱状", "03_柱状_全年各月发电量")
def bar_05():
"""三种降损方案的年线损对比。"""
fig, ax = new_fig()
plan = ["方案一", "方案二", "方案三"]
before = [4.62, 4.62, 4.62]
after = [3.71, 3.24, 2.86]
x = np.arange(len(plan))
ax.bar(x - 0.18, before, width=0.34, color=C6, edgecolor="0.35", linewidth=0.5,
label="改造前")
ax.bar(x + 0.18, after, width=0.34, color=C1, edgecolor="0.35", linewidth=0.5,
label="改造后")
for xi, v in zip(x + 0.18, after):
ax.text(xi, v + 0.12, f"{v:.2f}", ha="center", va="bottom", fontsize=FS - 2)
ax.set_xticks(x)
ax.set_xticklabels(plan)
ax.set_xlabel("降损方案")
ax.set_ylabel("年线损率 / %")
ax.set_ylim(0, 5.6)
legend(ax, "upper right", ncol=1)
save(fig, "03_柱状", "03_柱状_三方案年线损对比")
# ==================== 4. 条形图 barh ====================
def barh_01():
"""各馈线最大负载率。"""
fig, ax = new_fig(left=0.24)
f = ["城东", "城西", "城南", "城北", "开发区", "新区"]
r = [86, 72, 94, 61, 103, 78]
cols = [C2 if v > 100 else C1 for v in r]
bars = ax.barh(range(len(f)), r, height=0.58, color=cols, edgecolor="0.35",
linewidth=0.5)
ax.axvline(100, color=C4, lw=0.6, ls=":", label="满载线")
for b, v in zip(bars, r):
ax.text(v + 1.5, b.get_y() + b.get_height() / 2, f"{v}", va="center",
fontsize=FS - 2)
ax.set_yticks(range(len(f)))
ax.set_yticklabels(f)
ax.set_ylabel("馈线")
ax.set_xlabel("最大负载率 / %")
ax.set_xlim(0, 118)
legend(ax, "lower right", ncol=1)
save(fig, "04_条形", "04_条形_各馈线最大负载率")
def barh_02():
"""各次谐波电流幅值(对数横轴)。"""
fig, ax = new_fig(left=0.20)
h = ["2", "3", "4", "5", "6", "7", "9", "11", "13"]
a = [4.2, 38.6, 2.1, 26.4, 1.3, 15.8, 6.2, 3.4, 1.8]
bars = ax.barh(range(len(h)), a, height=0.6, color=C5, edgecolor="0.35",
linewidth=0.5)
for i in (1, 3):
bars[i].set_color(C1)
for b, v in zip(bars, a):
ax.text(v * 1.08, b.get_y() + b.get_height() / 2, f"{v:g}", va="center",
fontsize=FS - 2)
ax.set_xscale("log")
ax.set_xlim(0.8, 90)
ax.set_yticks(range(len(h)))
ax.set_yticklabels(h)
ax.invert_yaxis()
ax.set_ylabel("谐波次数 " + IT["h"])
ax.set_xlabel("谐波电流 " + IT["I"] + SUB["h"] + " / A")
save(fig, "04_条形", "04_条形_谐波电流幅值")
def barh_03():
"""各节点电压偏差百分数。"""
fig, ax = new_fig(left=0.19)
n = ["N1", "N2", "N3", "N4", "N5", "N6", "N7"]
d = [2.1, -1.4, 3.6, -4.2, 0.8, 5.1, -2.7]
cols = [C1 if v >= 0 else C3 for v in d]
bars = ax.barh(range(len(n)), d, height=0.56, color=cols, edgecolor="0.35",
linewidth=0.5)
ax.axvline(0, color=C4, lw=0.6)
ax.axvline(7, color=C2, lw=0.6, ls="--", label="上限 +7%")
ax.axvline(-7, color=C2, lw=0.6, ls="--", label="下限 -7%")
for b, v in zip(bars, d):
ax.text(v + (0.35 if v >= 0 else -0.35), b.get_y() + b.get_height() / 2,
f"{v:+.1f}", va="center",
ha="left" if v >= 0 else "right", fontsize=FS - 2)
ax.set_yticks(range(len(n)))
ax.set_yticklabels(n)
ax.set_ylabel("节点")
ax.set_xlabel("电压偏差 / %")
ax.set_xlim(-9, 9)
legend(ax, "lower right", ncol=1)
save(fig, "04_条形", "04_条形_各节点电压偏差")
def barh_04():
"""主要电气设备额定容量。"""
fig, ax = new_fig(left=0.30)
d = ["主变 T1", "主变 T2", "储能 PCS", "SVG", "光伏逆变器", "柴油机"]
c = [50, 40, 10, 6, 5, 2]
bars = ax.barh(range(len(d)), c, height=0.58, color=C1, edgecolor="0.35",
linewidth=0.5)
for b, v in zip(bars, c):
ax.text(v + 0.9, b.get_y() + b.get_height() / 2, f"{v}", va="center",
fontsize=FS - 2)
ax.set_yticks(range(len(d)))
ax.set_yticklabels(d)
ax.set_ylabel("设备")
ax.set_xlabel("额定容量 " + IT["S"] + " / (MV·A)")
ax.set_xlim(0, 58)
save(fig, "04_条形", "04_条形_主要设备额定容量")
def barh_05():
"""四种储能配置方案的年化费用构成对比(堆叠条形)。"""
fig, ax = new_fig(left=0.21)
plan = ["方案 A", "方案 B", "方案 C", "方案 D"]
inv = np.array([420, 365, 318, 402])
ope = np.array([88, 96, 112, 82])
pen = np.array([46, 32, 21, 58])
ax.barh(range(len(plan)), inv, height=0.55, color=C1, edgecolor="0.35",
linewidth=0.5, label="投资摊销")
ax.barh(range(len(plan)), ope, left=inv, height=0.55, color=C3,
edgecolor="0.35", linewidth=0.5, label="运行维护")
ax.barh(range(len(plan)), pen, left=inv + ope, height=0.55, color=C5,
edgecolor="0.35", linewidth=0.5, label="惩罚费用")
for i, tot in enumerate(inv + ope + pen):
ax.text(tot + 12, i, f"{tot:.0f}", va="center", fontsize=FS - 2)
ax.set_yticks(range(len(plan)))
ax.set_yticklabels(plan)
ax.set_ylabel("配置方案")
ax.set_xlabel("年化费用 / 万元")
ax.set_xlim(0, 1080)
legend(ax, "lower right", ncol=1)
save(fig, "04_条形", "04_条形_储能方案费用构成")
# ==================== 5. 饼图 pie ====================
def pie_01():
"""园区用电量构成。"""
fig, ax = new_fig(left=0.14, bottom=0.10, right=0.86, top=0.90)
lab = ["动力用电", "照明", "空调", "充电桩", "其他"]
val = [46, 15, 24, 10, 5]
ax.pie(val, labels=lab, colors=PALETTE5, autopct="%1.1f%%",
startangle=95, counterclock=False,
wedgeprops={"edgecolor": "white", "linewidth": 0.8},
textprops={"fontsize": FS})
ax.axis("equal")
save(fig, "05_饼图", "05_饼图_园区用电量构成")
def pie_02():
"""配电网线损构成。"""
fig, ax = new_fig(left=0.14, bottom=0.10, right=0.86, top=0.90)
lab = ["中压线路", "配变空载", "配变负载", "低压线路", "计量"]
val = [38, 21, 24, 12, 5]
ax.pie(val, labels=lab, colors=PALETTE5, autopct="%1.1f%%",
startangle=60, counterclock=False, explode=(0.06, 0, 0, 0, 0),
wedgeprops={"edgecolor": "white", "linewidth": 0.8},
textprops={"fontsize": FS})
ax.axis("equal")
save(fig, "05_饼图", "05_饼图_线损构成")
def pie_03():
"""区域电源装机容量结构。"""
fig, ax = new_fig(left=0.14, bottom=0.10, right=0.86, top=0.90)
lab = ["火电", "光伏", "风电", "储能", "水电"]
val = [520, 380, 260, 120, 90]
ax.pie(val, labels=lab, colors=PALETTE5, autopct="%1.1f%%",
startangle=110, counterclock=False,
wedgeprops={"width": 0.42, "edgecolor": "white", "linewidth": 0.8},
textprops={"fontsize": FS},
pctdistance=0.79)
ax.text(0, 0, "总装机\n1 370 MW", ha="center", va="center", fontsize=FS)
ax.axis("equal")
save(fig, "05_饼图", "05_饼图_电源装机结构")
def pie_04():
"""谐波源贡献占比。"""
fig, ax = new_fig(left=0.14, bottom=0.10, right=0.86, top=0.90)
lab = ["变频器", "整流装置", "UPS", "电弧炉", "荧光灯"]
val = [34, 28, 16, 14, 8]
ax.pie(val, labels=lab, colors=PALETTE5, autopct="%1.1f%%",
startangle=45, counterclock=False,
wedgeprops={"edgecolor": "white", "linewidth": 0.8},
textprops={"fontsize": FS},
shadow=False)
ax.axis("equal")
save(fig, "05_饼图", "05_饼图_谐波源贡献占比")
def pie_05():
"""变电站负荷类型占比。"""
fig, ax = new_fig(left=0.14, bottom=0.10, right=0.86, top=0.90)
lab = ["工业", "商业", "居民", "农业", "市政"]
val = [42, 26, 19, 8, 5]
ax.pie(val, labels=lab, colors=PALETTE5, autopct="%1.1f%%",
startangle=90, counterclock=False, radius=1.0,
wedgeprops={"edgecolor": "white", "linewidth": 0.8},
textprops={"fontsize": FS},
labeldistance=1.12)
ax.axis("equal")
save(fig, "05_饼图", "05_饼图_变电站负荷类型")
ALL = (line_01, line_02, line_03, line_04, line_05,
scatter_01, scatter_02, scatter_03, scatter_04, scatter_05,
bar_01, bar_02, bar_03, bar_04, bar_05,
barh_01, barh_02, barh_03, barh_04, barh_05,
pie_01, pie_02, pie_03, pie_04, pie_05)
if __name__ == "__main__":
for fn in ALL:
fn()
print("第一批 25 张完成")
5.3 第二批_分布图型261106.py
# -*- coding: utf-8 -*-
"""第二批:直方、箱线、小提琴、误差棒(各 5 个电气场景,共 20 张)
执行方式:在本 code/ 目录下运行
python 第二批_分布图型261106.py
"""
from 公共配置261106 import (
C1, C2, C3, C4, C5, C6, COLORS, MARKERS, LINES, IT, SUB,
FS, np, new_fig, legend, save, PALETTE5,
)
import matplotlib.pyplot as plt
# ==================== 6. 直方图 hist ====================
def hist_01():
"""日负荷功率分布直方图(双峰:白天与晚高峰)。"""
fig, ax = new_fig()
rng = np.random.default_rng(101)
day = rng.normal(420, 68, 2600)
eve = rng.normal(640, 52, 1500)
data = np.clip(np.concatenate([day, eve]), 240, 820)
ax.hist(data, bins=36, color=C5, edgecolor="0.35", linewidth=0.5,
density=True, label="样本分布")
ax.hist(data, bins=36, histtype="step", color=C1, linewidth=1.0,
density=True, label="分布轮廓")
ax.set_xlabel("有功功率 " + IT["P"] + " / kW")
ax.set_ylabel("概率密度")
ax.set_xlim(240, 830)
legend(ax, "upper center", ncol=2)
save(fig, "06_直方", "06_直方_日负荷功率分布")
def hist_02():
"""母线电压偏差分布(正态,含合格区间)。"""
fig, ax = new_fig()
rng = np.random.default_rng(102)
d = rng.normal(0.6, 2.4, 3200)
n, bins, patches = ax.hist(d, bins=34, color=C5, edgecolor="0.35",
linewidth=0.5, density=True)
for ptc, left in zip(patches, bins[:-1]):
if left < -7 or left > 7:
ptc.set_facecolor(C3)
ax.axvline(7, color=C2, lw=0.8, ls="--", label="允许上限 +7%")
ax.axvline(-7, color=C2, lw=0.8, ls=":", label="允许下限 -7%")
ax.set_xlabel("电压偏差 / %")
ax.set_ylabel("概率密度")
ax.set_xlim(-9, 10)
legend(ax, "upper left", ncol=1)
save(fig, "06_直方", "06_直方_母线电压偏差分布")
def hist_03():
"""风电场风速 Weibull 分布与实测频次对比。"""
fig, ax = new_fig()
rng = np.random.default_rng(103)
k, lam = 2.1, 8.4
v = lam * rng.weibull(k, 6000)
ax.hist(v, bins=32, range=(0, 26), color=C5, edgecolor="0.35", linewidth=0.5,
density=True, label="实测频次")
xx = np.linspace(0.1, 26, 300)
pdf = (k / lam) * (xx / lam) ** (k - 1) * np.exp(-(xx / lam) ** k)
ax.plot(xx, pdf, color=C2, lw=1.0, label="Weibull 拟合")
ax.set_xlabel("风速 " + IT["v"] + " / (m/s)")
ax.set_ylabel("概率密度")
ax.set_xlim(0, 26)
legend(ax, "upper right", ncol=1)
save(fig, "06_直方", "06_直方_风速Weibull分布")
def hist_04():
"""光伏功率短期预测误差分布(叠加零均值参考线)。"""
fig, ax = new_fig()
rng = np.random.default_rng(104)
e = rng.laplace(0, 3.6, 4000)
e = e[(e > -18) & (e < 18)]
ax.hist(e, bins=40, color=C5, edgecolor="0.35", linewidth=0.5,
density=True, label="误差分布")
ax.axvline(0, color=C4, lw=0.7, label="零误差线")
ax.axvline(np.mean(e), color=C2, lw=0.9, ls="--",
label="均值 " + f"{np.mean(e):+.2f}")
ax.set_xlabel("预测误差 / %")
ax.set_ylabel("概率密度")
ax.set_xlim(-18, 18)
legend(ax, "upper left", ncol=1)
save(fig, "06_直方", "06_直方_光伏预测误差分布")
def hist_05():
"""谐波电流幅值累积分布(按不同监测点分层)。"""
fig, ax = new_fig()
rng = np.random.default_rng(105)
a = rng.gamma(2.4, 4.0, 2200)
b = rng.gamma(2.8, 3.2, 2200)
ax.hist(a, bins=30, color=C5, alpha=0.85, edgecolor="0.35", linewidth=0.4,
density=True, label="监测点 A")
ax.hist(b, bins=30, color=C3, alpha=0.55, edgecolor="0.35", linewidth=0.4,
density=True, label="监测点 B")
ax.set_xlabel("5 次谐波电流 " + IT["I"] + SUB["h"] + " / A")
ax.set_ylabel("概率密度")
ax.set_xlim(0, 40)
legend(ax, "upper right", ncol=1)
save(fig, "06_直方", "06_直方_谐波电流幅值分布")
# ==================== 7. 箱线图 boxplot ====================
def box_01():
"""四季日负荷率箱线图。"""
fig, ax = new_fig(left=0.19)
rng = np.random.default_rng(201)
data = [rng.normal(0.72, 0.055, 90), rng.normal(0.78, 0.048, 90),
rng.normal(0.83, 0.042, 90), rng.normal(0.75, 0.052, 90)]
bp = ax.boxplot(data, patch_artist=True, widths=0.5,
medianprops={"color": C2, "linewidth": 1.2},
boxprops={"linewidth": 0.6, "edgecolor": "0.3"},
whiskerprops={"linewidth": 0.6},
capprops={"linewidth": 0.6},
flierprops={"marker": "o", "markersize": 2.5,
"markerfacecolor": C6, "markeredgewidth": 0})
for patch in bp["boxes"]:
patch.set_facecolor(C5)
ax.set_xticks([1, 2, 3, 4])
ax.set_xticklabels(["春季", "夏季", "秋季", "冬季"])
ax.set_xlabel("季节")
ax.set_ylabel("日负荷率")
ax.set_ylim(0.56, 0.98)
save(fig, "07_箱线", "07_箱线_四季日负荷率")
def box_02():
"""三条母线电压波动箱线图(含离群点)。"""
fig, ax = new_fig(left=0.17)
rng = np.random.default_rng(202)
data = [rng.normal(10.15, 0.09, 300), rng.normal(10.02, 0.13, 300),
rng.normal(9.93, 0.17, 300)]
bp = ax.boxplot(data, patch_artist=True, widths=0.45, notch=False,
medianprops={"color": C2, "linewidth": 1.2},
boxprops={"linewidth": 0.6, "edgecolor": "0.3"},
whiskerprops={"linewidth": 0.6},
capprops={"linewidth": 0.6},
flierprops={"marker": "o", "markersize": 2.0,
"markerfacecolor": C6, "markeredgewidth": 0})
for patch, c in zip(bp["boxes"], [C1, C5, C3]):
patch.set_facecolor(c)
ax.set_xticks([1, 2, 3])
ax.set_xticklabels(["Ⅰ 段", "Ⅱ 段", "Ⅲ 段"])
ax.set_xlabel("母线")
ax.set_ylabel("电压 " + IT["U"] + " / kV")
ax.set_ylim(9.5, 10.55)
save(fig, "07_箱线", "07_箱线_三母线电压分布")
def box_03():
"""四种降损方案的年线损率分布箱线图。"""
fig, ax = new_fig(left=0.20)
rng = np.random.default_rng(203)
data = [rng.normal(4.62, 0.30, 120), rng.normal(3.71, 0.26, 120),
rng.normal(3.24, 0.22, 120), rng.normal(2.86, 0.19, 120)]
bp = ax.boxplot(data, patch_artist=True, widths=0.48,
medianprops={"color": C2, "linewidth": 1.2},
boxprops={"linewidth": 0.6, "edgecolor": "0.3"},
whiskerprops={"linewidth": 0.6},
capprops={"linewidth": 0.6},
flierprops={"marker": "o", "markersize": 2.2,
"markerfacecolor": C6, "markeredgewidth": 0})
for patch, c in zip(bp["boxes"], [C6, C5, C1, C3]):
patch.set_facecolor(c)
ax.set_xticks([1, 2, 3, 4])
ax.set_xticklabels(["原状", "方案一", "方案二", "方案三"])
ax.set_xlabel("降损方案")
ax.set_ylabel("年线损率 / %")
ax.set_ylim(2.0, 5.5)
save(fig, "07_箱线", "07_箱线_降损方案线损率")
def box_04():
"""各时段功率因数分布。"""
fig, ax = new_fig(left=0.19)
rng = np.random.default_rng(204)
base = [0.88, 0.91, 0.86, 0.84, 0.89, 0.93]
data = [rng.normal(b, 0.028, 80) for b in base]
bp = ax.boxplot(data, patch_artist=True, widths=0.5,
medianprops={"color": C2, "linewidth": 1.2},
boxprops={"linewidth": 0.6, "edgecolor": "0.3"},
whiskerprops={"linewidth": 0.6},
capprops={"linewidth": 0.6},
flierprops={"marker": "o", "markersize": 2.2,
"markerfacecolor": C6, "markeredgewidth": 0})
for patch in bp["boxes"]:
patch.set_facecolor(C5)
ax.axhline(0.90, color=C2, lw=0.8, ls="--", label="考核值 0.90")
ax.set_xticks(range(1, 7))
ax.set_xticklabels(["0-4", "4-8", "8-12", "12-16", "16-20", "20-24"])
ax.set_xlabel("时段 / h")
ax.set_ylabel("功率因数 cos " + IT["phi"])
ax.set_ylim(0.78, 1.0)
legend(ax, "lower right", ncol=1)
save(fig, "07_箱线", "07_箱线_各时段功率因数")
def box_05():
"""不同负载率下变压器顶层油温升分布。"""
fig, ax = new_fig(left=0.19)
rng = np.random.default_rng(205)
load = [0.4, 0.6, 0.8, 1.0, 1.2]
data = [rng.normal(28 + 26 * L ** 2, 2.6, 70) for L in load]
bp = ax.boxplot(data, patch_artist=True, widths=0.48,
medianprops={"color": C2, "linewidth": 1.2},
boxprops={"linewidth": 0.6, "edgecolor": "0.3"},
whiskerprops={"linewidth": 0.6},
capprops={"linewidth": 0.6},
flierprops={"marker": "o", "markersize": 2.2,
"markerfacecolor": C6, "markeredgewidth": 0})
cols = [C5, C5, C1, C3, C2]
for patch, c in zip(bp["boxes"], cols):
patch.set_facecolor(c)
ax.set_xticks(range(1, 6))
ax.set_xticklabels([f"{L:.1f}" for L in load])
ax.set_xlabel("负载率 " + IT["beta"])
ax.set_ylabel("顶层油温升 " + IT["theta"] + " / K")
ax.set_ylim(20, 80)
save(fig, "07_箱线", "07_箱线_变压器油温升")
# ==================== 8. 小提琴图 violinplot ====================
def violin_01():
"""各月光伏出力分布小提琴图。"""
fig, ax = new_fig(left=0.19)
rng = np.random.default_rng(301)
means = [210, 268, 340, 396, 442, 470, 486, 468, 402, 320, 238, 196]
data = [rng.normal(m, m * 0.16, 120).clip(0) for m in means]
parts = ax.violinplot(data, showmedians=True, widths=0.75,
showextrema=False)
for body in parts["bodies"]:
body.set_facecolor(C5)
body.set_edgecolor("0.35")
body.set_linewidth(0.5)
body.set_alpha(0.9)
parts["cmedians"].set_color(C2)
parts["cmedians"].set_linewidth(1.2)
ax.set_xticks(range(1, 13))
ax.set_xticklabels([str(i) for i in range(1, 13)])
ax.set_xlabel("月份")
ax.set_ylabel("日均出力 " + IT["P"] + " / kW")
ax.set_ylim(-40, 780)
save(fig, "08_小提琴", "08_小提琴_各月光伏出力")
def violin_02():
"""各馈线电流分布小提琴图(含中位数)。"""
fig, ax = new_fig(left=0.19)
rng = np.random.default_rng(302)
data = [rng.gamma(9, 32, 200), rng.gamma(11, 24, 200),
rng.gamma(8, 46, 200), rng.gamma(14, 18, 200),
rng.gamma(10, 30, 200)]
parts = ax.violinplot(data, showmedians=True, widths=0.75,
showextrema=False)
for body, c in zip(parts["bodies"], [C5, C1, C5, C3, C5]):
body.set_facecolor(c)
body.set_edgecolor("0.35")
body.set_linewidth(0.5)
body.set_alpha(0.9)
parts["cmedians"].set_color(C2)
parts["cmedians"].set_linewidth(1.2)
ax.set_xticks(range(1, 6))
ax.set_xticklabels(["L1", "L2", "L3", "L4", "L5"])
ax.set_xlabel("馈线")
ax.set_ylabel("电流有效值 " + IT["I"] + " / A")
ax.set_ylim(0, 560)
save(fig, "08_小提琴", "08_小提琴_各馈线电流分布")
def violin_03():
"""四种储能调度策略的效率分布。"""
fig, ax = new_fig(left=0.20)
rng = np.random.default_rng(303)
data = [rng.normal(0.86, 0.022, 160), rng.normal(0.89, 0.018, 160),
rng.normal(0.91, 0.015, 160), rng.normal(0.92, 0.012, 160)]
parts = ax.violinplot(data, showmedians=True, widths=0.72,
showextrema=False)
for body, c in zip(parts["bodies"], [C6, C5, C1, C3]):
body.set_facecolor(c)
body.set_edgecolor("0.35")
body.set_linewidth(0.5)
body.set_alpha(0.9)
parts["cmedians"].set_color(C2)
parts["cmedians"].set_linewidth(1.2)
ax.set_xticks([1, 2, 3, 4])
ax.set_xticklabels(["策略一", "策略二", "策略三", "策略四"])
ax.set_xlabel("调度策略")
ax.set_ylabel("综合效率 " + IT["eta"])
ax.set_ylim(0.80, 0.96)
save(fig, "08_小提琴", "08_小提琴_储能策略效率分布")
def violin_04():
"""四季风速分布小提琴图。"""
fig, ax = new_fig(left=0.19)
rng = np.random.default_rng(304)
shapes = [(2.4, 9.2), (1.9, 6.8), (2.2, 7.6), (2.7, 10.4)]
data = [lam * rng.weibull(k, 240) for k, lam in shapes]
parts = ax.violinplot(data, showmedians=True, widths=0.75,
showextrema=False)
for body, c in zip(parts["bodies"], [C5, C1, C3, C5]):
body.set_facecolor(c)
body.set_edgecolor("0.35")
body.set_linewidth(0.5)
body.set_alpha(0.9)
parts["cmedians"].set_color(C2)
parts["cmedians"].set_linewidth(1.2)
ax.set_xticks([1, 2, 3, 4])
ax.set_xticklabels(["春季", "夏季", "秋季", "冬季"])
ax.set_xlabel("季节")
ax.set_ylabel("风速 " + IT["v"] + " / (m/s)")
ax.set_ylim(0, 26)
save(fig, "08_小提琴", "08_小提琴_四季风速分布")
def violin_05():
"""三相电压不平衡度分布对比。"""
fig, ax = new_fig(left=0.20)
rng = np.random.default_rng(305)
data = [rng.gamma(5.5, 0.42, 220), rng.gamma(7.2, 0.36, 220),
rng.gamma(4.4, 0.50, 220)]
parts = ax.violinplot(data, showmedians=True, widths=0.7,
showextrema=False)
for body, c in zip(parts["bodies"], [C5, C1, C3]):
body.set_facecolor(c)
body.set_edgecolor("0.35")
body.set_linewidth(0.5)
body.set_alpha(0.9)
parts["cmedians"].set_color(C2)
parts["cmedians"].set_linewidth(1.2)
ax.axhline(2.0, color=C2, lw=0.8, ls="--", label="限值 2.0%")
ax.set_xticks([1, 2, 3])
ax.set_xticklabels(["节点 A", "节点 B", "节点 C"])
ax.set_xlabel("监测节点")
ax.set_ylabel("电压不平衡度 / %")
ax.set_ylim(0, 12)
legend(ax, "upper right", ncol=1)
save(fig, "08_小提琴", "08_小提琴_三相电压不平衡度")
# ==================== 9. 误差棒图 errorbar ====================
def err_01():
"""实测与仿真节点电压对比(含测量误差棒)。"""
fig, ax = new_fig(left=0.19)
rng = np.random.default_rng(401)
node = np.arange(1, 9)
sim = np.array([10.12, 10.06, 9.98, 9.91, 9.86, 9.94, 10.03, 10.09])
err = rng.uniform(0.012, 0.032, 8)
meas = sim + rng.normal(0, 0.012, 8)
ax.errorbar(node, sim, yerr=err, fmt="o", color=C1, ms=4, lw=1.0,
capsize=3, capthick=0.7, elinewidth=0.7, label="仿真值 ± 误差")
ax.plot(node, meas, ls="none", marker="s", ms=4, color=C2, label="实测值")
ax.set_xlabel("节点编号")
ax.set_ylabel("节点电压 " + IT["U"] + " / kV")
ax.set_xticks(node)
ax.set_xlim(0.5, 8.5)
ax.set_ylim(9.80, 10.20)
legend(ax, "lower left", ncol=1)
save(fig, "09_误差棒", "09_误差棒_节点电压实测与仿真")
def err_02():
"""变压器不同负载率下的效率测量值与标准差。"""
fig, ax = new_fig(left=0.19)
rng = np.random.default_rng(402)
beta = np.linspace(0.1, 1.0, 10)
eff = 98.55 - 1.65 * (1 - beta) ** 1.4 - 0.55 * beta ** 3
sd = rng.uniform(0.05, 0.14, 10)
ax.errorbar(beta, eff, yerr=sd, fmt="-o", color=C1, ms=3.5, lw=1.0,
capsize=3, capthick=0.7, elinewidth=0.7, label="实测均值 ± 标准差")
ax.set_xlabel("负载率 " + IT["beta"])
ax.set_ylabel("效率 " + IT["eta"] + " / %")
ax.set_xlim(0, 1.05)
ax.set_ylim(97.0, 99.2)
legend(ax, "lower right", ncol=1)
save(fig, "09_误差棒", "09_误差棒_变压器效率测量")
def err_03():
"""各次谐波电流测量值与不确定度(对数纵轴)。"""
fig, ax = new_fig(left=0.19)
rng = np.random.default_rng(403)
h = np.array([2, 3, 4, 5, 6, 7, 9, 11, 13])
amp = np.array([4.2, 38.6, 2.1, 26.4, 1.3, 15.8, 6.2, 3.4, 1.8])
err = amp * rng.uniform(0.04, 0.11, len(h))
ax.errorbar(h, amp, yerr=err, fmt="s", color=C1, ms=4, lw=1.0,
capsize=3, capthick=0.7, elinewidth=0.7, label="测量值 ± 不确定度")
ax.set_yscale("log")
ax.set_xlabel("谐波次数 " + IT["h"])
ax.set_ylabel("谐波电流 " + IT["I"] + SUB["h"] + " / A")
ax.set_xticks(h)
ax.set_xlim(1, 14)
legend(ax, "lower left", ncol=1)
save(fig, "09_误差棒", "09_误差棒_谐波电流不确定度")
def err_04():
"""电缆接头温度测量值随环境温度变化(三测点)。"""
fig, ax = new_fig(left=0.19)
rng = np.random.default_rng(404)
ta = np.arange(5, 41, 4)
for i, (slope, nm) in enumerate([(1.62, "接头 A"), (1.48, "接头 B"),
(1.75, "接头 C")]):
tc = 8 + slope * ta
sd = rng.uniform(0.6, 1.5, len(ta))
ax.errorbar(ta, tc, yerr=sd, fmt=["o", "s", "^"][i],
color=COLORS[i], ms=3.5, lw=1.0, capsize=2.5,
capthick=0.7, elinewidth=0.7, label=nm)
ax.set_xlabel("环境温度 " + IT["T"] + SUB["0"] + " / °C")
ax.set_ylabel("接头温度 " + IT["T"] + " / °C")
ax.set_xlim(0, 45)
ax.margins(y=0.22)
legend(ax, "upper left", ncol=1)
save(fig, "09_误差棒", "09_误差棒_电缆接头温度")
def err_05():
"""短期负荷预测各时段误差(MAPE 与置信区间)。"""
fig, ax = new_fig(left=0.19)
rng = np.random.default_rng(405)
t = np.arange(1, 25)
mape = 3.2 + 1.8 * np.abs(np.sin(t / 24 * np.pi)) + rng.normal(0, 0.25, 24)
ci = 0.9 + 0.35 * np.sin(t / 24 * np.pi * 2) ** 2
ax.errorbar(t, mape, yerr=ci, fmt="-o", color=C1, ms=3, lw=1.0,
capsize=2.5, capthick=0.7, elinewidth=0.7,
label="平均绝对百分比误差 ± 置信半宽")
ax.axhline(np.mean(mape), color=C2, lw=0.8, ls="--",
label="全日均值 " + f"{np.mean(mape):.2f}%")
ax.set_xlabel("时刻 " + IT["t"] + " / h")
ax.set_ylabel("预测误差 MAPE / %")
ax.set_xlim(0, 25)
ax.set_ylim(2.0, 6.6)
legend(ax, "upper center", ncol=1)
save(fig, "09_误差棒", "09_误差棒_短期负荷预测误差")
ALL = (hist_01, hist_02, hist_03, hist_04, hist_05,
box_01, box_02, box_03, box_04, box_05,
violin_01, violin_02, violin_03, violin_04, violin_05,
err_01, err_02, err_03, err_04, err_05)
if __name__ == "__main__":
for fn in ALL:
fn()
print("第二批 20 张完成")
5.4 第三批_面积与场261106.py
# -*- coding: utf-8 -*-
"""第三批:填充带、阶梯、堆叠面积、热图、等值线、网格图(各 5 个,共 30 张)
执行方式:在本 code/ 目录下运行
python 第三批_面积与场261106.py
"""
from 公共配置261106 import (
C1, C2, C3, C4, C5, C6, COLORS, MARKERS, LINES, IT, SUB,
FS, np, new_fig, legend, save, PALETTE5,
)
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap, LogNorm
# 电气惯用的单色与双色渐变(浅→深蓝;蓝→橙)
CMAP_SEQ = LinearSegmentedColormap.from_list(
"elec_seq", ["#F4F9FD", "#CFE1F1", "#93B8DC", "#4E82B8", "#1E4E86"])
CMAP_DIV = LinearSegmentedColormap.from_list("elec_div", ["#2E5FA3", "#F2F2F2", "#C0392B"])
def _cbar(fig, ax, mappable, label):
"""统一色条:细边框、字号 8 磅、贴坐标区右侧。"""
cb = fig.colorbar(mappable, ax=ax, pad=0.02, fraction=0.055)
cb.set_label(label)
cb.ax.tick_params(direction="in", labelsize=FS - 2, length=2.5, width=0.5)
cb.outline.set_linewidth(0.5)
return cb
# ==================== 10. 填充带 fill_between ====================
def fill_01():
"""光伏出力预测区间(P10~P90 带 + 预测中值)。"""
fig, ax = new_fig()
t = np.arange(5, 20.01, 0.5)
mid = 820 * np.sin((t - 5) / 15 * np.pi) ** 1.3
lo = mid * 0.72
hi = np.minimum(mid * 1.18, 980)
ax.fill_between(t, lo, hi, color=C5, alpha=0.55, linewidth=0,
label="P10~P90 区间")
ax.plot(t, mid, color=C1, lw=1.0, label="预测中值")
ax.plot(t, lo, color=C4, lw=0.6, ls="--", label="P10")
ax.plot(t, hi, color=C4, lw=0.6, ls=":", label="P90")
ax.set_xlabel("时间 " + IT["t"] + " / h")
ax.set_ylabel("有功出力 " + IT["P"] + " / kW")
ax.set_xlim(5, 20)
ax.set_ylim(-40, 1480)
legend(ax, "upper center", ncol=2)
save(fig, "10_填充带", "10_填充带_光伏出力预测区间")
def fill_02():
"""母线负荷预测置信带。"""
fig, ax = new_fig()
t = np.arange(0, 24.01, 1.0)
base = 480 + 170 * np.exp(-((t - 10) / 3.4) ** 2) + 210 * np.exp(-((t - 16) / 3.0) ** 2)
sd = 18 + 26 * (1 - np.exp(-((t - 12) / 8) ** 2))
ax.fill_between(t, base - 1.96 * sd, base + 1.96 * sd, color=C5, alpha=0.5,
linewidth=0, label="95% 置信带")
ax.plot(t, base, color=C1, lw=1.0, marker="o", markevery=3, ms=3, label="预测值")
ax.set_xlabel("时间 " + IT["t"] + " / h")
ax.set_ylabel("有功负荷 " + IT["P"] + " / kW")
ax.set_xlim(0, 24)
ax.set_ylim(380, 880)
legend(ax, "upper center", ncol=2)
save(fig, "10_填充带", "10_填充带_负荷预测置信带")
def fill_03():
"""节点电压运行范围与实测曲线(GB/T 12325 允许偏差带)。"""
fig, ax = new_fig()
t = np.arange(0, 24.01, 0.5)
u = 10.0 + 0.22 * np.sin((t - 8) / 24 * 2 * np.pi) + 0.05 * np.sin(t * 1.7)
ax.axhspan(9.3, 10.7, color=C5, alpha=0.35, linewidth=0, label="±7% 允许范围")
ax.plot(t, u, color=C1, lw=1.0, label="实测电压")
ax.axhline(10.7, color=C2, lw=0.6, ls="--")
ax.axhline(9.3, color=C2, lw=0.6, ls="--")
ax.set_xlabel("时间 " + IT["t"] + " / h")
ax.set_ylabel("母线电压 " + IT["U"] + " / kV")
ax.set_xlim(0, 24)
ax.set_ylim(9.0, 11.0)
legend(ax, "upper center", ncol=2)
save(fig, "10_填充带", "10_填充带_节点电压允许范围")
def fill_04():
"""储能荷电状态运行区间与上下限。"""
fig, ax = new_fig()
t = np.arange(0, 24.01, 0.5)
soc = 58 + 26 * np.sin((t - 4) / 24 * 2 * np.pi)
ax.fill_between(t, 10, 90, color=C5, alpha=0.30, linewidth=0, label="允许运行区间")
ax.fill_between(t, soc, 58, where=soc >= 58, color=C3, alpha=0.45,
linewidth=0, label="放电段")
ax.fill_between(t, soc, 58, where=soc < 58, color=C1, alpha=0.30,
linewidth=0, label="充电段")
ax.plot(t, soc, color=C1, lw=1.0, label="SOC 轨迹")
ax.axhline(90, color=C2, lw=0.6, ls="--")
ax.axhline(10, color=C2, lw=0.6, ls="--")
ax.set_xlabel("时间 " + IT["t"] + " / h")
ax.set_ylabel("荷电状态 SOC / %")
ax.set_xlim(0, 24)
ax.set_ylim(0, 108)
legend(ax, "upper center", ncol=3)
save(fig, "10_填充带", "10_填充带_储能SOC运行区间")
def fill_05():
"""系统频率偏差与一次调频死区。"""
fig, ax = new_fig()
t = np.linspace(0, 60, 400)
df = 0.16 * np.exp(-((t - 22) / 5.0) ** 2) - 0.11 * np.exp(-((t - 45) / 6.0) ** 2)
ax.axhspan(-0.05, 0.05, color=C5, alpha=0.45, linewidth=0, label="调频死区 ±0.05 Hz")
ax.plot(t, df, color=C1, lw=1.0, label="频率偏差")
ax.axhline(0.2, color=C2, lw=0.6, ls="--", label="允许上限 +0.2 Hz")
ax.axhline(-0.2, color=C2, lw=0.6, ls=":", label="允许下限 -0.2 Hz")
ax.set_xlabel("时间 " + IT["t"] + " / s")
ax.set_ylabel("频率偏差 Δ" + IT["f"] + " / Hz")
ax.set_xlim(0, 60)
ax.set_ylim(-0.27, 0.32)
legend(ax, "lower left", ncol=2)
save(fig, "10_填充带", "10_填充带_系统频率偏差")
# ==================== 11. 阶梯图 step ====================
def step_01():
"""分时电价阶梯曲线。"""
fig, ax = new_fig()
h = np.arange(0, 25)
price = np.select(
[(h >= 8) & (h < 11), (h >= 11) & (h < 18), (h >= 18) & (h < 23)],
[0.86, 0.58, 0.92], default=0.32)
ax.step(h, price, where="post", color=C1, lw=1.2, label="分时电价")
ax.fill_between(h, 0, price, step="post", color=C5, alpha=0.35, linewidth=0)
ax.set_xlabel("时刻 " + IT["t"] + " / h")
ax.set_ylabel("电价 / (元/(kW·h))")
ax.set_xlim(0, 24)
ax.set_ylim(0, 1.12)
legend(ax, "upper left", ncol=1)
save(fig, "11_阶梯", "11_阶梯_分时电价曲线")
def step_02():
"""变压器分接头档位日调节过程。"""
fig, ax = new_fig()
h = np.arange(0, 25)
tap = np.select([(h >= 6) & (h < 9), (h >= 9) & (h < 17), (h >= 17) & (h < 21)],
[1, 2, 3], default=0)
ax.step(h, tap, where="post", color=C1, lw=1.2, marker="o", ms=3,
label="分接头档位")
ax.set_xlabel("时刻 " + IT["t"] + " / h")
ax.set_ylabel("档位")
ax.set_xlim(0, 24)
ax.set_yticks([0, 1, 2, 3])
ax.set_ylim(-0.4, 3.8)
legend(ax, "upper left", ncol=1)
save(fig, "11_阶梯", "11_阶梯_变压器分接头档位")
def step_03():
"""储能功率指令阶梯化与荷电状态响应。"""
fig, ax = new_fig()
h = np.arange(0, 25)
p = np.select([(h >= 1) & (h < 7), (h >= 18) & (h < 22)], [-200, 160], default=0)
ax.step(h, p, where="post", color=C1, lw=1.2, label="功率指令")
ax.axhline(0, color=C4, lw=0.5)
ax.set_xlabel("时刻 " + IT["t"] + " / h")
ax.set_ylabel("有功功率 " + IT["P"] + " / kW")
ax.set_xlim(0, 24)
ax.set_ylim(-260, 230)
legend(ax, "lower left", ncol=1)
save(fig, "11_阶梯", "11_阶梯_储能功率指令")
def step_04():
"""无功补偿装置投切档位(累计投入容量)。"""
fig, ax = new_fig()
h = np.arange(0, 25)
q = np.select([(h >= 8) & (h < 12), (h >= 12) & (h < 17), (h >= 17) & (h < 21)],
[200, 400, 300], default=100)
ax.step(h, q, where="post", color=C3, lw=1.2, label="投入容量")
ax.fill_between(h, 0, q, step="post", color=C3, alpha=0.25, linewidth=0)
ax.set_xlabel("时刻 " + IT["t"] + " / h")
ax.set_ylabel("无功补偿容量 " + IT["Q"] + " / kvar")
ax.set_xlim(0, 24)
ax.set_ylim(0, 520)
legend(ax, "upper left", ncol=1)
save(fig, "11_阶梯", "11_阶梯_无功补偿投切")
def step_05():
"""负荷分级投切:可中断负荷容量阶梯变化。"""
fig, ax = new_fig(right=0.86)
t = np.arange(0, 120)
shed = np.select([(t >= 30) & (t < 55), (t >= 55) & (t < 80), (t >= 80) & (t < 100)],
[80, 140, 60], default=0)
ax.step(t, shed, where="post", color=C2, lw=1.2, label="切除负荷")
ax2 = ax.twinx()
ax2.plot(t, 620 - 0.85 * t + 26 * np.sin(t / 6), color=C1, lw=1.0,
label="母线负荷")
ax2.set_ylabel("母线负荷 " + IT["P"] + " / kW")
ax2.tick_params(direction="in", length=3, width=0.5)
ax2.spines["right"].set_linewidth(0.5)
ax2.set_ylim(400, 700)
ax.set_xlabel("时间 " + IT["t"] + " / min")
ax.set_ylabel("切除负荷 " + IT["P"] + "shed / kW")
ax.set_xlim(0, 120)
ax.set_ylim(-10, 180)
h1, l1 = ax.get_legend_handles_labels()
h2, l2 = ax2.get_legend_handles_labels()
lg = ax.legend(h1 + h2, l1 + l2, loc="upper left", ncol=1, fontsize=FS - 1,
framealpha=0.60, edgecolor="0.6", borderpad=0.3)
save(fig, "11_阶梯", "11_阶梯_可中断负荷投切")
# ==================== 12. 堆叠面积 stackplot ====================
def stack_01():
"""日电源出力构成(火电、光伏、风电、储能)。"""
fig, ax = new_fig()
t = np.arange(0, 24.01, 0.5)
pv = np.where((t > 6) & (t < 19), 420 * np.sin((t - 6) / 13 * np.pi) ** 1.4, 0)
wind = 180 + 60 * np.sin(t / 3.4) + 30 * np.sin(t / 1.3)
ess = np.where((t >= 18) & (t < 22), 120, 0) - np.where((t >= 1) & (t < 6), 80, 0)
th = 520 - pv * 0.42 - ess * 0.3 + 60 * np.exp(-((t - 19) / 2.2) ** 2)
ax.stackplot(t, th, pv, wind, np.clip(ess, 0, None),
colors=[C4, C3, C5, C1], alpha=0.92, linewidth=0.4,
edgecolor="white",
labels=["火电", "光伏", "风电", "储能放电"])
ax.set_xlabel("时间 " + IT["t"] + " / h")
ax.set_ylabel("出力 " + IT["P"] + " / MW")
ax.set_xlim(0, 24)
ax.set_ylim(0, 1360)
legend(ax, "upper center", ncol=2)
save(fig, "12_堆叠面积", "12_堆叠面积_日电源出力构成")
def stack_02():
"""工业用户日负荷构成。"""
fig, ax = new_fig()
t = np.arange(0, 24.01, 0.5)
motor = 280 + 90 * np.exp(-((t - 10) / 4.0) ** 2) + 60 * np.exp(-((t - 16) / 3.0) ** 2)
furnace = 180 + 40 * np.sin(t / 4.2) ** 2
lighting = np.where((t > 6) & (t < 22), 60, 12)
hvac = 40 + 70 * np.exp(-((t - 14) / 4.4) ** 2)
ax.stackplot(t, motor, furnace, lighting, hvac,
colors=[C1, C5, C3, C6], alpha=0.92, linewidth=0.4,
edgecolor="white",
labels=["电机", "电炉", "照明", "空调"])
ax.set_xlabel("时间 " + IT["t"] + " / h")
ax.set_ylabel("有功负荷 " + IT["P"] + " / kW")
ax.set_xlim(0, 24)
ax.set_ylim(0, 930)
legend(ax, "upper center", ncol=2)
save(fig, "12_堆叠面积", "12_堆叠面积_工业用户负荷构成")
def stack_03():
"""配电网线损构成随负荷变化。"""
fig, ax = new_fig()
beta = np.linspace(0.2, 1.2, 30)
line = 2.8 * beta ** 2
cu = 1.4 * beta ** 2
core = np.full_like(beta, 1.1)
meter = np.full_like(beta, 0.35)
ax.stackplot(beta, line, cu, core, meter,
colors=[C1, C5, C3, C6], alpha=0.92, linewidth=0.4,
edgecolor="white",
labels=["中压线路", "配变负载", "配变空载", "计量"])
ax.set_xlabel("负载率 " + IT["beta"])
ax.set_ylabel("线损率 / %")
ax.set_xlim(0.2, 1.2)
ax.set_ylim(0, 8.2)
legend(ax, "upper left", ncol=2)
save(fig, "12_堆叠面积", "12_堆叠面积_线损构成随负载变化")
def stack_04():
"""谐波电流各次分量随时间的叠加。"""
fig, ax = new_fig()
t = np.arange(0, 24.01, 0.5)
h3 = 18 + 14 * np.exp(-((t - 11) / 3.6) ** 2) + 12 * np.exp(-((t - 18) / 3.0) ** 2)
h5 = 12 + 9 * np.exp(-((t - 10) / 3.2) ** 2) + 8 * np.exp(-((t - 17) / 2.8) ** 2)
h7 = 6 + 5 * np.exp(-((t - 10) / 3.4) ** 2)
h11 = 2.4 + 2.0 * np.exp(-((t - 11) / 3.0) ** 2)
ax.stackplot(t, h3, h5, h7, h11,
colors=[C1, C5, C3, C6], alpha=0.92, linewidth=0.4,
edgecolor="white",
labels=["3 次", "5 次", "7 次", "11 次"])
ax.set_xlabel("时间 " + IT["t"] + " / h")
ax.set_ylabel("谐波电流 " + IT["I"] + SUB["h"] + " / A")
ax.set_xlim(0, 24)
ax.set_ylim(0, 75)
legend(ax, "upper center", ncol=2)
save(fig, "12_堆叠面积", "12_堆叠面积_谐波电流分量叠加")
def stack_05():
"""多能互补系统供能构成(电、热、冷)。"""
fig, ax = new_fig()
m = np.arange(1, 13)
ele = np.array([320, 296, 268, 240, 232, 258, 302, 316, 280, 246, 262, 310])
heat = np.array([420, 386, 268, 142, 68, 22, 8, 10, 46, 158, 306, 412])
cool = np.array([12, 18, 46, 118, 226, 318, 372, 344, 212, 96, 26, 14])
ax.stackplot(m, ele, heat, cool, colors=[C1, C3, C5], alpha=0.92,
linewidth=0.4, edgecolor="white", labels=["电负荷", "热负荷", "冷负荷"])
ax.set_xlabel("月份")
ax.set_ylabel("供能量 / (MW·h)")
ax.set_xlim(1, 12)
ax.set_ylim(0, 1000)
legend(ax, "upper center", ncol=3)
save(fig, "12_堆叠面积", "12_堆叠面积_多能互补供能构成")
# ==================== 13. 热图 imshow ====================
def heat_01():
"""月—时负荷矩阵热图。"""
fig, ax = new_fig(left=0.19, bottom=0.21, right=0.86)
rng = np.random.default_rng(501)
M = np.zeros((12, 24))
for m in range(12):
season = 1 + 0.22 * np.cos((m - 0.5) / 12 * 2 * np.pi + np.pi)
for h in range(24):
daily = 0.62 + 0.38 * np.exp(-((h - 10) / 3.4) ** 2) + 0.45 * np.exp(-((h - 16) / 3.0) ** 2)
M[m, h] = 640 * season * daily + rng.normal(0, 12)
im = ax.imshow(M, aspect="auto", cmap=CMAP_SEQ, origin="lower",
extent=[0, 24, 0.5, 12.5])
ax.set_xticks([0, 4, 8, 12, 16, 20, 24])
ax.set_yticks([1, 3, 5, 7, 9, 11])
ax.set_xlabel("时刻 " + IT["t"] + " / h")
ax.set_ylabel("月份")
_cbar(fig, ax, im, "负荷 " + IT["P"] + " / kW")
save(fig, "13_热图", "13_热图_月时负荷矩阵")
def heat_02():
"""谐波时频图(次数—时间)。"""
fig, ax = new_fig(left=0.19, bottom=0.21, right=0.86)
rng = np.random.default_rng(502)
f = np.arange(2, 26)
t = np.arange(0, 24, 0.5)
Z = np.zeros((len(f), len(t)))
for i, fi in enumerate(f):
Z[i] = (30 / (fi ** 0.9)) * (1 + 0.6 * np.exp(-((t - 11) / 4) ** 2)
+ 0.5 * np.exp(-((t - 18) / 3) ** 2))
Z += rng.normal(0, 0.5, Z.shape)
im = ax.imshow(Z, aspect="auto", cmap=CMAP_SEQ, origin="lower",
extent=[0, 24, 1.5, 26.5])
ax.set_xticks([0, 4, 8, 12, 16, 20, 24])
ax.set_yticks([3, 5, 7, 11, 17, 23])
ax.set_xlabel("时刻 " + IT["t"] + " / h")
ax.set_ylabel("谐波次数 " + IT["h"])
_cbar(fig, ax, im, "幅值 " + IT["I"] + SUB["h"] + " / A")
save(fig, "13_热图", "13_热图_谐波时频分布")
def heat_03():
"""各节点各时段电压偏差矩阵。"""
fig, ax = new_fig(left=0.19, bottom=0.21, right=0.86)
rng = np.random.default_rng(503)
nodes = 10
M = np.zeros((nodes, 24))
for i in range(nodes):
base = 1.8 - 0.42 * i
M[i] = base + rng.normal(0, 0.5, 24) - 0.9 * np.exp(-((np.arange(24) - 15) / 4) ** 2)
im = ax.imshow(M, aspect="auto", cmap=CMAP_DIV, origin="lower",
extent=[0, 24, 0.5, nodes + 0.5], vmin=-6, vmax=6)
ax.set_xticks([0, 4, 8, 12, 16, 20, 24])
ax.set_yticks([1, 3, 5, 7, 9])
ax.set_xlabel("时刻 " + IT["t"] + " / h")
ax.set_ylabel("节点编号")
_cbar(fig, ax, im, "电压偏差 / %")
save(fig, "13_热图", "13_热图_节点电压偏差矩阵")
def heat_04():
"""电气量相关系数矩阵热图。"""
fig, ax = new_fig(left=0.26, bottom=0.26, right=0.86)
rng = np.random.default_rng(504)
names = ["负荷", "电压", "电流", "功率因数", "温度", "谐波"]
A = rng.normal(0, 1, (6, 200))
A[1] = -0.75 * A[0] + 0.4 * rng.normal(0, 1, 200)
A[2] = 0.92 * A[0] + 0.2 * rng.normal(0, 1, 200)
A[3] = 0.5 * A[0] + 0.5 * rng.normal(0, 1, 200)
A[4] = 0.68 * A[0] + 0.5 * rng.normal(0, 1, 200)
A[5] = 0.55 * A[0] + 0.6 * rng.normal(0, 1, 200)
C = np.corrcoef(A)
im = ax.imshow(C, cmap=CMAP_DIV, vmin=-1, vmax=1)
for i in range(6):
for j in range(6):
ax.text(j, i, f"{C[i, j]:.2f}", ha="center", va="center",
fontsize=FS - 3)
ax.set_xticks(range(6))
ax.set_yticks(range(6))
ax.set_xticklabels(names, rotation=30, ha="right")
ax.set_yticklabels(names)
_cbar(fig, ax, im, "相关系数 " + IT["r"])
save(fig, "13_热图", "13_热图_电气量相关系数矩阵")
def heat_05():
"""日负荷灰度图(一周 7 天)。"""
fig, ax = new_fig(left=0.19, bottom=0.21, right=0.86)
rng = np.random.default_rng(505)
M = np.zeros((7, 24))
for d in range(7):
wk = 1.0 if d < 5 else 0.78
for h in range(24):
M[d, h] = wk * (430 + 180 * np.exp(-((h - 10) / 3.4) ** 2)
+ 210 * np.exp(-((h - 16) / 3.0) ** 2)) + rng.normal(0, 14)
im = ax.imshow(M, aspect="auto", cmap="Greys", origin="lower",
extent=[0, 24, 0.5, 7.5])
ax.set_xticks([0, 4, 8, 12, 16, 20, 24])
ax.set_yticks([1, 2, 3, 4, 5, 6, 7])
ax.set_yticklabels(["周一", "周二", "周三", "周四", "周五", "周六", "周日"])
ax.set_xlabel("时刻 " + IT["t"] + " / h")
ax.set_ylabel("星期")
_cbar(fig, ax, im, "负荷 " + IT["P"] + " / kW")
save(fig, "13_热图", "13_热图_一周负荷灰度图")
# ==================== 14. 等值线 contourf ====================
def cont_01():
"""电机效率 MAP 等值线。"""
fig, ax = new_fig(left=0.17, bottom=0.19, right=0.86)
n = np.linspace(600, 3000, 160)
tq = np.linspace(0, 120, 160)
N, T = np.meshgrid(n, tq)
eta = 94.5 - 8.0 * ((T - 62) / 90) ** 2 - 3.4 * ((N - 2100) / 1500) ** 2 \
- 2.0 * ((T - 40) / 80) * ((N - 1800) / 1400)
cs = ax.contourf(N, T, eta, levels=np.linspace(78, 95, 18), cmap=CMAP_SEQ)
cl = ax.contour(N, T, eta, levels=[82, 86, 89, 91, 93], colors="0.15",
linewidths=0.5)
ax.clabel(cl, fmt="%.0f", fontsize=FS - 3, inline=True)
ax.set_xlabel("转速 " + IT["n"] + " / (r/min)")
ax.set_ylabel("转矩 " + IT["T"] + " / (N·m)")
_cbar(fig, ax, cs, "效率 " + IT["eta"] + " / %")
save(fig, "14_等值线", "14_等值线_电机效率MAP")
def cont_02():
"""开关柜内部温度场分布。"""
fig, ax = new_fig(left=0.17, bottom=0.19, right=0.86)
x = np.linspace(0, 0.6, 180)
y = np.linspace(0, 0.9, 200)
X, Y = np.meshgrid(x, y)
Z = 28 + 34 * np.exp(-(((X - 0.32) / 0.16) ** 2 + ((Y - 0.46) / 0.14) ** 2)) \
+ 14 * np.exp(-(((X - 0.14) / 0.10) ** 2 + ((Y - 0.74) / 0.10) ** 2)) \
+ 8 * (Y / 0.9)
cs = ax.contourf(X, Y, Z, levels=22, cmap=CMAP_SEQ)
cl = ax.contour(X, Y, Z, levels=[40, 50, 60, 70], colors="0.15", linewidths=0.5)
ax.clabel(cl, fmt="%.0f", fontsize=FS - 3, inline=True)
ax.set_xlabel("柜宽 " + IT["x"] + " / m")
ax.set_ylabel("柜高 " + IT["y"] + " / m")
_cbar(fig, ax, cs, "温度 " + IT["T"] + " / °C")
save(fig, "14_等值线", "14_等值线_开关柜温度场")
def cont_03():
"""接地网地表电位分布。"""
fig, ax = new_fig(left=0.17, bottom=0.19, right=0.86)
x = np.linspace(-30, 30, 200)
y = np.linspace(-30, 30, 200)
X, Y = np.meshgrid(x, y)
V = 0.0
for gx, gy, I in [(-8, -6, 1.0), (8, -6, 0.85), (0, 8, 0.92)]:
r2 = (X - gx) ** 2 + (Y - gy) ** 2
V += I / np.sqrt(r2 + 1.2) * 62
cs = ax.contourf(X, Y, V, levels=20, cmap=CMAP_SEQ)
cl = ax.contour(X, Y, V, levels=[60, 120, 200, 300], colors="0.15",
linewidths=0.5)
ax.clabel(cl, fmt="%.0f", fontsize=FS - 3, inline=True)
ax.set_xlabel("距接地网中心 " + IT["x"] + " / m")
ax.set_ylabel("距接地网中心 " + IT["y"] + " / m")
ax.set_aspect("equal")
_cbar(fig, ax, cs, "地表电位 " + IT["U"] + " / V")
save(fig, "14_等值线", "14_等值线_接地网地表电位")
def cont_04():
"""电缆终端电场强度分布。"""
fig, ax = new_fig(left=0.17, bottom=0.19, right=0.86)
x = np.linspace(-40, 40, 200)
y = np.linspace(0, 120, 200)
X, Y = np.meshgrid(x, y)
E = 42 * np.exp(-(np.abs(X) / 14) ** 1.3) * (1 - 0.004 * Y) \
+ 12 * np.exp(-((np.abs(X) - 22) / 7) ** 2) * np.exp(-Y / 60)
cs = ax.contourf(X, Y, E, levels=20, cmap=CMAP_SEQ)
cl = ax.contour(X, Y, E, levels=[10, 20, 30, 40], colors="0.15",
linewidths=0.5)
ax.clabel(cl, fmt="%.0f", fontsize=FS - 3, inline=True)
ax.set_xlabel("轴向位置 " + IT["x"] + " / mm")
ax.set_ylabel("径向位置 " + IT["y"] + " / mm")
_cbar(fig, ax, cs, "电场强度 " + IT["E"] + " / (kV/mm)")
save(fig, "14_等值线", "14_等值线_电缆终端电场")
def cont_05():
"""变压器铁心损耗分布等值线。"""
fig, ax = new_fig(left=0.17, bottom=0.19, right=0.86)
x = np.linspace(0, 200, 180)
y = np.linspace(0, 160, 180)
X, Y = np.meshgrid(x, y)
P = 2.1 + 1.4 * np.sin(X / 42 + 0.6) * np.cos(Y / 38) \
+ 0.8 * np.exp(-(((X - 100) / 70) ** 2 + ((Y - 80) / 56) ** 2))
cs = ax.contourf(X, Y, P, levels=18, cmap=CMAP_SEQ)
cl = ax.contour(X, Y, P, levels=[1.5, 2.0, 2.5, 3.0], colors="0.15",
linewidths=0.5)
ax.clabel(cl, fmt="%.1f", fontsize=FS - 3, inline=True)
ax.set_xlabel("铁心长度 " + IT["x"] + " / mm")
ax.set_ylabel("铁心高度 " + IT["y"] + " / mm")
ax.set_aspect("equal")
_cbar(fig, ax, cs, "单位损耗 / (W/kg)")
save(fig, "14_等值线", "14_等值线_铁心损耗分布")
# ==================== 15. 网格图 pcolormesh ====================
def pcm_01():
"""非均匀网格下母线室温度场。"""
fig, ax = new_fig(left=0.17, bottom=0.19, right=0.86)
xe = np.concatenate([np.linspace(0, 0.5, 26), np.linspace(0.5, 1.4, 12)[1:]])
ye = np.concatenate([np.linspace(0, 0.4, 20), np.linspace(0.4, 1.1, 10)[1:]])
X, Y = np.meshgrid(xe, ye)
Z = 30 + 26 * np.exp(-((X - 0.55) / 0.28) ** 2) * np.exp(-((Y - 0.5) / 0.32) ** 2) \
+ 6 * Y
m = ax.pcolormesh(X, Y, Z, cmap=CMAP_SEQ, shading="auto", rasterized=True)
ax.set_xlabel("柜宽 " + IT["x"] + " / m")
ax.set_ylabel("柜高 " + IT["y"] + " / m")
_cbar(fig, ax, m, "温度 " + IT["T"] + " / °C")
save(fig, "15_网格图", "15_网格图_非均匀温度场")
def pcm_02():
"""时频能量分布(非均匀频率轴)。"""
fig, ax = new_fig(left=0.17, bottom=0.19, right=0.86)
rng = np.random.default_rng(601)
t = np.linspace(0, 2.0, 60)
f = np.concatenate([np.linspace(20, 100, 20), np.linspace(100, 2000, 40)[1:]])
T, F = np.meshgrid(t, f)
Z = 6 * np.exp(-((F - 150) / 120) ** 2) * (0.6 + 0.4 * np.sin(2 * np.pi * 1.5 * T)) \
+ 4 * np.exp(-((F - 850) / 300) ** 2) * (0.5 + 0.5 * np.cos(2 * np.pi * 3 * T)) \
+ rng.gamma(1.6, 0.35, F.shape)
m = ax.pcolormesh(T, F, Z, cmap=CMAP_SEQ, shading="auto",
norm=LogNorm(vmin=0.3, vmax=12),
rasterized=True)
ax.set_yscale("log")
ax.set_xlabel("时间 " + IT["t"] + " / s")
ax.set_ylabel("频率 " + IT["f"] + " / Hz")
_cbar(fig, ax, m, "能量密度 / (V²/Hz)")
save(fig, "15_网格图", "15_网格图_时频能量分布")
def pcm_03():
"""供电分区负荷密度网格图。"""
fig, ax = new_fig(left=0.17, bottom=0.19, right=0.86)
rng = np.random.default_rng(602)
xe = np.linspace(0, 12, 25)
ye = np.linspace(0, 9, 19)
X, Y = np.meshgrid(xe, ye)
Z = (1200 * np.exp(-(((X - 4.2) / 2.2) ** 2 + ((Y - 4.6) / 1.8) ** 2))
+ 900 * np.exp(-(((X - 9.0) / 1.6) ** 2 + ((Y - 3.0) / 1.4) ** 2))
+ rng.normal(0, 40, X.shape)).clip(20)
m = ax.pcolormesh(X, Y, Z, cmap=CMAP_SEQ, shading="gouraud", rasterized=True)
ax.set_xlabel("东向距离 " + IT["x"] + " / km")
ax.set_ylabel("北向距离 " + IT["y"] + " / km")
ax.set_aspect("equal")
_cbar(fig, ax, m, "负荷密度 / (kW/km²)")
save(fig, "15_网格图", "15_网格图_供电分区负荷密度")
def pcm_04():
"""风电场风功率密度分布。"""
fig, ax = new_fig(left=0.17, bottom=0.19, right=0.86)
rng = np.random.default_rng(603)
xe = np.linspace(0, 6, 31)
ye = np.linspace(0, 4, 21)
X, Y = np.meshgrid(xe, ye)
Z = 320 + 180 * np.exp(-(((X - 2.0) / 1.4) ** 2 + ((Y - 2.4) / 1.0) ** 2)) \
+ 140 * np.exp(-(((X - 4.6) / 1.0) ** 2 + ((Y - 1.3) / 0.9) ** 2)) \
+ rng.normal(0, 16, X.shape)
m = ax.pcolormesh(X, Y, Z, cmap=CMAP_SEQ, shading="nearest", rasterized=True)
ax.set_xlabel("东向距离 " + IT["x"] + " / km")
ax.set_ylabel("北向距离 " + IT["y"] + " / km")
ax.set_aspect("equal")
_cbar(fig, ax, m, "风功率密度 / (W/m²)")
save(fig, "15_网格图", "15_网格图_风功率密度分布")
def pcm_05():
"""绝缘子表面电位分布(不规则极坐标网格映射)。"""
fig, ax = new_fig(left=0.17, bottom=0.19, right=0.86)
r = np.linspace(0, 1.0, 46)
z = np.concatenate([np.linspace(0, 0.15, 12), np.linspace(0.15, 1.2, 30)[1:]])
R, Zc = np.meshgrid(r, z)
V = 100 * np.exp(-((R - 0.35) / 0.30) ** 2) * (1 - 0.45 * Zc / 1.2) \
+ 22 * np.sin(Zc * 9) ** 2 * np.exp(-((R - 0.55) / 0.2) ** 2)
m = ax.pcolormesh(R, Zc, V, cmap=CMAP_SEQ, shading="auto", rasterized=True)
ax.set_xlabel("径向位置 " + IT["r"] + " / m")
ax.set_ylabel("轴向位置 " + IT["z"] + " / m")
_cbar(fig, ax, m, "表面电位 " + IT["U"] + " / kV")
save(fig, "15_网格图", "15_网格图_绝缘子表面电位")
ALL = (fill_01, fill_02, fill_03, fill_04, fill_05,
step_01, step_02, step_03, step_04, step_05,
stack_01, stack_02, stack_03, stack_04, stack_05,
heat_01, heat_02, heat_03, heat_04, heat_05,
cont_01, cont_02, cont_03, cont_04, cont_05,
pcm_01, pcm_02, pcm_03, pcm_04, pcm_05)
if __name__ == "__main__":
for fn in ALL:
fn()
print("第三批 30 张完成")
5.5 第四批_信号与三维261106.py
# -*- coding: utf-8 -*-
"""第四批:频谱、向量场、极坐标、三维曲面、三维柱状(各 5 个,共 25 张)
执行方式:在本 code/ 目录下运行
python 第四批_信号与三维261106.py
"""
from 公共配置261106 import (
C1, C2, C3, C4, C5, C6, COLORS, MARKERS, LINES, IT, SUB,
FS, np, new_fig, new_fig3d, legend, save, PALETTE5,
)
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
from mpl_toolkits.mplot3d import Axes3D # noqa: F401 注册 3d projection
CMAP_SEQ = LinearSegmentedColormap.from_list(
"elec_seq", ["#F4F9FD", "#CFE1F1", "#93B8DC", "#4E82B8", "#1E4E86"])
def new_fig_polar(w_mm: float = 100, h_mm: float = 90):
"""极坐标画布(new_fig 不支持 projection='polar')。"""
fig = plt.figure(figsize=(w_mm / 25.4, h_mm / 25.4))
ax = fig.add_subplot(111, projection="polar")
fig.subplots_adjust(left=0.20, bottom=0.16, right=0.92, top=0.94)
ax.tick_params(labelsize=FS - 1, direction="in", pad=1)
ax.grid(linewidth=0.4, color="0.75", linestyle="-")
ax.spines["polar"].set_linewidth(0.5)
return fig, ax
# ==================== 16. 频谱分析 ====================
def psd_01():
"""母线电压谐波功率谱(含 3/5/7 次谐波)。"""
fig, ax = new_fig()
rng = np.random.default_rng(701)
fs = 6400
t = np.arange(0, 1.0, 1 / fs)
x = (311 * np.sin(2 * np.pi * 50 * t)
+ 18 * np.sin(2 * np.pi * 150 * t + 0.4)
+ 26 * np.sin(2 * np.pi * 250 * t + 1.1)
+ 11 * np.sin(2 * np.pi * 350 * t + 2.0)
+ rng.normal(0, 2.0, t.size))
ax.psd(x, NFFT=2048, Fs=fs, color=C1, lw=0.9, label="电压信号")
ax.set_xscale("log")
ax.set_xlim(10, 3200)
ax.grid(False)
ax.set_xlabel("频率 " + IT["f"] + " / Hz")
ax.set_ylabel("功率谱密度 / dB")
legend(ax, "upper right", ncol=1)
save(fig, "16_频谱", "16_频谱_母线电压谐波功率谱")
def psd_02():
"""变频器输出电流谐波频谱(柱状对数纵轴)。"""
fig, ax = new_fig()
rng = np.random.default_rng(702)
h = np.arange(1, 31)
amp = 120 / h ** 1.1
amp[2], amp[4], amp[6] = 26, 38, 17
amp = amp + rng.uniform(0, 2.2, h.size)
ax.bar(h * 50, amp, width=14, color=C1, edgecolor="0.35", linewidth=0.4,
label="谐波幅值")
ax.set_yscale("log")
ax.set_xlabel("频率 " + IT["f"] + " / Hz")
ax.set_ylabel("电流幅值 " + IT["I"] + SUB["h"] + " / A")
ax.set_xlim(0, 1550)
ax.set_ylim(1, 200)
legend(ax, "upper right", ncol=1)
save(fig, "16_频谱", "16_频谱_变频器电流谐波")
def psd_03():
"""异步电机振动加速度频谱(含转频与轴承故障频率)。"""
fig, ax = new_fig()
rng = np.random.default_rng(703)
fs = 5000
t = np.arange(0, 2.0, 1 / fs)
x = (1.8 * np.sin(2 * np.pi * 24.7 * t)
+ 0.9 * np.sin(2 * np.pi * 49.4 * t)
+ 0.55 * np.sin(2 * np.pi * 148 * t)
+ 0.35 * np.sin(2 * np.pi * 312 * t)
+ rng.normal(0, 0.12, t.size))
ax.magnitude_spectrum(x, Fs=fs, color=C1, lw=0.8, scale="dB",
label="振动信号")
ax.grid(False)
ax.set_xlim(0, 900)
ax.set_xlabel("频率 " + IT["f"] + " / Hz")
ax.set_ylabel("幅值 / dB")
legend(ax, "upper right", ncol=1)
save(fig, "16_频谱", "16_频谱_电机振动加速度谱")
def psd_04():
"""风电功率波动的功率谱密度。"""
fig, ax = new_fig()
rng = np.random.default_rng(704)
n = 4096
f = np.fft.rfftfreq(n, d=1.0)
P = np.zeros_like(f)
P[1:] = 1e3 / f[1:] ** (5 / 3)
P[1:] *= rng.gamma(3.0, 1 / 3.0, P.size - 1)
ax.loglog(f[1:], P[1:], color=C5, lw=0.8, label="功率谱")
fk = np.array([1e-3, 5e-1])
ax.loglog(fk, 2.2e1 / fk ** (5 / 3), color=C2, lw=1.0, ls="--",
label="−5/3 参考斜率")
ax.set_xlim(1e-3, 5e-1)
ax.set_xlabel("频率 " + IT["f"] + " / Hz")
ax.set_ylabel("功率谱密度 / (kW²/Hz)")
legend(ax, "lower left", ncol=1)
save(fig, "16_频谱", "16_频谱_风电功率波动谱")
def psd_05():
"""并网逆变器开关频率附近噪声频谱。"""
fig, ax = new_fig()
rng = np.random.default_rng(705)
f = np.linspace(2000, 20000, 900)
base = 1.2e-3 * (f / 2000) ** -0.45
peak = 8e-3 * np.exp(-((f - 10000) / 420) ** 2) \
+ 3e-3 * np.exp(-((f - 20000) / 380) ** 2) \
+ 1.6e-3 * np.exp(-((f - 5000) / 300) ** 2)
y = base + peak + rng.uniform(0, 2.4e-4, f.size)
ax.semilogy(f, y, color=C1, lw=0.8, label="噪声谱")
ax.set_xlabel("频率 " + IT["f"] + " / Hz")
ax.set_ylabel("电压噪声密度 / (V/√Hz)")
ax.set_xlim(2000, 20000)
legend(ax, "upper right", ncol=1)
save(fig, "16_频谱", "16_频谱_逆变器开关噪声")
# ==================== 17. 向量场 quiver ====================
def quiver_01():
"""双极板间电场强度矢量场。"""
fig, ax = new_fig(left=0.17, right=0.86)
x = np.linspace(-1.0, 1.0, 17)
y = np.linspace(-1.0, 1.0, 17)
X, Y = np.meshgrid(x, y)
R1 = np.sqrt((X + 0.45) ** 2 + Y ** 2) + 0.08
R2 = np.sqrt((X - 0.45) ** 2 + Y ** 2) + 0.08
Ex = (X + 0.45) / R1 ** 3 - (X - 0.45) / R2 ** 3
Ey = Y / R1 ** 3 - Y / R2 ** 3
mag = np.hypot(Ex, Ey)
q = ax.quiver(X, Y, Ex, Ey, mag, cmap=CMAP_SEQ, scale=90,
width=0.0035, pivot="mid")
ax.quiverkey(q, 0.86, 1.04, 20, r"20 kV/m", labelpos="E", fontproperties={"size": FS - 2})
ax.set_xlabel("水平位置 " + IT["x"] + " / m")
ax.set_ylabel("垂直位置 " + IT["y"] + " / m")
ax.set_aspect("equal")
ax.set_xlim(-1.05, 1.05)
ax.set_ylim(-1.05, 1.05)
cb = fig.colorbar(q, ax=ax, pad=0.02, fraction=0.05)
cb.set_label("场强 " + IT["E"] + " / (kV/m)")
cb.ax.tick_params(direction="in", labelsize=FS - 2, length=2.5, width=0.5)
cb.outline.set_linewidth(0.5)
save(fig, "17_向量场", "17_向量场_双极板电场矢量")
def quiver_02():
"""单根载流导线周围磁场矢量场。"""
fig, ax = new_fig(left=0.17)
x = np.linspace(-0.5, 0.5, 15)
y = np.linspace(-0.5, 0.5, 15)
X, Y = np.meshgrid(x, y)
R2 = X ** 2 + Y ** 2 + 0.012
Hx = -Y / R2
Hy = X / R2
mag = np.hypot(Hx, Hy)
q = ax.quiver(X, Y, Hx, Hy, np.log10(mag + 1), cmap=CMAP_SEQ, scale=26,
width=0.0035, pivot="mid")
ax.plot(0, 0, marker="o", ms=5, color=C2)
ax.text(0.03, 0.03, "导线", fontsize=FS - 1, color=C2)
ax.set_xlabel("水平位置 " + IT["x"] + " / m")
ax.set_ylabel("垂直位置 " + IT["y"] + " / m")
ax.set_aspect("equal")
ax.set_xlim(-0.55, 0.55)
ax.set_ylim(-0.55, 0.55)
save(fig, "17_向量场", "17_向量场_载流导线磁场")
def quiver_03():
"""配电网潮流方向与有功流动。"""
fig, ax = new_fig(left=0.17)
nodes = {
"S": (0.0, 0.0), "N1": (1.5, 0.6), "N2": (1.4, -0.9),
"N3": (3.0, 0.2), "N4": (3.2, -1.1),
}
edges = [("S", "N1", 0.62), ("S", "N2", 0.48), ("N1", "N3", 0.41),
("N2", "N4", 0.33)]
for a, b, w in edges:
xa, ya = nodes[a]
xb, yb = nodes[b]
ax.quiver(xa, ya, xb - xa, yb - ya, angles="xy",
scale_units="xy", scale=1 / 0.86, width=0.006,
color=C1, headwidth=4.5, headlength=6)
ax.text((xa + xb) / 2, (ya + yb) / 2 + 0.09, f"{w:.2f}",
fontsize=FS - 2, ha="center", color=C4)
for k, (x, y) in nodes.items():
ax.plot(x, y, "o", ms=6, color=C2)
ax.text(x, y - 0.26, k, fontsize=FS - 1, ha="center")
ax.set_xlabel("东向距离 " + IT["x"] + " / km")
ax.set_ylabel("北向距离 " + IT["y"] + " / km")
ax.set_xlim(-0.5, 3.9)
ax.set_ylim(-1.7, 1.2)
save(fig, "17_向量场", "17_向量场_配电网潮流方向")
def quiver_04():
"""风电场轮毂高度风速矢量场。"""
fig, ax = new_fig(left=0.17, right=0.86)
x = np.linspace(0, 6, 13)
y = np.linspace(0, 4, 11)
X, Y = np.meshgrid(x, y)
U = 9.5 + 1.6 * np.sin(Y / 1.3) * np.cos(X / 2.4)
V = 1.4 * np.cos(X / 1.9) * np.sin(Y / 1.1) - 0.5
mag = np.hypot(U, V)
q = ax.quiver(X, Y, U, V, mag, cmap=CMAP_SEQ, scale=115,
width=0.0035, pivot="mid")
ax.quiverkey(q, 0.86, 1.04, 4, r"4 m/s", labelpos="E",
fontproperties={"size": FS - 2})
ax.set_xlabel("东向距离 " + IT["x"] + " / km")
ax.set_ylabel("北向距离 " + IT["y"] + " / km")
ax.set_aspect("equal")
ax.set_xlim(-0.2, 6.2)
ax.set_ylim(-0.2, 4.2)
cb = fig.colorbar(q, ax=ax, pad=0.02, fraction=0.05)
cb.set_label("风速 " + IT["v"] + " / (m/s)")
cb.ax.tick_params(direction="in", labelsize=FS - 2, length=2.5, width=0.5)
cb.outline.set_linewidth(0.5)
save(fig, "17_向量场", "17_向量场_风电场风速矢量")
def quiver_05():
"""接地网附近地表电流密度矢量。"""
fig, ax = new_fig(left=0.17, right=0.86)
x = np.linspace(-20, 20, 15)
y = np.linspace(-20, 20, 15)
X, Y = np.meshgrid(x, y)
R = np.sqrt(X ** 2 + Y ** 2) + 1.5
Jx = X / R ** 2 * 240
Jy = Y / R ** 2 * 240
mag = np.hypot(Jx, Jy)
q = ax.quiver(X, Y, Jx, Jy, mag, cmap=CMAP_SEQ, scale=520,
width=0.0035, pivot="mid")
ax.plot(0, 0, marker="s", ms=6, color=C2)
ax.set_xlabel("东向距离 " + IT["x"] + " / m")
ax.set_ylabel("北向距离 " + IT["y"] + " / m")
ax.set_aspect("equal")
ax.set_xlim(-22, 22)
ax.set_ylim(-22, 22)
cb = fig.colorbar(q, ax=ax, pad=0.02, fraction=0.05)
cb.set_label("电流密度 / (A/m²)")
cb.ax.tick_params(direction="in", labelsize=FS - 2, length=2.5, width=0.5)
cb.outline.set_linewidth(0.5)
save(fig, "17_向量场", "17_向量场_接地网地表电流密度")
# ==================== 18. 极坐标 polar ====================
def polar_01():
"""三相电压相量图。"""
fig, ax = new_fig_polar()
amps = [10.12, 10.06, 10.15]
angs = [np.pi / 2, np.pi / 2 - 2 * np.pi / 3, np.pi / 2 + 2 * np.pi / 3]
names = ["A 相", "B 相", "C 相"]
for i, (a, th, nm) in enumerate(zip(amps, angs, names)):
ax.annotate("", xy=(th, a), xytext=(0, 0),
arrowprops={"arrowstyle": "-|>", "color": COLORS[i],
"lw": 1.2})
ax.text(th, a + 1.15, nm, ha="center", va="center", fontsize=FS - 1,
color=COLORS[i])
ax.set_ylim(0, 13.2)
ax.set_yticks([2, 4, 6, 8, 10, 12])
ax.set_yticklabels(["2", "4", "6", "8", "10", "12"])
ax.set_rlabel_position(22)
ax.set_xlabel("径向:电压幅值 " + IT["U"] + " / kV")
save(fig, "18_极坐标", "18_极坐标_三相电压相量")
def polar_02():
"""功率因数随负荷变化的极坐标圆图。"""
fig, ax = new_fig_polar()
th = np.linspace(0, np.pi / 2, 120)
for i, (pf, nm) in enumerate([(0.85, "cos φ = 0.85"), (0.90, "0.90"),
(0.95, "0.95")]):
ang = np.arccos(pf)
r = 100 * np.cos(ang) / np.maximum(np.cos(th), 1e-6)
r = np.clip(r, 0, 140)
ax.plot(np.full_like(th, ang), np.linspace(0, 140, th.size),
color=COLORS[i], lw=1.0, ls=LINES[i], label=nm)
ax.set_ylim(0, 150)
ax.set_thetamin(0)
ax.set_thetamax(90)
ax.set_xlabel("有功功率 " + IT["P"] + " / kW")
legend(ax, "upper right", ncol=1, fontsize=FS - 2)
save(fig, "18_极坐标", "18_极坐标_功率因数圆图")
def polar_03():
"""风向玫瑰图(16 方位风速频率)。"""
fig, ax = new_fig_polar()
n = 16
theta = np.arange(n) * 2 * np.pi / n
freq = np.array([4.2, 3.1, 2.4, 2.8, 3.6, 5.2, 7.8, 9.6,
11.4, 8.9, 6.4, 5.1, 4.6, 3.9, 3.4, 3.8])
width = 2 * np.pi / n * 0.92
bars = ax.bar(theta, freq, width=width, bottom=0, color=C5,
edgecolor="0.35", linewidth=0.5, label="风频")
for b, f in zip(bars, freq):
if f >= 9.0:
b.set_facecolor(C3)
ax.set_theta_zero_location("N")
ax.set_theta_direction(-1)
ax.set_rlabel_position(112)
ax.set_ylim(0, 13)
ax.set_xlabel("径向:风频 / %")
save(fig, "18_极坐标", "18_极坐标_风向玫瑰图")
def polar_04():
"""变电站局部放电超声阵列方向响应。"""
fig, ax = new_fig_polar()
th = np.linspace(0, 2 * np.pi, 720)
main = np.exp(-((th - np.pi / 2) / 0.16) ** 2)
side1 = 0.28 * np.exp(-((th - np.pi / 2 - 0.85) / 0.13) ** 2)
side2 = 0.28 * np.exp(-((th - np.pi / 2 + 0.85) / 0.13) ** 2)
back = 0.10 * np.exp(-((th + np.pi / 2) / 0.35) ** 2)
r = 10 * np.log10(main + side1 + side2 + back + 1e-3) + 26
ax.plot(th, np.clip(r, 0, 30), color=C1, lw=1.0, label="归一化响应")
ax.fill_between(th, 0, np.clip(r, 0, 30), color=C5, alpha=0.35, linewidth=0)
ax.set_theta_zero_location("N")
ax.set_theta_direction(-1)
ax.set_rlabel_position(135)
ax.set_ylim(0, 34)
ax.set_xlabel("径向:增益 / dB")
save(fig, "18_极坐标", "18_极坐标_声阵列方向响应")
def polar_05():
"""谐波电流极坐标分布(次数—幅值)。"""
fig, ax = new_fig_polar()
orders = np.array([2, 3, 4, 5, 6, 7, 9, 11, 13])
amp = np.array([4.2, 38.6, 2.1, 26.4, 1.3, 15.8, 6.2, 3.4, 1.8])
th = orders / orders.max() * 2 * np.pi
bars = ax.bar(th, amp, width=0.24, bottom=0, color=C5, edgecolor="0.35",
linewidth=0.5, label="谐波幅值")
for b, a in zip(bars, amp):
if a > 20:
b.set_facecolor(C2)
ax.set_theta_zero_location("E")
ax.set_rlabel_position(112)
ax.set_ylim(0, 44)
ax.set_xlabel("径向:谐波电流 " + IT["I"] + SUB["h"] + " / A")
save(fig, "18_极坐标", "18_极坐标_谐波电流分布")
# ==================== 19. 三维曲面 plot_surface ====================
def surf_01():
"""电机效率 MAP 三维曲面。"""
fig, ax = new_fig3d(right=0.86, left=0.13)
n = np.linspace(600, 3000, 60)
tq = np.linspace(0, 120, 60)
N, T = np.meshgrid(n, tq)
Z = 94.5 - 8.0 * ((T - 62) / 90) ** 2 - 3.4 * ((N - 2100) / 1500) ** 2 \
- 2.0 * ((T - 40) / 80) * ((N - 1800) / 1400)
s = ax.plot_surface(N, T, Z, cmap=CMAP_SEQ, linewidth=0.15,
edgecolor="0.45", antialiased=True, rstride=2, cstride=2)
ax.set_xlabel("转速 " + IT["n"] + " / (r/min)", labelpad=2)
ax.set_ylabel("转矩 " + IT["T"] + " / (N·m)", labelpad=2)
ax.set_zlabel("效率 " + IT["eta"] + " / %", labelpad=2)
ax.view_init(elev=26, azim=-128)
cb = fig.colorbar(s, ax=ax, pad=0.06, fraction=0.03, shrink=0.72)
cb.set_label("效率 " + IT["eta"] + " / %")
cb.ax.tick_params(direction="in", labelsize=FS - 3, length=2, width=0.5)
cb.outline.set_linewidth(0.5)
save(fig, "19_三维曲面", "19_三维曲面_电机效率MAP")
def surf_02():
"""光伏组件 P-V 特性曲面(不同辐照度与温度)。"""
fig, ax = new_fig3d(right=0.86, left=0.13)
u = np.linspace(0, 42, 70)
g = np.linspace(0.2, 1.0, 40)
U, G = np.meshgrid(u, g)
isc = 9.8 * G
P = U * isc * (1 - np.exp((U - 42 * (0.86 + 0.14 * G)) / 3.2))
P = np.clip(P, 0, None)
s = ax.plot_surface(U, G * 1000, P, cmap=CMAP_SEQ, linewidth=0.15,
edgecolor="0.45", antialiased=True, rstride=2, cstride=2)
ax.set_xlabel("电压 " + IT["U"] + " / V", labelpad=2)
ax.set_ylabel("辐照度 " + IT["G"] + " / (W/m²)", labelpad=2)
ax.set_zlabel("功率 " + IT["P"] + " / W", labelpad=2)
ax.view_init(elev=24, azim=-136)
cb = fig.colorbar(s, ax=ax, pad=0.06, fraction=0.03, shrink=0.72)
cb.set_label("功率 " + IT["P"] + " / W")
cb.ax.tick_params(direction="in", labelsize=FS - 3, length=2, width=0.5)
cb.outline.set_linewidth(0.5)
save(fig, "19_三维曲面", "19_三维曲面_光伏P-V特性")
def surf_03():
"""母线室温度分布三维曲面。"""
fig, ax = new_fig3d(right=0.86, left=0.13)
x = np.linspace(0, 1.4, 60)
y = np.linspace(0, 1.1, 50)
X, Y = np.meshgrid(x, y)
Z = 30 + 26 * np.exp(-((X - 0.55) / 0.30) ** 2) * np.exp(-((Y - 0.5) / 0.34) ** 2) \
+ 6 * Y + 3 * np.sin(X * 4) * np.cos(Y * 3)
s = ax.plot_surface(X, Y, Z, cmap=CMAP_SEQ, linewidth=0.12,
edgecolor="0.5", antialiased=True, rstride=2, cstride=2)
ax.set_xlabel("柜宽 " + IT["x"] + " / m", labelpad=2)
ax.set_ylabel("柜高 " + IT["y"] + " / m", labelpad=2)
ax.set_zlabel("温度 " + IT["T"] + " / °C", labelpad=2)
ax.view_init(elev=28, azim=-122)
cb = fig.colorbar(s, ax=ax, pad=0.06, fraction=0.03, shrink=0.72)
cb.set_label("温度 " + IT["T"] + " / °C")
cb.ax.tick_params(direction="in", labelsize=FS - 3, length=2, width=0.5)
cb.outline.set_linewidth(0.5)
save(fig, "19_三维曲面", "19_三维曲面_母线室温度分布")
def surf_04():
"""LC 滤波器幅频响应曲面。"""
fig, ax = new_fig3d(right=0.86, left=0.13)
f = np.logspace(1, 4.3, 80)
q = np.linspace(0.5, 8, 40)
F, Q = np.meshgrid(f, q)
fr = 800.0
H = 1 / np.sqrt((1 - (F / fr) ** 2) ** 2 + (F / (fr * Q)) ** 2)
HdB = 20 * np.log10(np.clip(H, 1e-4, None))
s = ax.plot_surface(np.log10(F), Q, HdB, cmap=CMAP_SEQ, linewidth=0.15,
edgecolor="0.45", antialiased=True, rstride=2, cstride=2)
ax.set_xlabel("lg(" + IT["f"] + " / Hz)", labelpad=2)
ax.set_ylabel("品质因数 " + IT["Q"], labelpad=2)
ax.set_zlabel("幅值 / dB", labelpad=2)
ax.view_init(elev=24, azim=-132)
ax.set_zlim(-60, 30)
cb = fig.colorbar(s, ax=ax, pad=0.06, fraction=0.03, shrink=0.72)
cb.set_label("幅值 / dB")
cb.ax.tick_params(direction="in", labelsize=FS - 3, length=2, width=0.5)
cb.outline.set_linewidth(0.5)
save(fig, "19_三维曲面", "19_三维曲面_LC滤波器频响")
def surf_05():
"""锂电池放电容量特性:倍率与温度的影响。"""
fig, ax = new_fig3d(left=0.13)
c = np.linspace(0.2, 3.0, 60)
t = np.linspace(-20, 45, 50)
C, T = np.meshgrid(c, t)
cap = 100 * (1 - 0.11 * (C - 0.2) ** 1.25) * (1 - 0.0042 * np.abs(T - 25) ** 1.4)
cap = np.clip(cap, 45, 102)
s = ax.plot_surface(C, T, cap, cmap=CMAP_SEQ, linewidth=0.15,
edgecolor="0.45", antialiased=True, rstride=2, cstride=2)
ax.set_xlabel("放电倍率 " + IT["C"], labelpad=2)
ax.set_ylabel("温度 " + IT["T"] + " / °C", labelpad=2)
ax.set_zlabel("可用容量 / %", labelpad=2)
ax.view_init(elev=25, azim=-130)
save(fig, "19_三维曲面", "19_三维曲面_电池容量特性")
# ==================== 20. 三维柱状 bar3d ====================
def bar3d_01():
"""各月各时段负荷三维柱状图。"""
fig, ax = new_fig3d(left=0.13)
months = np.arange(1, 7)
segs = np.arange(0, 4)
vals = np.array([
[420, 580, 640, 470],
[398, 552, 612, 445],
[362, 508, 566, 412],
[336, 470, 524, 386],
[348, 486, 542, 398],
[404, 566, 626, 458],
])
X, Y = np.meshgrid(months, segs, indexing="ij")
X = X.ravel() - 0.32
Y = Y.ravel() - 0.32
dz = vals.ravel()
colors = plt.get_cmap(CMAP_SEQ)(dz / dz.max())
ax.bar3d(X, Y, np.zeros_like(dz), 0.64, 0.64, dz, color=colors,
edgecolor="0.4", linewidth=0.2, shade=True)
ax.set_xticks(months)
ax.set_xticklabels([str(m) for m in months])
ax.set_yticks([0, 1, 2, 3])
ax.set_yticklabels(["0-6", "6-12", "12-18", "18-24"])
ax.set_xlabel("月份", labelpad=2)
ax.set_ylabel("时段 / h", labelpad=2)
ax.set_zlabel("负荷 " + IT["P"] + " / kW", labelpad=2)
ax.view_init(elev=22, azim=-126)
ax.set_zlim(0, 780)
save(fig, "20_三维柱状", "20_三维柱状_月时负荷分布")
def bar3d_02():
"""各节点各次谐波幅值三维柱状图。"""
fig, ax = new_fig3d(left=0.13)
nodes = np.arange(1, 6)
orders = np.array([3, 5, 7, 9])
vals = np.array([
[32.6, 22.4, 13.8, 5.2],
[28.4, 19.6, 11.2, 4.4],
[36.8, 25.8, 15.4, 6.0],
[24.2, 17.2, 9.8, 3.8],
[30.8, 21.6, 12.6, 4.9],
])
X, Y = np.meshgrid(nodes, np.arange(len(orders)), indexing="ij")
X = X.ravel() - 0.3
Y = Y.ravel() - 0.3
dz = vals.ravel()
colors = plt.get_cmap(CMAP_SEQ)(dz / dz.max())
ax.bar3d(X, Y, np.zeros_like(dz), 0.6, 0.6, dz, color=colors,
edgecolor="0.4", linewidth=0.2, shade=True)
ax.set_xticks(nodes)
ax.set_xticklabels([f"N{i}" for i in nodes])
ax.set_yticks([0, 1, 2, 3])
ax.set_yticklabels([str(o) for o in orders])
ax.set_xlabel("节点", labelpad=2)
ax.set_ylabel("谐波次数 " + IT["h"], labelpad=2)
ax.set_zlabel("电流幅值 " + IT["I"] + SUB["h"] + " / A", labelpad=2)
ax.view_init(elev=24, azim=-124)
ax.set_zlim(0, 42)
save(fig, "20_三维柱状", "20_三维柱状_节点谐波幅值")
def bar3d_03():
"""三种方案三项指标对比三维柱状图。"""
fig, ax = new_fig3d(left=0.22)
plans = np.arange(3)
metrics = np.arange(3)
vals = np.array([
[4.62, 12.8, 86.4],
[3.24, 9.6, 92.1],
[2.86, 8.2, 94.8],
])
X, Y = np.meshgrid(plans, metrics, indexing="ij")
X = X.ravel() - 0.3
Y = Y.ravel() - 0.3
dz = vals.ravel()
colors = plt.get_cmap(CMAP_SEQ)(dz / dz.max())
ax.bar3d(X, Y, np.zeros_like(dz), 0.6, 0.6, dz, color=colors,
edgecolor="0.4", linewidth=0.2, shade=True)
ax.set_xticks(plans)
ax.set_xticklabels(["原状", "方案一", "方案二"])
ax.set_yticks([0, 1, 2])
ax.set_yticklabels(["线损率/%", "投资/百万", "效率/%"])
ax.set_xlabel("方案", labelpad=2)
ax.set_ylabel("指标", labelpad=2)
ax.set_zlabel("指标值", labelpad=2)
ax.view_init(elev=24, azim=-128)
ax.set_zlim(0, 105)
save(fig, "20_三维柱状", "20_三维柱状_方案指标对比")
def bar3d_04():
"""各馈线四季负载率三维柱状图。"""
fig, ax = new_fig3d(left=0.13)
feeders = np.arange(1, 6)
seasons = np.arange(4)
vals = np.array([
[72, 88, 76, 68],
[64, 79, 70, 61],
[81, 96, 84, 74],
[58, 71, 63, 55],
[69, 85, 74, 66],
])
X, Y = np.meshgrid(feeders, seasons, indexing="ij")
X = X.ravel() - 0.3
Y = Y.ravel() - 0.3
dz = vals.ravel()
colors = plt.get_cmap(CMAP_SEQ)(dz / dz.max())
ax.bar3d(X, Y, np.zeros_like(dz), 0.6, 0.6, dz, color=colors,
edgecolor="0.4", linewidth=0.2, shade=True)
ax.set_xticks(feeders)
ax.set_xticklabels([f"L{i}" for i in feeders])
ax.set_yticks([0, 1, 2, 3])
ax.set_yticklabels(["春", "夏", "秋", "冬"])
ax.set_xlabel("馈线", labelpad=2)
ax.set_ylabel("季节", labelpad=2)
ax.set_zlabel("负载率 / %", labelpad=2)
ax.view_init(elev=23, azim=-125)
ax.set_zlim(0, 110)
save(fig, "20_三维柱状", "20_三维柱状_馈线四季负载率")
def bar3d_05():
"""各设备容量五年增长三维柱状图。"""
fig, ax = new_fig3d(left=0.13)
years = np.arange(2022, 2027)
devs = np.arange(4)
vals = np.array([
[420, 260, 180, 90],
[468, 312, 214, 108],
[512, 386, 268, 132],
[566, 452, 318, 158],
[624, 528, 372, 186],
])
X, Y = np.meshgrid(years, devs, indexing="ij")
X = X.ravel() - 0.3
Y = Y.ravel() - 0.3
dz = vals.ravel()
colors = plt.get_cmap(CMAP_SEQ)(dz / dz.max())
ax.bar3d(X, Y, np.zeros_like(dz), 0.6, 0.6, dz, color=colors,
edgecolor="0.4", linewidth=0.2, shade=True)
ax.set_xticks(years)
ax.set_xticklabels([str(y) for y in years])
ax.set_yticks([0, 1, 2, 3])
ax.set_yticklabels(["光伏", "风电", "储能", "充电桩"])
ax.set_xlabel("年份", labelpad=2)
ax.set_ylabel("设备类型", labelpad=2)
ax.set_zlabel("装机容量 " + IT["S"] + " / MW", labelpad=2)
ax.view_init(elev=23, azim=-127)
ax.set_zlim(0, 700)
save(fig, "20_三维柱状", "20_三维柱状_设备容量增长")
ALL = (psd_01, psd_02, psd_03, psd_04, psd_05,
quiver_01, quiver_02, quiver_03, quiver_04, quiver_05,
polar_01, polar_02, polar_03, polar_04, polar_05,
surf_01, surf_02, surf_03, surf_04, surf_05,
bar3d_01, bar3d_02, bar3d_03, bar3d_04, bar3d_05)
if __name__ == "__main__":
for fn in ALL:
fn()
print("第四批 25 张完成")
浙公网安备 33010602011771号