自动化脚本一键绘制三元化合物相图

本文脚本集成了从 Materials Project 拉取数据、自动下载所有竞争相的 POSCAR 文件、到生成带标签避让的交互式/静态三元相图的完整流程。

一行命令即可得到三元化合物体系相图。

示例:Li-P-S 三元体系

【依赖安装】

运行本脚本前,请确保安装以下 Python 库:

pip install numpy matplotlib scipy plotly pymatgen

【API 密钥配置】

本脚本需要 Materials Project API 密钥才能获取数据。

【使用方法】

基本使用(默认 Li-P-S 体系)

python super_unified_script.py

自定义化学体系

python super_unified_script.py --system Li P S

自定义图片尺寸和字体

python super_unified_script.py --fig-width 1800 --fig-height 1600 --title-size 28

使用 Matplotlib 静态图

python super_unified_script.py --matplotlib

【输出文件】

  • phase_diagram_output.html # 交互式 HTML(Plotly)

  • phase_diagram_output.png # 静态图片

  • phase/ # 竞争相结构文件夹

运行示例

$ python super_unified_script-.py

2026-05-0519:45:38,113 - INFO - ============================================================

2026-05-0519:45:38,113 - INFO - Li-P-S 三元相图绘制开始

2026-05-0519:45:38,114 - INFO - ============================================================

2026-05-0519:45:38,114 - INFO - 化学体系: ['Li', 'P', 'S']

2026-05-0519:45:38,114 - INFO - 图片尺寸: 900 x 800

2026-05-0519:45:38,114 - INFO - 使用 Plotly 绘制交互式相图...

2026-05-0519:45:38,114 - INFO - 连接 Materials Project...

2026-05-0519:45:38,650 - INFO - 获取 ['Li', 'P', 'S'] 体系数据...

Retrieving ThermoDoc documents: 100%|████████████████████████████████████████████████████████████████████████████████████████████████| 94/94 [00:00<00:00, 1288446.33it/s]

2026-05-0519:45:40,974 - INFO - 从 MP 获取到 94 个相

2026-05-0519:45:40,975 - INFO - 开始下载 94 个竞争相到 phase_Li_P_S/

2026-05-0519:45:40,979 - INFO - 下载成功: Li_EaH_0.000

2026-05-0519:45:40,980 - INFO - 跳过(已存在): Li_EaH_0.000

2026-05-0519:45:40,980 - INFO - 跳过(已存在): Li_EaH_0.000

2026-05-0519:45:40,980 - INFO - 跳过(已存在): Li_EaH_0.000

2026-05-0519:45:40,980 - INFO - 跳过(已存在): Li_EaH_0.000

2026-05-0519:45:40,981 - INFO - 跳过(已存在): Li_EaH_0.000

2026-05-0519:45:40,981 - INFO - 跳过(已存在): Li_EaH_0.000

2026-05-0519:45:40,981 - INFO - 跳过(已存在): Li_EaH_0.000

2026-05-0519:45:40,981 - INFO - 跳过(已存在): Li_EaH_0.000

2026-05-0519:45:40,984 - INFO - 下载成功: LiP_EaH_0.000

2026-05-0519:45:40,990 - INFO - 下载成功: LiP7_EaH_0.000

2026-05-0519:45:40,995 - INFO - 下载成功: Li3P7_EaH_0.000

2026-05-0519:45:40,998 - INFO - 下载成功: LiP3_EaH_0.000

2026-05-0519:45:41,001 - INFO - 下载成功: Li3P_EaH_0.000

2026-05-0519:45:41,005 - INFO - 下载成功: LiP5_EaH_0.000

2026-05-0519:45:41,006 - INFO - 跳过(已存在): LiP5_EaH_0.000

2026-05-0519:45:41,010 - INFO - 下载成功: Li2S_EaH_0.000

2026-05-0519:45:41,013 - INFO - 下载成功: LiS4_EaH_0.000

2026-05-0519:45:41,016 - INFO - 下载成功: LiS_EaH_0.000

2026-05-0519:45:41,016 - INFO - 跳过(已存在): LiS_EaH_0.000

2026-05-0519:45:41,017 - INFO - 跳过(已存在): Li2S_EaH_0.000

2026-05-0519:45:41,017 - INFO - 跳过(已存在): Li2S_EaH_0.000

2026-05-0519:45:41,017 - INFO - 跳过(已存在): Li2S_EaH_0.000

2026-05-0519:45:41,017 - INFO - 跳过(已存在): LiS_EaH_0.000

2026-05-0519:45:41,017 - INFO - 跳过(已存在): Li2S_EaH_0.000

2026-05-0519:45:41,020 - INFO - 下载成功: P_EaH_0.000

2026-05-0519:45:41,020 - INFO - 跳过(已存在): P_EaH_0.000

2026-05-0519:45:41,020 - INFO - 跳过(已存在): P_EaH_0.000

2026-05-0519:45:41,020 - INFO - 跳过(已存在): P_EaH_0.000

2026-05-0519:45:41,021 - INFO - 跳过(已存在): P_EaH_0.000

2026-05-0519:45:41,021 - INFO - 跳过(已存在): P_EaH_0.000

2026-05-0519:45:41,021 - INFO - 跳过(已存在): P_EaH_0.000

2026-05-0519:45:41,021 - INFO - 跳过(已存在): P_EaH_0.000

2026-05-0519:45:41,021 - INFO - 跳过(已存在): P_EaH_0.000

2026-05-0519:45:41,021 - INFO - 跳过(已存在): P_EaH_0.000

2026-05-0519:45:41,021 - INFO - 跳过(已存在): P_EaH_0.000

2026-05-0519:45:41,021 - INFO - 跳过(已存在): P_EaH_0.000

2026-05-0519:45:41,022 - INFO - 跳过(已存在): P_EaH_0.000

2026-05-0519:45:41,022 - INFO - 跳过(已存在): P_EaH_0.000

2026-05-0519:45:41,025 - INFO - 下载成功: Li3PS4_EaH_0.000

2026-05-0519:45:41,028 - INFO - 下载成功: Li2PS3_EaH_0.000

2026-05-0519:45:41,033 - INFO - 下载成功: Li7P3S11_EaH_0.000

