Skip to content
BellahraPublic

About

Postprocessing code for PIC simulation code --EPOCH

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

10 Commits

Folders and files

Repository files navigation

epochpost

当前说明对应 v0.1.3。

epochpost 是一个基于 sdf_xarray 的 EPOCH PIC 通用后处理脚本。核心思路是:代码只维护一份,日常只修改 epochpost_input.json 来描述要画什么图。

仓库建议保持以下结构:

epochpost/
├── epochpost.py
├── epochpost_input.json
├── README.md
└── Assets/                    # 可选:本地字体资源
    └── fonts/                 # 可放有授权的 Arial .ttf/.otf/.ttc

最常用的运行方式:

python epochpost.py epochpost_input.json

如果配置文件就在当前目录且名称为 epochpost_input.json,也可以直接:

python epochpost.py

本程序用于 EPOCH/PIC 的 sdf 文件后处理,底层使用 sdf_xarray。当前版本已经把程序逻辑和每个算例的输入参数分开:

  • epochpost.py:通用后处理模块,原则上不再为每个 run 修改。
  • epochpost_input.json:每个算例自己的后处理输入,只描述“数据在哪里、要画什么图”。

建议以后每个 PIC 算例目录只保存一份自己的 epochpost_input.json。


1. 推荐目录结构

例如:

run7/
├── input.deck
├── epochpost_input.json
├── Data/
│   ├── sourcexyspecies0001.sdf
│   ├── sourcexyspecies0002.sdf
│   ├── ...
│   ├── foilparticles0001.sdf
│   └── ...
└── Data/postprocess/        # 程序自动创建

而通用 Python 程序可以放在统一位置,例如:

~/python_lib/
├── epochpost.py
└── Assets/                    # 可选
    └── fonts/
        └── Arial.ttf          # 示例;仅放你有权使用的字体

这样不同 run 之间只需要复制和修改 epochpost_input.json。


2. 运行方法

方法 A:命令行直接运行

在算例目录中:

python ~/python_lib/epochpost.py epochpost_input.json

如果当前目录存在 epochpost_input.json,也可以直接:

python ~/python_lib/epochpost.py

程序会依次尝试:

  1. epochpost_input.json
  2. post_input.json(兼容旧版本)
  3. input.json(兼容旧版本)

也可以传入任意其他 JSON 文件名:

python ~/python_lib/epochpost.py run7_post.json

方法 B:作为 Python 模块调用

from epochpost import run_config

run_config("epochpost_input.json")

这也是以后打包成 Python library 后推荐保留的接口。


3. 最简 JSON

如果你的数据就在 Data/,激光波长为 800 nm,那么 settings 可以完全省略,最简输入可以只有:

{
  "tasks": [
    {
      "type": "spatial",
      "name": "density_10fs",
      "prefix": "sourcexyspecies",
      "variable": {
        "contains": ["Number_Density", "Electron_foil"]
      },
      "axes": ["x", "y"],
      "cuts": {"z": 0.0},
      "select": {"mode": "time", "time_fs": 10.0},
      "animate": false,
      "normalization": {
        "kind": "density_nc"
      },
      "scale": "log"
    }
  ]
}

任务中的 enabled 如果省略,默认视为 true。


4. 全局 settings 与绘图默认样式

推荐配置为:

"settings": {
  "data_dir": "Data",
  "out_dir": null,
  "laser_wavelength_nm": 800.0,
  "space_unit": "nm",
  "space_factor": 1000000000.0,
  "time_unit": "fs",
  "time_factor": 1000000000000000.0,
  "plot": {
    "format": "png",
    "dpi": 300,
    "fig_single": [3.4, 2.7],
    "fig_double": [7.0, 4.8],
    "font_family": "Arial",
    "mathtext_fontset": "custom",
    "font_label": 10,
    "font_tick": 8,
    "font_legend": 8,
    "font_panel": 11,
    "font_annotation": 8,
    "line_width": 1.5,
    "axis_width": 1.0,
    "bbox_inches": "tight"
  }
}

这些值已经写成 epochpost.py 的默认值,因此如果你满意这些论文绘图规范,settings.plot 可以整个省略。