2026-05-0519:45:41,039 - INFO - 下载成功: Li7PS6_EaH_0.000

2026-05-0519:45:41,048 - INFO - 下载成功: Li48P16S61_EaH_0.000

2026-05-0519:45:41,048 - INFO - 跳过(已存在): Li3PS4_EaH_0.000

2026-05-0519:45:41,049 - INFO - 跳过(已存在): Li2PS3_EaH_0.000

2026-05-0519:45:41,049 - INFO - 跳过(已存在): Li3PS4_EaH_0.000

2026-05-0519:45:41,053 - INFO - 下载成功: P2S3_EaH_0.000

2026-05-0519:45:41,057 - INFO - 下载成功: P4S5_EaH_0.000

2026-05-0519:45:41,057 - INFO - 跳过(已存在): P2S3_EaH_0.000

2026-05-0519:45:41,062 - INFO - 下载成功: P2S7_EaH_0.000

2026-05-0519:45:41,068 - INFO - 下载成功: P4S7_EaH_0.000

2026-05-0519:45:41,068 - INFO - 跳过(已存在): P2S3_EaH_0.000

2026-05-0519:45:41,074 - INFO - 下载成功: P4S9_EaH_0.000

2026-05-0519:45:41,077 - INFO - 下载成功: P2S_EaH_0.000

2026-05-0519:45:41,081 - INFO - 下载成功: PS_EaH_0.000

2026-05-0519:45:41,082 - INFO - 跳过(已存在): P2S7_EaH_0.000

2026-05-0519:45:41,086 - INFO - 下载成功: P2S5_EaH_0.000

2026-05-0519:45:41,087 - INFO - 跳过(已存在): P4S9_EaH_0.000

2026-05-0519:45:41,087 - INFO - 跳过(已存在): P2S3_EaH_0.000

2026-05-0519:45:41,088 - INFO - 跳过(已存在): P4S5_EaH_0.000

2026-05-0519:45:41,094 - INFO - 下载成功: P4S3_EaH_0.000

2026-05-0519:45:41,094 - INFO - 跳过(已存在): P4S3_EaH_0.000

2026-05-0519:45:41,099 - INFO - 下载成功: S_EaH_0.000

2026-05-0519:45:41,100 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,100 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,100 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,101 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,101 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,101 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,101 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,101 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,101 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,102 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,102 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,102 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,102 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,102 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,102 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,103 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,103 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,103 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,103 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,103 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,103 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,104 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,104 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,104 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,104 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,104 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,104 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,105 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,105 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,105 - INFO - 跳过(已存在): S_EaH_0.000

2026-05-0519:45:41,105 - INFO - 下载完成:26/94 个相

2026-05-0519:45:41,105 - INFO - 创建相图对象...

2026-05-0519:45:41,126 - INFO - 稳定相数量: 15

2026-05-0519:45:47,299 - INFO - 保存 HTML: phase_diagram_output.html

2026-05-0519:45:47,510 - INFO - 保存 PNG: phase_diagram_output.png

2026-05-0519:45:52,641 - INFO - ======================================================================

2026-05-0519:45:52,642 - INFO - 相图数据统计:

2026-05-0519:45:52,642 - INFO - ======================================================================

2026-05-0519:45:52,642 - INFO - Li3P (0.250, 0.000) E=-3.4816 eV

2026-05-0519:45:52,642 - INFO - LiP7 (0.875, 0.000) E=-5.1305 eV

2026-05-0519:45:52,642 - INFO - P (1.000, 0.000) E=-5.4133 eV

2026-05-0519:45:52,642 - INFO - Li3P7 (0.700, 0.000) E=-4.7216 eV

2026-05-0519:45:52,642 - INFO - LiP (0.500, 0.000) E=-4.1844 eV

2026-05-0519:45:52,642 - INFO - Li (0.000, 0.000) E=-1.9089 eV

2026-05-0519:45:52,643 - INFO - Li2S (0.167, 0.289) E=-4.1552 eV

2026-05-0519:45:52,643 - INFO - P4S3 (0.786, 0.371) E=-5.2280 eV

2026-05-0519:45:52,643 - INFO - Li3PS4 (0.375, 0.433) E=-4.6433 eV

2026-05-0519:45:52,643 - INFO - P4S7 (0.682, 0.551) E=-5.0994 eV

2026-05-0519:45:52,643 - INFO - P4S9 (0.654, 0.600) E=-5.0466 eV

2026-05-0519:45:52,643 - INFO - P2S5 (0.643, 0.619) E=-5.0206 eV

2026-05-0519:45:52,643 - INFO - P2S7 (0.611, 0.674) E=-4.9322 eV

2026-05-0519:45:52,643 - INFO - LiS4 (0.400, 0.693) E=-4.3039 eV

2026-05-0519:45:52,643 - INFO - S (0.500, 0.866) E=-4.1364 eV

2026-05-0519:45:52,644 - INFO -

完成! 输出: phase_diagram_output.html, phase_diagram_output.png

2026-05-0519:45:52,644 - INFO - 脚本执行成功!

如果需要更为严格或不同泛函/计算参数的相图,可直接对所下载下来的相文件进行计算,然后再通过Doped的代码读取并储存不同化合物的能量,然后修改本文代码的文件读入,再重新绘图。

绘制图片图例如下

图片

脚本代码

#!/usr/bin/env python3

-*- coding: utf-8 -*-

"""

============================================================================

三元相图计算与可视化 - 超级统一脚本

============================================================================

【项目介绍】

本脚本用于绘制 Li-P-S 三元体系的相图,从 Materials Project 获取数据,

自动下载所有竞争相结构,并生成交互式/静态相图。

【核心功能】

  1. 从 Materials Project 获取 Li-P-S 体系相图数据

  2. 自动下载所有竞争相的 POSCAR 结构文件到 phase/ 文件夹

  3. 绘制三元相图 (Plotly 交互式 / Matplotlib 静态)

  4. Delaunay 三角剖分 Hull 连线

  5. 标签自动避让算法(防止标签重叠)

  6. 112 色颜色方案(支持 100+ 数据点)

【使用示例】

1. 基本使用(默认 Li-P-S 体系)

python super_unified_script.py

2. 自定义化学体系

python super_unified_script.py --system Li P S

3. 交互式配置所有参数

python super_unified_script.py --config

4. 自定义图片尺寸和字体

python super_unified_script.py --fig-width 1800 --fig-height 1600 --title-size 28

5. 使用 Matplotlib 静态图

python super_unified_script.py --matplotlib

6. 关闭 Hull 连线

python super_unified_script.py --no-hull

7. 查看所有参数

python super_unified_script.py --help

【输出文件】

  • phase_diagram_output.html # 交互式 HTML(Plotly)

  • phase_diagram_output.png # 静态图片

  • phase/ # 竞争相结构文件夹

【日期】2026-05-05

【版本】3.0 (Li-P-S 专用公开版)

============================================================================

【依赖安装】

运行本脚本前,请确保安装以下 Python 库:

核心依赖

pip install numpy matplotlib scipy plotly

Materials Project 相关(重要!)

pip install pymatgen

详细安装命令:

pip install numpy matplotlib scipy plotly pymatgen

如果遇到安装问题,尝试:

pip install --upgrade pip

pip install numpy matplotlib scipy plotly pymatgen

【API 密钥配置】

本脚本需要 Materials Project API 密钥才能获取数据。

获取方式:

  1. 访问 https://next-gen.materialsproject.org/dashboard

  2. 注册/登录账号

  3. 在 Dashboard 页面复制 API Token

  4. 将下方的 API_KEY 替换为你的密钥

注意:

  • API 密钥是免费的,但有使用限制

  • 请勿与他人大规模共享你的 API 密钥

  • 每个用户有默认的速率限制

============================================================================

"""

============================================================================

【第一部分】用户可配置参数(所有参数集中在此区域)

============================================================================

---------- 1.1 API 密钥配置 ----------

【重要】请将下方的 API_KEY 替换为你自己的 Materials Project API 密钥

获取地址:https://next-gen.materialsproject.org/dashboard

API_KEY = "你的API密钥在这里" # <-- 替换这里!

---------- 1.2 化学体系配置 ----------

默认化学体系元素(三个元素构成三元相图)

SYSTEM_ELEMENTS = ["Li", "P", "S"]

---------- 1.3 显示控制 ----------

不稳定相显示阈值 (eV/atom)

-1 = 仅显示稳定相(能量在 convex hull 上)

0.05 = 显示 0.05 eV/atom 内的不稳定相

0.1 = 显示 0.1 eV/atom 内的不稳定相

SHOW_UNSTABLE = -1

---------- 1.4 图片输出配置 ----------

输出文件名前缀(不含扩展名)

OUTPUT_PREFIX = "phase_diagram_output"

相结构下载目录(所有竞争相的 POSCAR 文件会下载到这里)

文件夹名称包含元素集合,防止不同任务数据混合

PHASE_FOLDER = "phase"

图片尺寸(像素)

FIG_WIDTH = 900

FIG_HEIGHT = 800

图片分辨率(DPI)

OUTPUT_DPI = 150

---------- 1.5 字体大小配置 ----------

标题字体大小

TITLE_FONT_SIZE = 28

元素标签字体大小(三角形三个顶点的 Li, P, S)

ELEMENT_FONT_SIZE = 36

数据点标签字体大小(各化合物名称)

LABEL_FONT_SIZE = 16

---------- 1.6 标记样式配置 ----------

数据点大小(像素)

MARKER_SIZE = 25

数据点边框宽度

MARKER_LINE_WIDTH = 2

---------- 1.7 颜色方案(112 色) ----------

从色轮均匀分布的 112 种颜色,支持 100+ 数据点

颜色格式:16 进制 (RRGGBB)

COLOR_PALETTE = [

第1组:基础色轮 16色

"#FF0000", "#FF8800", "#FFDD00", "#00FF00",

"#00FFCC", "#00BBFF", "#0066FF", "#8800FF",

"#FF00AA", "#FF0044", "#AAFF00", "#00FF88",

"#00DDFF", "#4400FF", "#DD00FF", "#FF4400",

第2组:偏移30度 16色

"#FF3333", "#FF9933", "#FFEE33", "#33FF33",

"#33FFCC", "#33CCFF", "#3388FF", "#9933FF",

"#FF33AA", "#FF3388", "#99FF33", "#33FF99",

"#33EEFF", "#5533FF", "#EE33FF", "#FF5533",

第3组:偏移60度 16色

"#FF6666", "#FFAA66", "#FFFF66", "#66FF66",

"#66FFCC", "#66DDFF", "#66AAFF", "#AA66FF",

"#FF66CC", "#FF6666", "#AAFF66", "#66FFAA",

"#66EEFF", "#6644FF", "#FF66FF", "#FF6644",

第4组:浅色调 16色

"#FFAAAA", "#FFCCAA", "#FFFFAA", "#AAFFAA",

"#AAFFCC", "#AAEEFF", "#AACCFF", "#CCAAFF",

"#FFAAEE", "#FFAAAA", "#CCFFAA", "#AAFFCC",

"#AAEEFF", "#AAAFFF", "#FFAAFF", "#FFCCAA",

第5组:深色调 16色

"#CC0000", "#CC6600", "#CCCC00", "#00CC00",

"#00CCCC", "#0099CC", "#0033CC", "#6600CC",

"#CC0099", "#CC0033", "#99CC00", "#00CC66",

"#0099CC", "#3300CC", "#CC00CC", "#CC3300",

第6组:浅色调216色

"#FFDDDD", "#FFEEDD", "#FFFFDD", "#DDFFDD",

"#DDFFEE", "#DDEEFF", "#DDCCFF", "#EEDDFF",

"#FFDDFF", "#FFDDCC", "#EEFFDD", "#DDFFEE",

"#DDEEFF", "#CCDDFF", "#FFDDFF", "#FFEECC",

第7组:暗色调 8色

"#880000", "#884400", "#888800", "#008800",

"#008888", "#004488", "#440088", "#880044",

第8组:亮色调 8色

"#FFBBBB", "#FFDDAA", "#FFFFBB", "#BBFFBB",

"#BBFFDD", "#BBDDFF", "#BBCCFF", "#DDBBFF",

]