参数 含义 默认值
data_dir SDF 文件目录 Data
out_dir 输出目录;null 时为 <data_dir>/postprocess null
laser_wavelength_nm 激光波长,用于计算临界密度 nc 800
space_unit 图上的空间单位标签 nm
space_factor SDF 中 m 转换到绘图单位的乘数 1e9
time_unit 时间单位标签 fs
time_factor SDF 中 s 转换到绘图单位的乘数 1e15
plot.format 静态图默认格式 png
plot.dpi 位图输出分辨率 300
plot.fig_single 单栏图尺寸,单位 inch [3.4, 2.7]
plot.fig_double 双栏图尺寸,单位 inch [7.0, 4.8]
plot.font_family 普通文本字体 Arial
plot.mathtext_fontset 兼容字段;v0.1.2 中数学文本会自动跟随最终选中的普通字体 custom
plot.font_label 坐标轴标签字号 10
plot.font_tick 刻度字号 8
plot.font_legend 图例字号 8
plot.font_panel 标题/Panel 字号 11
plot.font_annotation 普通 annotation 默认字号 8
plot.line_width 普通曲线默认线宽 1.5
plot.axis_width 坐标轴和 tick 线宽 1.0
plot.bbox_inches 保存时边界裁切 tight

旧版本的:

"dpi": 180

仍然兼容,但新版本推荐统一写到 settings.plot.dpi 中。

4.1 日常图

默认就是:

300 dpi PNG

因此 task 中完全不需要额外指定保存格式。

4.2 投稿时改成 PDF

如果希望整个算例的静态图都输出成 PDF:

"plot": {
  "format": "pdf",
  "bbox_inches": "tight"
}

PDF 是矢量输出,dpi 对其中的矢量元素不重要,但程序仍会保留该参数以兼容可能包含的 raster 元素。

4.3 字体与 Assets(v0.1.2)

默认普通字体为 Arial。程序启动时会先尝试注册 epochpost.py 同目录下的:

Assets/
Assets/fonts/

其中的 .ttf、.otf 和 .ttc 字体文件。字体选择优先级为:

settings.plot.font_family
→ Arial
→ Helvetica
→ DejaVu Sans
→ Matplotlib 默认 sans-serif

因此,没有 Assets 文件夹也不会报错。如果系统没有 Arial,程序会自动回退并打印提示。

如果你希望本地、集群和其他机器的论文图字体尽量一致,可以把自己有权使用的 Arial 字体放到:

Assets/fonts/

例如:

Assets/fonts/Arial.ttf
Assets/fonts/Arial Italic.ttf
Assets/fonts/Arial Bold.ttf

注意:Arial 属于受许可字体。公开 GitHub 仓库或 release 中不要直接分发无授权的 Arial 字体文件;可以只保留空的 Assets/fonts/ 和说明文档。

v0.1.2 的自动 colorbar label 会让单位字母保持普通正体;像 m^-3 中的指数只局部交给 Matplotlib mathtext 排版,从而避免依赖字体是否包含 Unicode 上标负号。

4.4 单个 task 覆盖图尺寸或格式

绝大多数情况下不用写 plot。如果某一张图需要双栏宽度:

{
  "type": "xt",
  "name": "foil_Jy_xt",
  "plot": {
    "figure_size": "double"
  }
}

支持:

"figure_size": "single"
"figure_size": "double"

或者直接指定尺寸:

"figure_size": [5.0, 3.2]

也可以只让某一张图输出 PDF:

"plot": {
  "format": "pdf",
  "figure_size": "single",
  "bbox_inches": "tight"
}

或者单独覆盖 dpi:

"plot": {
  "dpi": 600
}

多 panel 对比图默认使用双栏宽度 7.0 inch。如果需要强制指定总尺寸,可写:

"plot": {
  "overview_figure_size": [7.0, 6.0]
}

**重要:**相对路径以 epochpost_input.json 所在目录为基准,而不是以 Python 库文件所在目录为基准。


5. 文件选择:单时刻、多个时刻、单编号、多个编号

5.1 最新文件

"select": {
  "mode": "latest"
}

5.2 单个 SDF 编号

"select": {
  "mode": "number",
  "number": 78
}

5.3 多个 SDF 编号:直接 list

"select": {
  "mode": "number",
  "number": [20, 40, 60, 78]
}

5.4 多个 SDF 编号:arange