---------- 1.8 Hull 连线配置 ----------

是否显示 Hull 连线(数据点之间的三角剖分连线)

SHOW_HULL_LINES = True

Hull 连线颜色

HULL_LINE_COLOR = "gray"

Hull 连线宽度(像素)

HULL_LINE_WIDTH = 1.5

---------- 1.9 图例与坐标轴配置 ----------

是否显示图例

SHOW_LEGEND = False

---------- 1.10 标签样式配置 ----------

标签最小间距(用于避让算法)

LABEL_MARGIN = 0.12

标签背景颜色(白色半透明)

LABEL_BACKGROUND = 'rgba(255,255,255,0.9)'

标签边框宽度

LABEL_BORDER_WIDTH = 1

---------- 1.11 绘图引擎配置 ----------

True = 使用 Plotly(生成交互式 HTML)

False = 使用 Matplotlib(生成静态 PNG)

默认使用 Matplotlib,因为用户更喜欢其绘图风格

USE_PLOTLY = False

============================================================================

【第二部分】导入必要的库

============================================================================

import argparse # 命令行参数解析库

import logging # 日志记录库

import sys # 系统操作库

import os # 文件路径操作库

from pathlib import Path # 面向对象的路径操作库

from datetime import datetime # 日期时间处理库

import numpy as np # 数值计算库(用于坐标转换和三角剖分)

import matplotlib # matplotlib 绑图主库

matplotlib.use('Agg') # 使用非 GUI 后端(仅生成文件,不显示窗口)

import matplotlib.pyplot as plt # matplotlib 绑图模块

from pymatgen.ext.matproj import MPRester # Materials Project API 接口

from pymatgen.analysis.phase_diagram import PhaseDiagram # 相图分析类

from scipy.spatial import Delaunay # Delaunay 三角剖分算法

import plotly.graph_objects as go # Plotly 交互式绑图对象

============================================================================

【第三部分】辅助函数

============================================================================

def setup_logging():

"""

配置日志记录器

功能说明:

日志同时输出到文件和控制台

日志文件名按时间戳自动生成,格式:log_YYYYMMDD_HHMMSS.log

返回:

logging.Logger: 配置好的日志记录器

"""

timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") # 获取当前时间戳

log_file = f"log_{timestamp}.log" # 日志文件名

log_format = '%(asctime)s - %(levelname)s - %(message)s' # 日志格式

logging.basicConfig(

level=logging.INFO, # 记录 INFO 级别及以上的日志

format=log_format, # 日志格式字符串

handlers=[

logging.FileHandler(log_file, encoding='utf-8'), # 输出到文件(支持中文)

logging.StreamHandler(sys.stdout) # 输出到控制台

]

)

return logging.getLogger(name) # 返回日志记录器实例

def coord_to_cartesian(comp_dict, elems=None):

"""

将化合物组成转换为三元相图笛卡尔坐标

物理背景:

三元相图使用等边三角形坐标系:

  • 顶点 A (元素1,如 Li): (0, 0)

  • 顶点 B (元素2,如 P): (1, 0)

  • 顶点 C (元素3,如 S): (0.5, √3/2)

坐标转换公式:

对于化合物 A_x B_y C_z:

  1. 计算总原子数:total = x + y + z

  2. 计算摩尔分数:f_A = x/total, f_B = y/total, f_C = z/total

  3. 转换为笛卡尔坐标:

X = f_B + 0.5 × f_C

Y = (√3/2) × f_C

参数:

comp_dict: 元素计数字典,格式 {元素符号: 原子数}

例如:{"Li": 2, "P": 1, "S": 4}

elems: 元素顺序列表,默认为 ["Li", "P", "S"]

返回:

tuple: (x, y) 笛卡尔坐标,范围 [0, 1]

计算示例:

Li3PS4(磷硫化锂):

  • 组成:Li=3, P=1, S=4,总原子数=8

  • f_Li = 3/8 = 0.375

  • f_P = 1/8 = 0.125

  • f_S = 4/8 = 0.5

  • X = 0.125 + 0.5×0.5 = 0.375

  • Y = 0.866 × 0.5 = 0.433

"""

if elems is None:

elems = SYSTEM_ELEMENTS

total = sum(comp_dict.values()) # 计算总原子数

if total == 0:

return0.5, 0.5 # 处理空组成情况

计算各元素的摩尔分数

fracs = {e: comp_dict.get(e, 0) / total for e in elems}

转换为笛卡尔坐标

x = fracs.get(elems[1], 0) + 0.5 * fracs.get(elems[2], 0)

y = (np.sqrt(3) / 2) * fracs.get(elems[2], 0)

return x, y

def find_best_label_position(px, py, occupied, margin=0.12):

"""

标签自动避让算法

算法原理:

  1. 定义 8 个候选标签位置(相对于数据点的偏移量)

  2. 检查每个位置是否在三角形边界内(不会被裁剪)

  3. 计算到最近已占用位置的距离

  4. 选择距离已占用位置最远的有效位置

参数:

px: 数据点的 x 坐标

py: 数据点的 y 坐标

occupied: 已占用位置列表,格式 [(x, y, label_text), ...]

margin: 最小间距阈值

返回:

tuple: (位置名称, 标签x坐标, 标签y坐标)

位置名称说明:

'top left': 左上方偏移

'top right': 右上方偏移

'bottom left': 左下方偏移

'bottom right': 右下方偏移

'middle left': 正左方偏移

'middle right': 正右方偏移

'top center': 正上方偏移

'bottom center': 正下方偏移

"""

8 个候选位置(名称,水平偏移,垂直偏移)

positions = [

('top left', -0.12, 0.10),

('top right', 0.12, 0.10),

('bottom left', -0.12, -0.08),

('bottom right', 0.12, -0.08),

('middle left', -0.15, 0),

('middle right', 0.15, 0),

('top center', 0, 0.12),

('bottom center', 0, -0.10),

]

初始化最佳位置

best_pos, best_x, best_y = 'top left', px - 0.12, py + 0.10

best_dist = -1

遍历所有候选位置

for pos_name, ox, oy in positions:

test_x, test_y = px + ox, py + oy

边界检查:确保标签在三角形内(不会被裁剪)

if test_y < -0.05or test_y > 0.95:

continue

if test_x < -0.05or test_x > 1.05:

continue

计算到最近已占用位置的距离

ifnot occupied:

min_dist = 999

else:

min_dist = min(

((test_x - pox)**2 + (test_y - poy)**2)**0.5

for (pox, poy, _) in occupied

)

选择距离已占用位置最远的有效位置

if min_dist > best_dist:

best_dist = min_dist

best_pos, best_x, best_y = pos_name, test_x, test_y

return best_pos, best_x, best_y

def allocate_colors(phases):

"""

为每个相分配唯一颜色

原理说明:

使用模运算循环使用颜色池中的颜色

颜色按色轮角度均匀分布,确保相邻颜色有足够区分度

参数:

phases: 相数据列表

返回:

list: 每个相增加 'color' 字段

"""

for i, phase in enumerate(phases):

phase['color'] = COLOR_PALETTE[i % len(COLOR_PALETTE)]

return phases

def calculate_label_positions(phases):

"""

计算每个相的标签位置

算法流程:

  1. 按 Y 坐标排序(从下到上),下方先放置标签

  2. 遍历每个相,调用 find_best_label_position

  3. 将放置好的位置加入已占用列表

参数:

phases: 相数据列表

返回:

list: 每个相增加 'label_pos', 'label_x', 'label_y' 字段

"""

occupied = [] # 已占用位置列表

sorted_phases = sorted(phases, key=lambda p: p['y']) # 按 Y 坐标排序

for phase in sorted_phases:

pos, lx, ly = find_best_label_position(

phase['x'], phase['y'], occupied, margin=LABEL_MARGIN

)

phase['label_pos'] = pos

phase['label_x'] = lx

phase['label_y'] = ly

occupied.append((lx, ly, phase['formula']))

return phases

def download_phases(entries, phase_folder, logger):

"""

下载所有竞争相的 POSCAR 结构文件

功能说明:

将 Materials Project 获取的所有相的结构文件保存到本地文件夹

每个相保存在单独的子文件夹中

参数:

entries: pymatgen 的 ComputedStructureEntry 列表

phase_folder: 下载目录路径

logger: 日志记录器

返回:

int: 成功下载的相数量

"""

phase_path = Path(phase_folder)

phase_path.mkdir(exist_ok=True) # 创建主文件夹

logger.info(f"开始下载 {len(entries)} 个竞争相到 {phase_folder}/")

downloaded = 0

for entry in entries:

try:

生成安全的文件夹名称(处理特殊字符)

safe_name = entry.name.replace(" ", "").replace("/", "").replace("(", "").replace(")", "")

获取能量 above hull

eah = entry.data.get("energy_above_hull", 0)

eah_str = f"{eah:.3f}"

创建子文件夹名称:化学式_EaH_能量

folder_name = f"{safe_name}EaH{eah_str}"

phase_dir = phase_path / folder_name

如果文件夹已存在且包含 POSCAR,跳过

if phase_dir.exists() and (phase_dir / "POSCAR").exists():

logger.info(f" 跳过(已存在): {folder_name}")

continue

phase_dir.mkdir(exist_ok=True)

保存 POSCAR 文件

entry.structure.to(filename=str(phase_dir / "POSCAR"))

downloaded += 1

logger.info(f" 下载成功: {folder_name}")

except Exception as e:

logger.warning(f" 下载失败: {entry.name} - {str(e)}")

logger.info(f"下载完成:{downloaded}/{len(entries)} 个相")

return downloaded

def get_phase_data(system, api_key, logger, download=True):

"""

从 Materials Project 获取相图数据

功能说明:

  1. 连接 Materials Project REST API

  2. 获取指定化学体系的所有条目

  3. 可选:下载所有竞争相的 POSCAR 文件

  4. 创建 pymatgen PhaseDiagram 对象

  5. 提取稳定相数据

参数:

system: 元素列表,如 ["Li", "P", "S"]

api_key: Materials Project API 密钥

logger: 日志记录器

download: 是否下载竞争相结构文件

返回:

tuple: (PhaseDiagram对象, 稳定相列表)

稳定相列表格式:

{'formula': str, 'x': float, 'y': float, 'e_per_atom': float}, ...

"""

logger.info("连接 Materials Project...")

创建 API 连接

mpr = MPRester(api_key=api_key)

logger.info(f"获取 {system} 体系数据...")

entries = mpr.get_entries_in_chemsys(system)

logger.info(f"从 MP 获取到 {len(entries)} 个相")

可选:下载所有相的结构文件

文件夹名称包含元素集合,防止不同任务数据混合

if download:

phase_folder = f"phase_{'_'.join(system)}"

download_phases(entries, phase_folder, logger)

创建相图对象

logger.info("创建相图对象...")

pd = PhaseDiagram(entries)

logger.info(f"稳定相数量: {len(pd.stable_entries)}")

提取稳定相数据

phases = []

for entry in pd.stable_entries:

comp_dict = entry.composition.as_dict()

formula = entry.composition.reduced_formula

x, y = coord_to_cartesian(comp_dict, system)

e_per_atom = pd.get_hull_energy(entry.composition) / entry.composition.num_atoms

phases.append({

'formula': formula,

'x': x,

'y': y,

'e_per_atom': e_per_atom,

'comp_dict': comp_dict

})

按 Y 坐标排序(从下到上)

phases.sort(key=lambda p: p['y'])

return pd, phases

============================================================================

【第四部分】Plotly 绘图函数

============================================================================

def draw_triangle_boundary_plotly(fig, elems):

"""

绘制三元相图的三角形边界和元素标签(Plotly 版本)

参数:

fig: Plotly Figure 对象

elems: 元素列表,如 ["Li", "P", "S"]

"""

等边三角形的三个顶点坐标

triangle_x = [0, 1, 0.5, 0] # 第四个点是闭合三角形

triangle_y = [0, 0, np.sqrt(3)/2, 0]

绘制三角形边界线

fig.add_trace(go.Scatter(

x=triangle_x, y=triangle_y,

mode='lines', # 只画线,不画点

line=dict(color='black', width=4), # 黑色线,宽4像素

name='边界',

hoverinfo='skip' # 悬停时不显示信息

))

绘制元素标签

Li 在左下角 (0, -0.12)

P 在右下角 (1, -0.12)

S 在顶部 (0.5, √3/2 + 0.14)

fig.add_trace(go.Scatter(

x=[0, 1, 0.5],

y=[-0.12, -0.12, np.sqrt(3)/2 + 0.14],

mode='text', # 只显示文本

text=[f"<b>{elems[0]}</b>",

f"<b>{elems[1]}</b>",

f"<b>{elems[2]}</b>"],

textfont=dict(size=ELEMENT_FONT_SIZE, color='black'),

name='元素标签',

hoverinfo='skip'

))

def draw_hull_lines_plotly(fig, phases):

"""

绘制 Delaunay 三角剖分连线(Plotly 版本)

物理意义:

Delaunay 三角剖分将所有数据点连接成三角形网格

这些连线表示相邻相之间的能量关系

密集区域表示可能的反应路径

技术说明:

Delaunay 三角剖分的特点:

  • 任意三角形的外接圆内不包含其他点

  • 三角形尽可能接近等边

  • 适合展示点之间的邻接关系

参数:

fig: Plotly Figure 对象

phases: 相数据列表

"""

ifnot SHOW_HULL_LINES:

return

points = np.array([[p['x'], p['y']] for p in phases])

if len(points) < 3:

return

tri = Delaunay(points)

hull_x, hull_y = [], []

遍历每个三角形

for simplex in tri.simplices:

for i in range(3):

hull_x.extend([points[simplex[i], 0], points[simplex[(i+1) % 3], 0], np.nan])

hull_y.extend([points[simplex[i], 1], points[simplex[(i+1) % 3], 1], np.nan])

fig.add_trace(go.Scatter(

x=hull_x, y=hull_y,

mode='lines',

line=dict(color=HULL_LINE_COLOR, width=HULL_LINE_WIDTH),

name='Hull连线',

hoverinfo='skip'

))

def draw_phases_plotly(fig, phases):

"""

绘制所有相的数据点和标签(Plotly 版本)

参数:

fig: Plotly Figure 对象

phases: 相数据列表

"""

for phase in phases:

color = phase['color']

绘制数据点

fig.add_trace(go.Scatter(

x=[phase['x']], y=[phase['y']],

mode='markers',

marker=dict(

size=MARKER_SIZE, # 点的大小

color=color, # 点的颜色

line=dict(color='white', width=MARKER_LINE_WIDTH) # 白色边框

),

name=phase['formula'],

hovertemplate=f"<b>{phase['formula']}</b><br>E = {phase['e_per_atom']:.4f} eV/atom",

showlegend=SHOW_LEGEND

))

添加标签

fig.add_annotation(

x=phase['label_x'], y=phase['label_y'],

text=f"<b>{phase['formula']}</b>",

showarrow=False, # 不显示箭头

font=dict(size=LABEL_FONT_SIZE, color=color),

bgcolor=LABEL_BACKGROUND,

bordercolor=color,

borderwidth=LABEL_BORDER_WIDTH,

borderpad=3,

xref='x', yref='y'

)

def plot_ternary_plotly(system, api_key, output_prefix, logger, download=True):

"""

使用 Plotly 绘制交互式三元相图

Plotly 特点:

  • 生成交互式 HTML,可放大缩小

  • 支持悬停查看详情

  • 适合网页展示和分享

参数:

system: 元素列表

api_key: MP API 密钥

output_prefix: 输出文件前缀

logger: 日志记录器

download: 是否下载竞争相

"""

logger.info("使用 Plotly 绘制交互式相图...")

获取相图数据

pd, phases = get_phase_data(system, api_key, logger, download=download)

分配颜色

phases = allocate_colors(phases)

计算标签位置

phases = calculate_label_positions(phases)

创建 Figure 对象

fig = go.Figure()

绘制三角形边界

draw_triangle_boundary_plotly(fig, system)

绘制 Hull 连线

draw_hull_lines_plotly(fig, phases)

绘制数据点和标签

draw_phases_plotly(fig, phases)

生成标题字符串

system_str = "-".join(system)

unstable_str = "stable only"if SHOW_UNSTABLE < 0else f"unstable<{SHOW_UNSTABLE}"

设置布局

fig.update_layout(

title=dict(

text=f"<b>{system_str} 三元相图</b><br><sup>{unstable_str} | {len(phases)} phases</sup>",

font=dict(size=TITLE_FONT_SIZE),

x=0.5,

xanchor='center'

),

xaxis=dict(

range=[-0.2, 1.2],

showgrid=False,

zeroline=False,

showticklabels=False

),

yaxis=dict(

range=[-0.25, 1.1],

showgrid=False,

zeroline=False,

showticklabels=False,

scaleanchor='x', # X 和 Y 轴等比例

scaleratio=1

),

showlegend=SHOW_LEGEND,

plot_bgcolor='white',

width=FIG_WIDTH,

height=FIG_HEIGHT,

margin=dict(l=100, r=50, t=120, b=100)

)

保存文件

html_file = f"{output_prefix}.html"

png_file = f"{output_prefix}.png"

logger.info(f"保存 HTML: {html_file}")

fig.write_html(html_file)

logger.info(f"保存 PNG: {png_file}")

fig.write_image(png_file, scale=2)

输出统计信息

logger.info("=" * 70)

logger.info("相图数据统计:")

logger.info("=" * 70)

for phase in phases:

logger.info(f" {phase['formula']:<12} ({phase['x']:.3f}, {phase['y']:.3f}) E={phase['e_per_atom']:.4f} eV")

logger.info(f"\n完成! 输出: {html_file}, {png_file}")

============================================================================

【第五部分】Matplotlib 绘图函数

============================================================================

def draw_triangle_boundary_mpl(ax, elems):

"""

绘制三元相图的三角形边界和元素标签(Matplotlib 版本)

参数:

ax: matplotlib Axes 对象

elems: 元素列表

"""

triangle_x, triangle_y = [0, 1, 0.5, 0], [0, 0, np.sqrt(3)/2, 0]

ax.plot(triangle_x, triangle_y, 'k-', linewidth=2)

ax.text(0, -0.08, elems[0], fontsize=ELEMENT_FONT_SIZE, ha='center', fontweight='bold')

ax.text(1, -0.08, elems[1], fontsize=ELEMENT_FONT_SIZE, ha='center', fontweight='bold')

ax.text(0.5, np.sqrt(3)/2 + 0.1, elems[2], fontsize=ELEMENT_FONT_SIZE, ha='center', fontweight='bold')

def draw_hull_lines_mpl(ax, phases):

"""

绘制 Delaunay 三角剖分连线(Matplotlib 版本)

参数:

ax: matplotlib Axes 对象

phases: 相数据列表

"""

ifnot SHOW_HULL_LINES:

return

points = np.array([[p['x'], p['y']] for p in phases])

if len(points) < 3:

return

tri = Delaunay(points)

for simplex in tri.simplices:

for i in range(3):

ax.plot(

points\[simplex\[i\], 0\], points\[simplex\[(i+1) % 3\], 0\]\], \[points\[simplex\[i\], 1\], points\[simplex\[(i+1) % 3\], 1\]\], color=HULL_LINE_COLOR, linewidth=HULL_LINE_WIDTH ) def draw_phases_mpl(ax, phases): """ 绘制数据点和标签(Matplotlib 版本) 参数: ax: matplotlib Axes 对象 phases: 相数据列表 """ for phase in phases: ax.plot(phase\['x'\], phase\['y'\], 'o', markersize=MARKER_SIZE/3, color=phase\['color'\]) ax.annotate( phase\['formula'\], (phase\['label_x'\], phase\['label_y'\]), fontsize=LABEL_FONT_SIZE, color=phase\['color'\], fontweight='bold', bbox=dict(boxstyle='round,pad=0.3', facecolor='white', alpha=0.8, edgecolor=phase\['color'\]) ) def plot_ternary_matplotlib(system, api_key, output_prefix, logger, download=True): """ 使用 Matplotlib 绘制静态三元相图 Matplotlib 特点: - 生成静态图片 (PNG, PDF, SVG) - 适合论文发表 - 可精确控制每个元素 参数: system: 元素列表 api_key: MP API 密钥 output_prefix: 输出文件前缀 logger: 日志记录器 download: 是否下载竞争相 """ logger.info("使用 Matplotlib 绘制静态相图...") # 获取相图数据 pd, phases = get_phase_data(system, api_key, logger, download=download) # 分配颜色 phases = allocate_colors(phases) # 计算标签位置 phases = calculate_label_positions(phases) # 创建图形 fig, ax = plt.subplots(1, 1, figsize=(FIG_WIDTH/100, FIG_HEIGHT/100)) # 绘制三角形边界 draw_triangle_boundary_mpl(ax, system) # 绘制 Hull 连线 draw_hull_lines_mpl(ax, phases) # 绘制数据点 draw_phases_mpl(ax, phases) # 设置坐标轴 ax.set_xlim(-0.15, 1.15) ax.set_ylim(-0.2, 1.05) ax.set_aspect('equal') ax.axis('off') # 设置标题 system_str = "-".join(system) ax.set_title(f"{system_str} Ternary Phase Diagram", fontsize=TITLE_FONT_SIZE, fontweight='bold') plt.tight_layout() # 保存 png_file = f"{output_prefix}.png" logger.info(f"保存 PNG: {png_file}") plt.savefig(png_file, dpi=OUTPUT_DPI, bbox_inches='tight', facecolor='white') plt.close() logger.info(f"\\n完成! 输出: {png_file}") # ============================================================================ # 【第六部分】交互式配置向导 # ============================================================================ def interactive_config(): """ 交互式配置向导 功能说明: 通过命令行交互,让用户逐步设置各种参数 每一步都有默认值和建议值 返回: dict: 用户配置的参数字典 """ print("\\n" + "=" \* 60) print(" Li-P-S 三元相图绘制 - 交互式配置向导") print("=" \* 60) config = {} # 配置项 1:化学体系 print("\\n【1/6】化学体系设置") print(f" 默认: {SYSTEM_ELEMENTS}") elem1 = input(f" 元素1 (默认 {SYSTEM_ELEMENTS\[0\]}): ").strip() or SYSTEM_ELEMENTS\[0

elem2 = input(f" 元素2 (默认 {SYSTEM_ELEMENTS[1]}): ").strip() or SYSTEM_ELEMENTS[1]

elem3 = input(f" 元素3 (默认 {SYSTEM_ELEMENTS[2]}): ").strip() or SYSTEM_ELEMENTS[2]

config['system'] = [elem1, elem2, elem3]

配置项 2:图片尺寸

print("\n【2/6】图片尺寸设置")

width = input(f" 宽度像素 (默认 {FIG_WIDTH}): ").strip()

config['fig_width'] = int(width) if width else FIG_WIDTH

height = input(f" 高度像素 (默认 {FIG_HEIGHT}): ").strip()

config['fig_height'] = int(height) if height else FIG_HEIGHT

配置项 3:字体大小

print("\n【3/6】字体大小设置")

title = input(f" 标题字体 (默认 {TITLE_FONT_SIZE}): ").strip()

config['title_size'] = int(title) if title else TITLE_FONT_SIZE

label = input(f" 标签字体 (默认 {LABEL_FONT_SIZE}): ").strip()

config['label_size'] = int(label) if label else LABEL_FONT_SIZE

配置项 4:显示选项

print("\n【4/6】显示选项")

hull = input(" 显示 Hull 连线? (Y/n): ").strip().lower()

config['show_hull'] = (hull != 'n')

legend = input(" 显示图例? (y/N): ").strip().lower()

config['show_legend'] = (legend == 'y')

marker = input(f" 标记大小 (默认 {MARKER_SIZE}): ").strip()

config['marker_size'] = int(marker) if marker else MARKER_SIZE

配置项 5:绘图引擎

print("\n【5/6】绘图引擎")

engine = input(" 使用 Plotly (交互式 HTML)? (Y/n): ").strip().lower()