JSON 不能直接写 np.arange(),因此程序提供等价写法:

"select": {
  "mode": "number",
  "number": {
    "arange": [10, 79, 5]
  }
}

等价于 Python:

np.arange(10, 79, 5)

即选择:

10, 15, 20, ..., 75

arange 的 stop 不包含在结果中,与 NumPy 完全一致。

5.5 单个物理时刻

"select": {
  "mode": "time",
  "time_fs": 10.0
}

程序会自动寻找与 10 fs 最接近的 SDF 文件。

5.6 多个物理时刻:list

"select": {
  "mode": "time",
  "time_fs": [2.0, 4.0, 6.0, 8.0, 10.0]
}

5.7 多个物理时刻:arange

"select": {
  "mode": "time",
  "time_fs": {
    "arange": [2.0, 10.1, 2.0]
  }
}

等价于:

np.arange(2.0, 10.1, 2.0)

5.8 linspace

"time_fs": {
  "linspace": [2.0, 10.0, 5]
}

等价于:

np.linspace(2.0, 10.0, 5)

结果为:

2, 4, 6, 8, 10 fs

linspace 包含终点。

5.9 Python range 风格

"number": {
  "range": [10, 79, 5]
}

等价于:

range(10, 79, 5)

只适合整数编号。


6. spatial:二维空间分布

用于某一时刻或多个时刻的二维场、密度、电流等空间分布。

例如:

{
  "type": "spatial",
  "name": "foil_density_xy",
  "prefix": "sourcexyspecies",
  "variable": {
    "contains": ["Number_Density", "Electron_foil"]
  },
  "axes": ["x", "y"],
  "cuts": {
    "z": 0.0
  },
  "select": {
    "mode": "time",
    "time_fs": [2, 4, 6, 8, 10]
  },
  "animate": false,
  "xlim": [320, 760],
  "ylim": [-750, 750],
  "normalization": {
    "kind": "density_nc"
  },
  "scale": "log",
  "vmin": 0.01,
  "vmax": 200,
  "cmap": "magma",
  "save_data": true
}

v0.1.2 推荐让内置 normalization 自动生成 colorbar label;density_nc、current_j0、momentum_mec 通常只需要写 kind,不必手动写 label。

多个 time_fs 或多个 number 时,程序会:

  1. 每个时刻/编号分别保存一张 PNG;
  2. 再生成一张统一 colorbar 的多面板比较图;
  3. save_data=true 时把所有结果保存到一个压缩 .npz 中。

图标题与时间显示

v0.1.2 的时刻标题统一使用:

time=12.34 fs

即实际时刻保留两位小数,并在数值与 fs 之间保留一个空格。

如果 task 写:

"title": null

则不会显示 None,也不会自动把 task 名称作为标题。单图/子图只显示:

time=12.34 fs

如果显式设置:

"title": "Electron density"

则显示:

Electron density, time=12.34 fs

多 panel 图在 title: null 时也不会额外显示总标题。

文件名仍使用适合文件系统的紧凑形式,例如 time=12.34fs;这里只改变图上的显示格式。

二维动图

{
  "type": "spatial",
  "name": "density_animation",
  "prefix": "sourcexyspecies",
  "variable": {"contains": ["Number_Density", "Electron_foil"]},
  "axes": ["x", "y"],
  "cuts": {"z": 0.0},
  "animate": true,
  "time_range_fs": [0.0, null],
  "stride": 5,
  "max_frames": 100,
  "fps": 12,
  "normalization": {"kind": "density_nc"},
  "scale": "log"
}

其中:

  • time_range_fs: [0, null]:从 0 fs 到最后一个文件;
  • stride: 5:每隔 5 个文件取一帧;
  • max_frames:最多保留多少帧;
  • fps:GIF 帧率。

7. xt:空间-时间图

适合从不同 SDF 文件中提取同一条空间线,例如:

  • J_y(x,t)
  • J_x(x,t)
  • 密度 n_e(x,t)
  • 某个固定 y,z 截面上的场随时间演化

示例:

{
  "type": "xt",
  "name": "foil_Jy_xt_y0",
  "prefix": "sourcexyspecies",
  "variable": {
    "contains": ["Jy", "Electron_foil"]
  },
  "line_axis": "x",
  "cuts": {
    "y": 0.0,
    "z": 0.0
  },
  "time_range_fs": [0.0, null],
  "stride": 1,
  "space_range": [320, 760],
  "normalization": {
    "kind": "current_j0"
  },
  "scale": "symlog",
  "linthresh": 0.02,
  "cmap": "RdBu_r",
  "save_data": true
}

xt 本身就是跨多个文件组成一张图,因此使用 time_range_fs + stride,不使用 select。


8. particle_phase:粒子相空间

例如电子的 x-px:

{
  "type": "particle_phase",
  "name": "foil_x_px",
  "prefix": "foilparticles",
  "x_variable": {
    "contains": ["Grid_Particles"],
    "component": "x"
  },
  "y_variable": {
    "contains": ["Particles_Px"]
  },
  "weight_variable": {
    "contains": ["Particles_Weight"]
  },
  "x_normalization": {
    "factor": 1000000000.0,
    "label": "x (nm)"
  },
  "y_normalization": {
    "kind": "momentum_mec"
  },
  "select": {
    "mode": "number",
    "number": [60, 65, 70, 75, 78]
  },
  "animate": false,
  "xlim": [360, 720],
  "ylim": [-20, 40],
  "bins": [400, 400],
  "scale": "log",
  "save_data": true
}

与 spatial 一样,particle_phase + animate=false 支持单个或多个 time_fs/number。


9. particle_xt:粒子位置-时间分布

示例:

{
  "type": "particle_xt",
  "name": "foil_particle_xt",
  "prefix": "foilparticles",
  "position_variable": {
    "contains": ["Grid_Particles"],
    "component": "x"
  },
  "weight_variable": {
    "contains": ["Particles_Weight"]
  },
  "position_normalization": {
    "factor": 1000000000.0,
    "label": "x (nm)"
  },
  "time_range_fs": [0.0, null],
  "stride": 1,
  "space_range": [360, 720],
  "bins": 500,
  "scale": "log",
  "save_data": true
}

10. 变量选择方式

精确变量名

如果已经知道完整变量名:

"variable": {
  "name": "完整的_sdf_xarray_变量名"
}

关键词自动匹配

推荐通常使用:

"variable": {
  "contains": ["Number_Density", "Electron_foil"]
}

程序要求这些关键词最终只能唯一匹配一个变量。如果匹配到 0 个或多个,程序会打印候选变量并报错。


11. INSPECT:不知道变量名时怎么办

把:

"inspect": {
  "enabled": true,
  "prefix": "foilparticles",
  "particle_file": true,
  "select": {
    "mode": "number",
    "number": 78
  }
}

设置为 true 后运行程序。

程序会打印这个文件中的:

  • 变量名称;
  • dimensions;
  • shape。

确认变量名之后,再把 inspect.enabled 改回 false。

对于粒子 SDF,必须:

"particle_file": true

这样 sdf_xarray 会使用 keep_particles=True。


12. 归一化与自动 label

v0.1.2 的内置归一化不仅负责数值变换,也会根据实际匹配到的变量自动生成论文式 label。通常不再需要在每个 task 中手动填写 normalization.label。

自动 label 的共同排版规则是:

  • 主物理量、单位字母使用当前选中的普通字体(默认 Arial);
  • 下标只局部使用 Matplotlib mathtext 排版;
  • 指数只局部使用 mathtext 上标排版;
  • 不把整个 label 放进数学模式;
  • 因此 m、A、V、T、kg、s 等 SI 单位保持正体;
  • 不依赖字体是否提供 Unicode 上标负号,因此不同环境下更稳定;
  • x / y / z 分量会根据已经选中的变量自动识别。

12.1 不归一化:none

可以完全省略 normalization,或者写:

"normalization": {
  "kind": "none"
}

程序会根据实际匹配到的 EPOCH/sdf_xarray 变量自动生成简洁的物理量名称与 SI 单位,不再直接显示很长的 SDF 变量名。

典型结果:

实际变量 自动 label
electron number density n_e (m^-3)
ion number density n_i (m^-3)
Current_Jx / Current_Jy / Current_Jz J_x (A m^-2) / J_y (A m^-2) / J_z (A m^-2)
Electric_Field_Ex/Ey/Ez E_x (V m^-1) / E_y (V m^-1) / E_z (V m^-1)
Magnetic_Field_Bx/By/Bz B_x (T) / B_y (T) / B_z (T)
particle momentum Px/Py/Pz p_x (kg m s^-1) 等