config['use_plotly'] = (engine != 'n')

配置项 6:输出设置

print("\n【6/6】输出设置")

prefix = input(f" 文件前缀 (默认 {OUTPUT_PREFIX}): ").strip()

config['output_prefix'] = prefix or OUTPUT_PREFIX

print("\n" + "=" * 60)

print("配置完成!")

print("=" * 60)

return config

============================================================================

【第七部分】主函数

============================================================================

def main():

"""

主函数入口

功能流程:

  1. 解析命令行参数

  2. 设置日志

  3. 处理交互式配置(如有)

  4. 执行绘图

  5. 输出结果

"""

在函数开头声明所有将使用的全局变量

global FIG_WIDTH, FIG_HEIGHT, TITLE_FONT_SIZE, LABEL_FONT_SIZE

global MARKER_SIZE, SHOW_HULL_LINES, SHOW_LEGEND, USE_PLOTLY

global SYSTEM_ELEMENTS, PHASE_FOLDER

parser = argparse.ArgumentParser(

description="""

============================================================================

Li-P-S 三元相图绘制脚本 v3.0

============================================================================

功能:

  • 从 Materials Project 获取 Li-P-S 体系相图数据

  • 自动下载所有竞争相结构到 phase_元素集合/ 文件夹

  • 绘制三元相图(默认 Matplotlib 静态模式)

  • Delaunay 三角剖分 Hull 连线

  • 标签自动避让算法

  • 112 色颜色方案

示例:

python super_unified_script.py # 使用默认参数

python super_unified_script.py --config # 交互式配置

python super_unified_script.py --system Li P S # 自定义体系

python super_unified_script.py --plotly # 使用 Plotly 交互式

============================================================================

""",

formatter_class=argparse.RawDescriptionHelpFormatter

)

添加命令行参数

parser.add_argument("--system", nargs="+", default=SYSTEM_ELEMENTS,

help="化学体系元素,如: Li P S")

parser.add_argument("--api-key", default=API_KEY,

help="Materials Project API 密钥")

parser.add_argument("--output", "-o", default=OUTPUT_PREFIX,

help="输出文件前缀")

parser.add_argument("--config", action="store_true",

help="启用交互式配置向导")

parser.add_argument("--fig-width", type=int, default=FIG_WIDTH,

help="图片宽度 (像素)")

parser.add_argument("--fig-height", type=int, default=FIG_HEIGHT,

help="图片高度 (像素)")

parser.add_argument("--title-size", type=int, default=TITLE_FONT_SIZE,

help="标题字体大小")

parser.add_argument("--label-size", type=int, default=LABEL_FONT_SIZE,

help="标签字体大小")

parser.add_argument("--marker-size", type=int, default=MARKER_SIZE,

help="标记大小")

parser.add_argument("--no-hull", action="store_true",

help="不显示 Hull 连线")

parser.add_argument("--legend", action="store_true",

help="显示图例")

parser.add_argument("--matplotlib", action="store_true",

help="使用 Matplotlib(已废弃,默认即Matplotlib)")

parser.add_argument("--plotly", action="store_true",

help="使用 Plotly 交互式模式")

parser.add_argument("--no-download", action="store_true",

help="不下载竞争相结构文件")

args = parser.parse_args()

logger = setup_logging()

logger.info("=" * 60)

logger.info("Li-P-S 三元相图绘制开始")

logger.info("=" * 60)

logger.info(f"化学体系: {args.system}")

logger.info(f"图片尺寸: {args.fig_width} x {args.fig_height}")

交互式配置模式

if args.config:

config = interactive_config()

args.system = config.get('system', args.system)

args.fig_width = config.get('fig_width', args.fig_width)

args.fig_height = config.get('fig_height', args.fig_height)

args.title_size = config.get('title_size', args.title_size)

args.label_size = config.get('label_size', args.label_size)

args.marker_size = config.get('marker_size', args.marker_size)

args.output = config.get('output_prefix', args.output)

args.show_hull = config.get('show_hull', True)

args.show_legend = config.get('show_legend', False)

args.use_plotly = config.get('use_plotly', True)

更新全局变量

FIG_WIDTH = args.fig_width

FIG_HEIGHT = args.fig_height

TITLE_FONT_SIZE = args.title_size

LABEL_FONT_SIZE = args.label_size

MARKER_SIZE = args.marker_size

SHOW_HULL_LINES = not args.no_hull

SHOW_LEGEND = args.legend

如果指定了 --plotly,则使用 Plotly;否则默认使用 Matplotlib

USE_PLOTLY = args.plotly if args.plotly elsenot args.matplotlib

SYSTEM_ELEMENTS = args.system

执行绘图

try:

if USE_PLOTLY:

plot_ternary_plotly(args.system, args.api_key, args.output, logger,

download=not args.no_download)

else:

plot_ternary_matplotlib(args.system, args.api_key, args.output, logger,

download=not args.no_download)

logger.info("脚本执行成功!")

except Exception as e:

logger.error(f"执行失败: {e}")

import traceback

logger.error(traceback.format_exc())

sys.exit(1)

============================================================================

程序入口

============================================================================

if name == "main":

main()

相关推荐
子兮曰2 小时前
深入 Superpowers:180k Stars 的开源 AI 编程方法论是如何工作的
前端·javascript·后端
2301_771717212 小时前
Spring Boot 自动配置核心注解
java·spring boot·mybatis
小Y._2 小时前
面试被问synchronized锁升级,这5个问题答不上来直接挂!
java
沸点小助手2 小时前
「新晋AI顶流PK:GPT-5.5 vs DeepSeek V4&掘友吐槽小会」沸点获奖名单公示|本周互动话题上新🎊
前端·人工智能
EW Frontier2 小时前
6G ISAC新范式:基于智能漏波天线的Wi‑Fi通感一体化系统设计与实测【附MATLAB+python代码】
开发语言·python·matlab·music·isac·doa·wi‑fi
姚青&2 小时前
测试技术体系
java·python
隔壁的大叔2 小时前
Markdown 渲染如何穿插自定义组件
前端·javascript·vue.js
Yupureki2 小时前
《Linux网络编程》6.UDP原理
linux·运维·服务器·网络·udp