这里表格为了 Markdown 可读性写成 m^-3;实际图片中 -3、-2、-1 会正确显示为上标,而单位字母保持正体。

例如:

{
  "type": "spatial",
  "name": "nxz",
  "prefix": "sourcexzspecies",
  "variable": {
    "contains": ["Number_Density", "Electron_foil", "source_xz"]
  },
  "axes": ["x", "z"],
  "cuts": {"y": 0.0},
  "select": {"mode": "time", "time_fs": 10.0},
  "normalization": {
    "kind": "none"
  },
  "scale": "log",
  "cmap": "gray",
  "title": null
}

colorbar 会自动得到类似:

n_e (m^-3)

其中指数在图片里为真正上标。

12.2 密度归一化:density_nc

"normalization": {
  "kind": "density_nc"
}

数值对应:

n / nc

其中 nc 根据 laser_wavelength_nm 自动计算。

label 也会根据已经匹配到的粒子种类自动生成。例如 electron number density:

n_e / n_c

如果匹配的是 ion number density,则会相应使用 n_i。

推荐直接写:

"normalization": {
  "kind": "density_nc"
}

不必再额外写:

"label": "$n_e/n_c$"

12.3 电流归一化:current_j0

"normalization": {
  "kind": "current_j0"
}

数值对应:

J / (e nc c)

程序会根据实际选中的电流分量自动生成 label:

J_x / (e n_c c)
J_y / (e n_c c)
J_z / (e n_c c)

例如变量选择:

"variable": {
  "contains": ["Jy", "Electron_foil"]
}

配合:

"normalization": {
  "kind": "current_j0"
}

会自动得到 J_y/(e n_c c) 风格的 colorbar label。

12.4 动量归一化:momentum_mec

"y_normalization": {
  "kind": "momentum_mec"
}

数值对应:

p / (me c)

程序会自动识别粒子动量分量:

p_x / (m_e c)
p_y / (m_e c)
p_z / (m_e c)

因此相空间任务中通常可以直接写:

"y_variable": {
  "contains": ["Particles_Px"]
},
"y_normalization": {
  "kind": "momentum_mec"
}

无需再手写 p_x/(m_e c)。

12.5 手动覆盖 label

自动 label 是默认推荐方式,但仍然保留完整的手动接口。

只要显式设置:

"normalization": {
  "kind": "current_j0",
  "label": "My custom label"
}

程序就会优先使用你写的 label。

优先级为:

手动 normalization.label
        ↓
内置 normalization 自动 label
        ↓
kind="none" 的物理量 + SI 单位自动 label

也就是说,已有旧 JSON 中的手动 label 不会失效。

12.6 自定义乘数

例如 m → nm:

"x_normalization": {
  "factor": 1000000000.0,
  "label": "x (nm)"
}

如果 kind: "none" 同时指定了非 1 的自定义 factor,程序不会擅自继续标原始 SI 单位,因为缩放后单位可能已经改变。

因此这种情况建议显式给出:

"label": "..."

以保证单位正确。

12.7 自动 label 与字体

自动 label 与 v0.1.2 的字体 fallback 配合工作:

Assets / Assets/fonts 中的指定字体
→ 系统 Arial
→ Helvetica
→ DejaVu Sans
→ Matplotlib 默认 sans-serif

没有 Assets 文件夹也不会影响 label 的数值逻辑或上下标排版。

对于 kind="none":

J_y (A m^-2)

单位字母 A、m 是普通正体,-2 只局部作为上标排版。

对于归一化量:

J_y / (e n_c c)

J、e、n、c 等主体同样跟随当前普通字体,下标只局部排版,因此和 kind="none" 使用的是同一套视觉规则。


13. 色标

colormap 名称

cmap 直接使用 Matplotlib 的 colormap 名称,名称需要与 Matplotlib 支持的名称一致。

例如:

"cmap": "gray"

或:

"cmap": "Greys"

都是有效的;但:

"cmap": "Grey"

不是有效名称,会由 Matplotlib 报错。因此推荐直接使用标准名称,例如 viridis、magma、gray、Greys、RdBu_r。

线性色标

"scale": "linear"

对数色标

适合密度、粒子计数等正值:

"scale": "log"

可指定:

"vmin": 0.01,
"vmax": 200

对称 symlog

适合正负电流、电场:

"scale": "symlog",
"linthresh": 0.02

对称线性色标

"scale": "linear",
"symmetric": true

自动范围

如果不写 vmin/vmax,程序使用 percentile 自动估计。默认:

"percentile": 99.5

14. 输出文件

每个 task 独立输出到:

<out_dir>/<task.name>/

例如:

Data/postprocess/foil_density_xy/

可能包含:

  • .png:静态图;
  • .gif:动画;
  • .npz:save_data=true 时保存的后处理数据。

多时刻/多编号任务会额外输出一张 multiple_time 或 multiple_number 对比图,并使用同一个 colorbar 范围,便于直接比较。

多时刻任务的单图文件名会包含实际时刻,例如:

density_time=12.34fs.png

图内标题则显示为:

time=12.34 fs

二者的空格规则不同是有意设计:图内符合物理量排版习惯,文件名保持紧凑。


15. 推荐的日常使用方式

以后不建议再复制很多不同版本的 Python 后处理脚本。建议:

统一程序:
~/python_lib/epochpost.py

每个算例:
run1/epochpost_input.json
run2/epochpost_input.json
run5/epochpost_input.json
run7/epochpost_input.json
...

每次后处理时只修改当前 run 的:

epochpost_input.json

如果一个 run 有很多不同分析,可以让 tasks 中同时包含多个任务:

"tasks": [
  {"type": "spatial", "name": "density", "...": "..."},
  {"type": "spatial", "name": "Jx", "...": "..."},
  {"type": "xt", "name": "Jy_xt", "...": "..."},
  {"type": "particle_phase", "name": "x_px", "...": "..."}
]

这样一份 JSON 本身也相当于这个算例的“后处理记录”,以后回头看 run7 时可以直接知道当时画过什么、用了什么范围和归一化。


16. 当前版本支持的任务类型总结

type 用途 单/多时刻 多编号 动图
spatial 2D 空间分布 ✓ ✓ ✓
xt 空间-时间图 通过时间范围 不使用 —
particle_phase 粒子相空间 ✓ ✓ ✓
particle_xt 粒子位置-时间图 通过时间范围 不使用 —

v0.1.2 已包含论文式默认绘图样式、字体 fallback、none/density_nc/current_j0/momentum_mec 统一的自动 colorbar label,以及统一的时间标题格式。这套结构已经适合作为后续 Python library 的基础:绘图算法集中在模块里,算例差异全部留在 JSON 配置中。


GitHub 上传

建议 GitHub 仓库只保存程序、示例输入和说明文档,不要提交大型 SDF 数据或后处理结果。

第一次上传一个新的本地仓库时,可以在 GitHub 新建一个空仓库 epochpost,然后在本地执行:

mkdir epochpost
cd epochpost

# 把这三个文件复制到当前目录:
# epochpost.py
# epochpost_input.json
# README.md

git init
git branch -M main
git add epochpost.py epochpost_input.json README.md
git commit -m "Initial release of epochpost"
git remote add origin https://github.com/YOUR_USERNAME/epochpost.git
git push -u origin main

之后每次更新只需要:

git add epochpost.py epochpost_input.json README.md
git commit -m "Update epochpost"
git push

对于实际 PIC 算例,建议把 Data/、*.sdf、postprocess/ 等大型数据加入 .gitignore,避免误上传模拟数据。


版本更新 log

v0.1.2 - 2026 年 8 月 9 日

在上一版本的基础上,新增功能:colormap 自动加label;并且输出图片的格式尽可能满足论文要求,也可以用户自定义。

v0.1.3 - 2026年 8 月 13 日

bugfix: epochpost_input.json 文件中当同时写入多个 task并且均为 true 时,存在多个task 先后多次读取同一 sdf 文件的情况,会导致一些问题,v0.1.3 修正这一问题并通过了测试。

bugfix: json 文件中针对变量名的输入,当选择'contains'时,将'Ex','Ey','Ez'等作为整体进行变量名称匹配,避免匹配到多个变量。

About

Postprocessing code for PIC simulation code --EPOCH

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages